@fasl-work/caos-app-shell 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -70,7 +70,11 @@ const config: ShellConfig = {
70
70
  Every control writes `data-control`. `PlotCard` and `Readout` take `lane`, `provenance` and a `dataKey`:
71
71
  pass `useWorkbenchState().stateKey` as captured when the computation started (rail and instrument share it), and
72
72
  a view still showing an earlier selection is overlaid as stale. Anything that loads declares
73
- `data-state="loading"` until it is ready; the gate waits for that, never for the network.
73
+ `data-state="loading"` until it is ready; the gate waits for that, never for the network. A view whose data is absent declares
74
+ `data-state="unavailable"` on an element a reader can see (a visible box of 40 by 12 px at least inside the instrument,
75
+ opaque, its text painted inside the box: not pushed out by `text-indent`, cut by `clip-path` or under 8 px) whose rendered text states the reason (12 letters or digits in two words at least) and renders no drawing element;
76
+ the gate then accepts it under G6, while an unreadable note, a declaration without a reason, or one beside a blank,
77
+ collapsed or invisible chart still fails. The gate checks that a reason exists, not that it is true.
74
78
  - **Fill the instrument:** `<PlotCard fill>` takes the height its panel leaves (several `fill` cards share it, a
75
79
  `.caos-views-row` puts them side by side), and a `Stage` or `<UPlotChart height="fill">` inside it gets a real
76
80
  size. A table (`.caos-table`) is sized to its rows: put it above the drawing of the same numbers, never alone.
@@ -99,7 +103,12 @@ const config: ShellConfig = {
99
103
  reader cannot scroll to, see whole or click (G5), blank or underfilled drawings and an instrument under half the
100
104
  viewport (G6), a view that never settles (G7), loops at rest (G8), controls that change nothing (G9), labels cut or
101
105
  overlapping in a drawing, also in a wider fallback font (G10), a decimal point on a Spanish page (G11), a vertical
102
- sub-tab list that scrolls away (G12), text under WCAG AA contrast (G13). The report
106
+ sub-tab list that scrolls away (G12), text under WCAG AA contrast (G13, every language on a documentation page), and a documentation page without the
107
+ required areas of its kind (`ADR-0017.2`: executive and technical Introduction, Methodology, Implementation,
108
+ Experiments, Benchmark; declared with `<DocSection area="...">`, waived only with a reason of 12 letters or digits in two
109
+ words through `floorWaivers`; the executive view opens first, every Introduction section sits in a view, a method cites
110
+ references, a `live` control is registered with `data-control`, and a tab list inside a section belongs to it;
111
+ [docs/04](docs/04_views.md)). The report
103
112
  (`gate-output/gate-report.json`) lists every failure and the smallest margins against each floor; `index.html`
104
113
  beside it shows every capture (G14). Details: [docs/07](docs/07_gate.md).
105
114
  - **Your own CSS:** never restyle a class of `reserved-classes.json` `components`; a `modifiers` word (`on`,
@@ -13,7 +13,7 @@
13
13
  // --sizes 390x844,...,2560x1440 --themes light,dark --langs en,es
14
14
  // --case-sample id1,id2 cases walked outside the primary mode; default the first and the last
15
15
  // --single-case the workbench has one case and no case control
16
- // --idle-ms 3000 --settle-ms 15000 --text-floor 400 --instrument-min 0.5 --stage-fill-min 0.3
16
+ // --idle-ms 3000 --settle-ms 15000 --text-floor 400 --instrument-min 0.5 --stage-fill-min 0.3 --stage-floor 140
17
17
  // --wide-font-sizes 390x844,1280x800 the sizes of the wide-font pass (G10); --no-wide-font skips it
18
18
  // --out gate-output report (gate-report.json), screenshots (shots/) and the capture index (index.html)
19
19
  // Needs `playwright` in the consuming project (optional peer dependency). Exit 1 on any failure, 2 on misuse.
@@ -49,6 +49,7 @@ const options = {
49
49
  textFloor: num('text-floor'),
50
50
  instrumentMin: num('instrument-min'),
51
51
  stageFillMin: num('stage-fill-min'),
52
+ stageFloor: num('stage-floor'),
52
53
  wideFontSizes: args['wide-font-sizes'],
53
54
  wideFont: args['no-wide-font'] === true ? false : undefined,
54
55
  out: args.out,
@@ -65,6 +66,7 @@ try {
65
66
  const shown = report.failures.slice(0, 80);
66
67
  for (const f of shown) console.log(`FAIL [${f.check}] ${f.route} ${f.mode}${f.trail ? ` (${f.trail})` : ''}: ${f.message}`);
67
68
  if (report.failures.length > shown.length) console.log(`... and ${report.failures.length - shown.length} more in ${options.out ?? 'gate-output'}/gate-report.json`);
69
+ for (const w of report.waivers ?? []) console.log(`WAIVER ${w.route} (${w.lang}) ${w.floor}: ${w.reason}${w.valid ? '' : ' [refused]'}`);
68
70
  const counts = Object.entries(report.checks).map(([k, n]) => `${k} ${n}`).join(', ');
69
71
  console.log(report.ok ? `caos-shell-gate: OK, ${report.states} measured states` : `caos-shell-gate: ${report.failures.length} failure(s) in ${report.states} measured states (${counts})`);
70
72
  process.exit(report.ok ? 0 : 1);
package/dist/chart.d.ts CHANGED
@@ -22,6 +22,12 @@ interface ChartAxis {
22
22
  unit?: BiText;
23
23
  format?: FormatOptions;
24
24
  }
25
+ /** What a reference line says (CAOS_APP_SHELL#94): `warn` an amber threshold (the default), `bad` a red one, `good` a
26
+ * level to reach, `neutral` a level that is no alarm (a nominal size, "no bias"). The line and its label take the tone's
27
+ * colour. */
28
+ type YMarkTone = 'warn' | 'bad' | 'good' | 'neutral';
29
+ /** The shell colour token of each tone of a reference line. */
30
+ declare const YMARK_TONE_TOKENS: Record<YMarkTone, ShellColorToken>;
25
31
  interface UPlotChartProps {
26
32
  /** The x values and their axis. A time axis must be declared (`time: true`); uPlot otherwise reads numbers as
27
33
  * epoch seconds and labels ticks as dates (failure class 7). */
@@ -45,6 +51,7 @@ interface UPlotChartProps {
45
51
  yMarks?: {
46
52
  y: number;
47
53
  label: BiText;
54
+ tone?: YMarkTone;
48
55
  }[];
49
56
  /** Height in pixels, or `fill` to take the container's height (inside `PlotCard fill`); the width is always the
50
57
  * container's. */
@@ -77,6 +84,14 @@ declare function seriesStyle(s: ChartSeries, i: number): {
77
84
  declare function sortOrder(xs: readonly number[]): number[] | null;
78
85
  /** One range for both axes of a parity plot, padded by 5% of the span on each side. */
79
86
  declare function parityRange(...arrays: readonly (readonly (number | null)[])[]): [number, number];
87
+ /**
88
+ * The data's y extent widened to take in the levels of the reference lines, so a line is always drawn (CAOS_APP_SHELL#94;
89
+ * a constant series pulled the range to it, a mark did not). A log axis takes in only the levels above 0. Returns the
90
+ * extent unchanged when every level is inside it; `null` ends (no data) take the levels alone. An end a level pushed out
91
+ * moves a further `pad` of the span (of the span in decades on a log axis), so the line sits inside the frame, not on it;
92
+ * the caller applies uPlot's own padding after this.
93
+ */
94
+ declare function rangeWithLevels(min: number | null, max: number | null, levels: readonly number[], log?: boolean, pad?: number): [number, number] | null;
80
95
  /**
81
96
  * The tick steps an axis may use. With fixed `decimals` (scaled by 100 for a percent) only multiples of the smallest
82
97
  * difference its labels can show, so no two ticks read the same: an integer axis ticks at integers, never at 1.5,
@@ -115,11 +130,13 @@ declare function drawMarks(u: uPlot, marks: {
115
130
  /**
116
131
  * The horizontal reference lines of a chart and their labels: a dashed line across the plot, its label at the right
117
132
  * end above the line (below it when the line runs along the top), inside the plot, haloed; a label that would overlap
118
- * the one placed before it moves left of it. A line outside the y range is not drawn.
133
+ * the one placed before it moves left of it. A line outside the y range is not drawn. A mark's `color` (the tone's) draws
134
+ * its line and its label; without one `style.color` does.
119
135
  */
120
136
  declare function drawYMarks(u: uPlot, marks: {
121
137
  y: number;
122
138
  label: string;
139
+ color?: string;
123
140
  }[], style: {
124
141
  color: string;
125
142
  halo: string;
@@ -143,4 +160,4 @@ declare function nearestPoint(xs: readonly number[], ys: readonly (readonly (num
143
160
  */
144
161
  declare function UPlotChart({ x, y, series, marks, yMarks, height, onCursor, onPick, parity, readout, hint }: UPlotChartProps): react.JSX.Element;
145
162
 
146
- export { type ChartAxis, type ChartSeries, LOG_TABLE_FLOOR, UPlotChart, type UPlotChartProps, drawMarks, drawYMarks, logDecades, nearestPoint, parityRange, repeatedLabels, seriesStyle, smallestPositive, sortOrder, tickSteps };
163
+ export { type ChartAxis, type ChartSeries, LOG_TABLE_FLOOR, UPlotChart, type UPlotChartProps, YMARK_TONE_TOKENS, type YMarkTone, drawMarks, drawYMarks, logDecades, nearestPoint, parityRange, rangeWithLevels, repeatedLabels, seriesStyle, smallestPositive, sortOrder, tickSteps };
package/dist/chart.js CHANGED
@@ -4,6 +4,12 @@ import { useRef, useCallback, useState, useMemo, useEffect } from 'react';
4
4
  import uPlot from 'uplot';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
6
6
 
7
+ var YMARK_TONE_TOKENS = {
8
+ warn: "--color-warn",
9
+ bad: "--color-bad",
10
+ good: "--color-good",
11
+ neutral: "--color-fg-subtle"
12
+ };
7
13
  var ROTATION = ["--color-accent", "--color-magenta", "--color-accent-2", "--color-good", "--color-warn", "--color-bad"];
8
14
  function seriesStyle(s, i) {
9
15
  const color = s.color ?? ROTATION[i % ROTATION.length];
@@ -30,6 +36,30 @@ function parityRange(...arrays) {
30
36
  const pad = (hi - lo || Math.abs(hi) || 1) * 0.05;
31
37
  return [lo - pad, hi + pad];
32
38
  }
39
+ function rangeWithLevels(min, max, levels, log = false, pad = 0) {
40
+ const d0 = min !== null && Number.isFinite(min) ? min : Infinity;
41
+ const d1 = max !== null && Number.isFinite(max) ? max : -Infinity;
42
+ let lo = d0;
43
+ let hi = d1;
44
+ for (const v of levels) {
45
+ if (!Number.isFinite(v) || log && !(v > 0)) continue;
46
+ lo = Math.min(lo, v);
47
+ hi = Math.max(hi, v);
48
+ }
49
+ if (!Number.isFinite(lo) || !Number.isFinite(hi)) return null;
50
+ if (pad > 0 && (lo < d0 || hi > d1)) {
51
+ if (log && lo > 0) {
52
+ const room = (Math.log10(hi) - Math.log10(lo) || 1) * pad;
53
+ if (lo < d0) lo = lo / 10 ** room;
54
+ if (hi > d1) hi = hi * 10 ** room;
55
+ } else {
56
+ const room = (hi - lo || Math.abs(hi) || 1) * pad;
57
+ if (lo < d0) lo -= room;
58
+ if (hi > d1) hi += room;
59
+ }
60
+ }
61
+ return [lo, hi];
62
+ }
33
63
  var STEPS = [];
34
64
  for (let e = -16; e <= 16; e++) for (const m of [1, 2, 2.5, 5]) STEPS.push(Number((m * 10 ** e).toPrecision(3)));
35
65
  function tickSteps(fmt) {
@@ -120,7 +150,8 @@ function drawYMarks(u, marks, style) {
120
150
  for (const m of [...marks].sort((a, b) => b.y - a.y)) {
121
151
  const py = u.valToPos(m.y, "y", true);
122
152
  if (!Number.isFinite(py) || py < top - 0.5 || py > bottom + 0.5) continue;
123
- ctx.strokeStyle = style.color;
153
+ const color = m.color ?? style.color;
154
+ ctx.strokeStyle = color;
124
155
  ctx.lineWidth = Math.max(1, pr);
125
156
  ctx.setLineDash([2 * pr, 4 * pr]);
126
157
  ctx.beginPath();
@@ -140,7 +171,7 @@ function drawYMarks(u, marks, style) {
140
171
  ctx.lineWidth = 3 * pr;
141
172
  ctx.strokeStyle = style.halo;
142
173
  ctx.strokeText(m.label, x0, base);
143
- ctx.fillStyle = style.color;
174
+ ctx.fillStyle = color;
144
175
  ctx.fillText(m.label, x0, base);
145
176
  }
146
177
  ctx.restore();
@@ -192,12 +223,13 @@ function UPlotChart({ x, y, series, marks, yMarks, height = 280, onCursor, onPic
192
223
  const ys = useMemo(() => series.map((s) => order ? order.map((i) => s.values[i] ?? null) : s.values), [order, series]);
193
224
  const levels = useMemo(() => (yMarks ?? []).map((m) => m.y), [yMarks]);
194
225
  const range = useMemo(() => parity ? parityRange(xs, ...ys, levels) : null, [parity, xs, ys, levels]);
226
+ const yLevels = !parity && !y.range && levels.length > 0 ? levels : null;
195
227
  const tiny = useMemo(
196
228
  () => ({
197
229
  x: Boolean(x.log) && smallestPositive([xs], range) < LOG_TABLE_FLOOR,
198
- y: Boolean(y.log) && smallestPositive(ys, range ?? y.range) < LOG_TABLE_FLOOR
230
+ y: Boolean(y.log) && smallestPositive(yLevels ? [...ys, yLevels] : ys, range ?? y.range) < LOG_TABLE_FLOOR
199
231
  }),
200
- [x.log, y.log, xs, ys, range, y.range]
232
+ [x.log, y.log, xs, ys, range, y.range, yLevels]
201
233
  );
202
234
  const xLabel = pick(x.label, lang);
203
235
  const yLabel = pick(y.label, lang);
@@ -210,7 +242,7 @@ function UPlotChart({ x, y, series, marks, yMarks, height = 280, onCursor, onPic
210
242
  pick: Boolean(onPick),
211
243
  y: [yLabel, yUnit, Boolean(y.log), y.range ?? null, y.format ?? null, tiny.y],
212
244
  m: (marks ?? []).map((m) => [m.x, pick(m.label, lang)]),
213
- ym: (yMarks ?? []).map((m) => [m.y, pick(m.label, lang)]),
245
+ ym: (yMarks ?? []).map((m) => [m.y, pick(m.label, lang), m.tone ?? "warn"]),
214
246
  theme,
215
247
  lang,
216
248
  h,
@@ -264,7 +296,17 @@ function UPlotChart({ x, y, series, marks, yMarks, height = 280, onCursor, onPic
264
296
  distr: x.log ? 3 : 1,
265
297
  ...range ? { range } : x.log ? { range: (_u, min, max) => [min, max] } : {}
266
298
  },
267
- y: { distr: y.log ? 3 : 1, ...range ? { range } : y.range ? { range: y.range } : {} }
299
+ y: {
300
+ distr: y.log ? 3 : 1,
301
+ ...range ? { range } : y.range ? { range: y.range } : yLevels ? {
302
+ // uPlot's own range of the data and the levels: its padding, its decade snap on a log axis
303
+ range: (u, dmin, dmax) => {
304
+ const w = rangeWithLevels(dmin, dmax, yLevels, Boolean(y.log), 0.05);
305
+ if (!w) return [dmin, dmax];
306
+ return y.log ? uPlot.rangeLog(w[0], w[1], u.scales.y.log ?? 10, false) : uPlot.rangeNum(w[0], w[1], 0.1, true);
307
+ }
308
+ } : {}
309
+ }
268
310
  },
269
311
  axes: [
270
312
  {
@@ -344,7 +386,7 @@ function UPlotChart({ x, y, series, marks, yMarks, height = 280, onCursor, onPic
344
386
  ctx.restore();
345
387
  }
346
388
  const markStyle = { color: resolveToken("--color-warn", "#c80"), halo: surface, family };
347
- if (yMarks?.length) drawYMarks(u, yMarks.map((m) => ({ y: m.y, label: pick(m.label, lang) })), markStyle);
389
+ if (yMarks?.length) drawYMarks(u, yMarks.map((m) => ({ y: m.y, label: pick(m.label, lang), color: resolveToken(YMARK_TONE_TOKENS[m.tone ?? "warn"], "#c80") })), markStyle);
348
390
  if (marks?.length) drawMarks(u, marks.map((m) => ({ x: m.x, label: pick(m.label, lang) })), markStyle);
349
391
  }
350
392
  ]
@@ -390,4 +432,4 @@ function UPlotChart({ x, y, series, marks, yMarks, height = 280, onCursor, onPic
390
432
  );
391
433
  }
392
434
 
393
- export { LOG_TABLE_FLOOR, UPlotChart, drawMarks, drawYMarks, logDecades, nearestPoint, parityRange, repeatedLabels, seriesStyle, smallestPositive, sortOrder, tickSteps };
435
+ export { LOG_TABLE_FLOOR, UPlotChart, YMARK_TONE_TOKENS, drawMarks, drawYMarks, logDecades, nearestPoint, parityRange, rangeWithLevels, repeatedLabels, seriesStyle, smallestPositive, sortOrder, tickSteps };
package/dist/index.d.ts CHANGED
@@ -361,14 +361,30 @@ declare function Cite({ id, paren }: {
361
361
  id: string;
362
362
  paren?: boolean;
363
363
  }): react.JSX.Element;
364
+ /** The citation ids of a scope in the order of first appearance: each id once, sections in the order given. */
365
+ declare function firstAppearance(sections: readonly (readonly string[])[]): string[];
366
+ /**
367
+ * A scope that lists the references of the sections inside it, once, at its own end: the `DocPage`, and each open panel
368
+ * of `Tabs` and `SubTabs` (the innermost scope wins). Sections register in a layout effect; the order is their order in
369
+ * the document (not the order of registration), so a section that mounts later keeps its place. It adds no element of
370
+ * its own, and nothing renders while no section registered an id.
371
+ */
372
+ declare function DocRefsScope({ children }: {
373
+ children: ReactNode;
374
+ }): react.JSX.Element;
375
+ /** The end-of-scope references block (ADR-0017 s4, shell 0.11.0): the full citation of each id, once, as an ordered list. */
376
+ declare function DocReferences({ ids }: {
377
+ ids: string[];
378
+ }): react.JSX.Element | null;
364
379
  /** A short inline "Refs: a · b · c" row (e.g. under a deep sub-tab). */
365
380
  declare function Refs({ ids, label }: {
366
381
  ids: string[];
367
382
  label: string;
368
383
  }): react.JSX.Element;
369
384
  /** Full ordered bibliography. Omit `ids` to render every provided citation.
370
- * @deprecated ADR-0017 s4 bans a bottom-of-page bibliography; end each section with `Refs` (or use `DocSection`).
371
- * Kept so existing products still compile; it reports itself on the console, which the gate fails on. */
385
+ * @deprecated Superseded by the end-of-page references block that `DocPage` and the tab panels render from their
386
+ * `DocSection`s (ADR-0017 s4). Kept so existing products still compile; it reports itself on the console, which the
387
+ * gate fails on. */
372
388
  declare function ReferenceList({ ids, heading }: {
373
389
  ids?: string[];
374
390
  heading?: string;
@@ -377,25 +393,91 @@ declare function ReferenceList({ ids, heading }: {
377
393
  /**
378
394
  * A documentation route (Introduction, Methodology, Implementation, Experiments, Benchmark): the page head with
379
395
  * its title and lede, then sections. `wide` for a route with a sub-tab rail (ADR-0071 rule 2, amended
380
- * 2026-09-04); a single prose column keeps the reading width and is centred (ADR-0017 §1).
396
+ * 2026-09-04); a single prose column keeps the reading width and is centred (ADR-0017 §1). Text takes the full width
397
+ * of the column, and the references of its sections are listed once at the end of the page (ADR-0017 §4).
398
+ */
399
+ /**
400
+ * The areas a section may declare, per documentation route (ADR-0017 section 2.1). The gate's table (`gate/areas.mjs`)
401
+ * holds each area's rule; `test/docFloors.test.ts` keeps these names equal to that table's.
381
402
  */
382
- declare function DocPage({ title, lede, wide, children }: {
403
+ declare const DOC_AREAS: {
404
+ readonly '/introduction': readonly ["initiative", "outcomes", "positioning", "credibility", "problem", "principles", "math", "pipeline", "honesty"];
405
+ readonly '/methodology': readonly ["method"];
406
+ readonly '/implementation': readonly ["architecture", "reproducibility", "engine", "lanes", "contract", "deployment"];
407
+ readonly '/experiments': readonly ["question", "metrics", "protocol", "datasets"];
408
+ readonly '/benchmark': readonly ["results", "comparison", "robustness", "live", "provenance", "caveat"];
409
+ };
410
+ interface DocPageProps {
383
411
  title: BiText;
384
412
  lede: ReactNode;
385
413
  wide?: boolean;
414
+ /**
415
+ * The required areas this page cannot carry, each with the reason (ADR-0017 section 2.1): rendered as
416
+ * `data-floor-waiver="<floor>: <reason>"`, separated by `; ` (a reason holds no semicolon). The gate skips a waived
417
+ * floor, lists the waiver, and fails one whose reason has fewer than 12 characters or whose floor is unknown.
418
+ */
419
+ floorWaivers?: Array<{
420
+ floor: string;
421
+ reason: BiText;
422
+ }>;
386
423
  children: ReactNode;
387
- }): react.JSX.Element;
424
+ }
425
+ declare function DocPage({ title, lede, wide, floorWaivers, children }: DocPageProps): react.JSX.Element;
388
426
  interface DocSectionProps {
389
427
  title: BiText;
390
428
  id?: string;
391
- /** Citation ids for this section only, rendered as one inline `Refs` row at its end (ADR-0017 §4). */
429
+ /** The required area this section covers (ADR-0017 section 2.1), rendered as `data-area`; the vocabulary is fixed per page (`gate/areas.mjs`). */
430
+ area?: string;
431
+ /** Methodology only: the kind of the method this section describes, rendered as `data-method-kind`. */
432
+ kind?: 'classical' | 'learned';
433
+ /** Citation ids this section cites; they are listed in the references block at the end of the page or open tab (ADR-0017 §4). */
392
434
  refs?: string[];
393
435
  /** Required when a section cites nothing, so the absence is a stated choice the gate can read. */
394
436
  noRefsReason?: BiText;
395
437
  children: ReactNode;
396
438
  }
397
- /** One section of a documentation route, ending in its own references. */
398
- declare function DocSection({ title, id, refs, noRefsReason, children }: DocSectionProps): react.JSX.Element;
439
+ /** One section of a documentation route; its references are registered with the nearest scope, not rendered here. */
440
+ declare function DocSection({ title, id, area, kind, refs, noRefsReason, children }: DocSectionProps): react.JSX.Element;
441
+
442
+ interface GlossaryEntry {
443
+ /** The symbol or term: a plain string (a symbol such as `S(t)`) or both languages. */
444
+ term: BiText;
445
+ definition: BiText;
446
+ }
447
+ /**
448
+ * The symbol glossary of the governing mathematics (ADR-0017 section 2.1, area `math`): one `<dt>` and `<dd>` per entry,
449
+ * in a `<dl>` that declares `data-glossary`. The gate counts its items and requires at least 10.
450
+ */
451
+ declare function Glossary({ entries, ariaLabel }: {
452
+ entries: GlossaryEntry[];
453
+ ariaLabel?: BiText;
454
+ }): react.JSX.Element;
455
+
456
+ interface StatProps {
457
+ /** The number as written, for example `42` or `3.5`. */
458
+ value: BiText;
459
+ /** Its unit, for example `% of downtime` (a statement without a unit is not quantified). */
460
+ unit: BiText;
461
+ /** Where the number comes from (a document, a measurement, a dataset): shown beside it and declared as `data-stat-source`. */
462
+ source: BiText;
463
+ }
464
+ /**
465
+ * A quantified statement (ADR-0017 section 2.1, the executive view): a value, its unit and its source, rendered as
466
+ * `data-stat` so the gate can count the claims of value that carry a number. A claim without all three is not a stat.
467
+ */
468
+ declare function Stat({ value, unit, source }: StatProps): react.JSX.Element;
469
+
470
+ interface QuestionProps {
471
+ /** The business question the executive would ask. */
472
+ question: ReactNode;
473
+ /** The plain answer, with the evidence and the honest limits. */
474
+ answer: ReactNode;
475
+ }
476
+ /**
477
+ * One question and its answer (ADR-0017 section 2.1, executive view, area `credibility`), rendered as `data-qa` with
478
+ * `data-question` and `data-answer` so the gate can count the pairs that hold both.
479
+ */
480
+ declare function Question({ question, answer }: QuestionProps): react.JSX.Element;
399
481
 
400
482
  interface SurfacePageProps {
401
483
  /** The route's heading; omit it when the header's active route already names the surface. */
@@ -836,4 +918,4 @@ interface ViewsRowProps {
836
918
  */
837
919
  declare function ViewsRow({ children, shares }: ViewsRowProps): react.JSX.Element;
838
920
 
839
- export { BREAKPOINTS, BarChart, type BarChartProps, type BarDatum, BiText, Callout, type CaseDef, type CaseGroup, type CaseKind, CaseSelector, type CaseSelectorProps, type CaseSelectorText, CaseWorkbench, type CaseWorkbenchProps, ChipGroup, type ChipGroupProps, type ChipOption, type Citation, CitationsProvider, Cite, DocPage, DocSection, type DocSectionProps, Equation, Figure, type FittedLabel, FocusShell, type FocusShellProps, FormatOptions, Gauge, type GaugeProps, type GaugeZone, InlineMath, Knob, type KnobProps, type Lane, LaneBadge, Lang, LanguageToggle, MAX_PEER_TABS, MAX_WORKBENCH_GROUPS, PanelBoundary, type PausedVizController, PlotCard, type PlotCardProps, type Provenance, type RailSection, Readout, type ReadoutItem, type ReadoutProps, ReferenceList, Refs, Stage, type StageSize, type SubTabDef, SubTabs, type SubTabsProps, SurfacePage, type SurfacePageProps, type TabDef, type TabGroupDef, TabGroups, type TabGroupsProps, Tabs, type TabsProps, type Theme, ThemeToggle, type Tone, type UsePausedVizOptions, VariantBar, type VariantBarProps, type VariantDef, Verdict, type VerdictProps, ViewsRow, type ViewsRowProps, type VizFrame, type VizLoop, type VizLoopDeps, type VizLoopOptions, type WorkbenchGroup, WorkbenchLayout, type WorkbenchLayoutProps, type WorkbenchRoute, WorkbenchShell, type WorkbenchShellProps, type WorkbenchState, WorkbenchStateContext, applyTheme, caseKind, caseTooltip, casesInSource, createVizLoop, fitLabel, fontFamily, groupByCategory, isStale, makeStateKey, niceStep, niceTicks, readCaseParam, readTheme, replaceQueryParam, sourcesPresent, textWidth, usePausedViz, useStageSize, useThemeStore, useUrlView, useWorkbenchState, viewParamOf, widestLabel, withCaseParam };
921
+ export { BREAKPOINTS, BarChart, type BarChartProps, type BarDatum, BiText, Callout, type CaseDef, type CaseGroup, type CaseKind, CaseSelector, type CaseSelectorProps, type CaseSelectorText, CaseWorkbench, type CaseWorkbenchProps, ChipGroup, type ChipGroupProps, type ChipOption, type Citation, CitationsProvider, Cite, DOC_AREAS, DocPage, type DocPageProps, DocReferences, DocRefsScope, DocSection, type DocSectionProps, Equation, Figure, type FittedLabel, FocusShell, type FocusShellProps, FormatOptions, Gauge, type GaugeProps, type GaugeZone, Glossary, type GlossaryEntry, InlineMath, Knob, type KnobProps, type Lane, LaneBadge, Lang, LanguageToggle, MAX_PEER_TABS, MAX_WORKBENCH_GROUPS, PanelBoundary, type PausedVizController, PlotCard, type PlotCardProps, type Provenance, Question, type QuestionProps, type RailSection, Readout, type ReadoutItem, type ReadoutProps, ReferenceList, Refs, Stage, type StageSize, Stat, type StatProps, type SubTabDef, SubTabs, type SubTabsProps, SurfacePage, type SurfacePageProps, type TabDef, type TabGroupDef, TabGroups, type TabGroupsProps, Tabs, type TabsProps, type Theme, ThemeToggle, type Tone, type UsePausedVizOptions, VariantBar, type VariantBarProps, type VariantDef, Verdict, type VerdictProps, ViewsRow, type ViewsRowProps, type VizFrame, type VizLoop, type VizLoopDeps, type VizLoopOptions, type WorkbenchGroup, WorkbenchLayout, type WorkbenchLayoutProps, type WorkbenchRoute, WorkbenchShell, type WorkbenchShellProps, type WorkbenchState, WorkbenchStateContext, applyTheme, caseKind, caseTooltip, casesInSource, createVizLoop, firstAppearance, fitLabel, fontFamily, groupByCategory, isStale, makeStateKey, niceStep, niceTicks, readCaseParam, readTheme, replaceQueryParam, sourcesPresent, textWidth, usePausedViz, useStageSize, useThemeStore, useUrlView, useWorkbenchState, viewParamOf, widestLabel, withCaseParam };