/*
 * THE PUBLIC SITE'S VISUAL SYSTEM. Loaded after `cockpit.css`, web build only.
 *
 * ── THE DESIGN SYSTEM (design-system session, 2026-08-12) ────────────────────
 *
 * Dark-first OKLCH tokens with full light parity, two self-hosted typefaces,
 * and a fixed shell: sticky topbar, a left rail grouped by job, content, one
 * footer. The tokens are the system; everything below them is arrangement.
 *
 * THE HIERARCHY IS STILL THE PRODUCT ARGUMENT (cockpit.css's own header).
 * Receipts, refusals and provenance labels are styled as first-class citizens
 * because they ARE the product. Three visual families carry that:
 *
 *   REFUSAL   red, left-barred, tinted ground. The loudest thing on any page.
 *             `--refusal` (né `--no`) means exactly one thing: this number
 *             does not hold.
 *   MEASURE   amber. Provenance, clocks, withheld figures, flags — the color
 *             of "measured, and here is the qualification". Amber means this
 *             and nothing else since BRANDSHIFT (2026-08-21): it used to carry
 *             brand duty as well, and a hue that means three things means none
 *             of them. The brand hue is now purple and it is a separate family.
 *   ABSENCE   muted italic on a faint diagonal hatch. A labelled absence is a
 *             value, not a gap — the hatch makes it structurally distinct from
 *             data, so color is never the only encoding.
 *
 * COLOR ECONOMY: gray is the default data color. Two accents, and each means
 * one thing: purple is BRAND — links, the primary action, selected nav, focus,
 * the mark — and amber is MEASURE. The pos/neg pair ships in two
 * user-selectable schemes (teal/vermilion, blue/vermilion — green against red
 * fails about 4% of readers, and the pair this sheet shipped until 2026-09-03
 * failed them measurably); four venue-identity hues in fixed order (the order is
 * documented where venues are named, in `src/app/charts/constitution.ts` —
 * this public sheet stays quiet).
 *
 * ── BRANDLAND (2026-09-03): THE PALETTE LAYER LANDED, IN PART ───────────────
 *
 * `docs/design/GROUNDUP_5_BRAND.md` §11 proposes a two-layer sheet: numbered
 * ramps with one role per step, and semantic tokens that read them. Three
 * families landed — the warm neutral ramp with the grounds, ink and edges on
 * it, the status and direction hues, and the measure and refusal families.
 * Three did not: the accent, the categorical slots and the venue hues. They
 * are one knot, and the argument for leaving them is measured at each
 * declaration below and reported in `sessions/REPORTbrandland.md` §A.
 *
 * CONTRAST IS A TEST, NOT A PROMISE: `tests/app/tokens.test.ts` parses this
 * file, converts every OKLCH token to sRGB, and REFUSES any named fg/bg pair
 * below WCAG AA — in both themes, both schemes. It reads a `light-dark()` arm
 * now rather than a theme block, because every colour is declared once.
 *
 * TYPE: Inter Variable for UI, JetBrains Mono Variable for every numeral,
 * unit, timestamp and formula; `font-variant-numeric: tabular-nums` set on
 * the root so no numeral anywhere proportionally spaces. Both fonts are
 * subsetted, self-hosted, two files, ≤60KiB combined — provenance and rebuild:
 * `scripts/subset-fonts.sh`; the byte ceiling: `scripts/perf-contract.ts`.
 *
 * NO @import and NO absolute url() — same-origin `url(fonts/…)` only. The
 * bundle audit refuses any off-origin reference in this file, and the
 * performance contract refuses a third woff2 or an oversized pair.
 */

/* ═══════════════════════════════════════════════════════════════════════════
 * FONTS. Two files, same-origin, weight-instanced 400–700.
 * ═══════════════════════════════════════════════════════════════════════════ */

@font-face {
  font-family: 'Inter Variable';
  src: url(fonts/inter-var.woff2) format('woff2-variations');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'JetBrains Mono Variable';
  src: url(fonts/jetbrains-mono-var.woff2) format('woff2-variations');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

/* ═══════════════════════════════════════════════════════════════════════════
 * TOKENS. One block, both schemes, `light-dark()` per colour.
 *
 * Naming: --bg0/1/2 grounds, --line edges, ink scale, --accent the brand hue
 * and --measure-* the measure family. Each one now READS a numbered palette
 * step instead of carrying a colour, which is §11's two-layer shape; the
 * names stayed, because a rename across four hundred call sites changes no
 * pixel and an alias that cannot drift is cheaper than the rename.
 * The legacy names (--paper, --panel, --amber, --no, …) are aliases onto
 * the same values: cockpit.css and every existing component rule read them.
 * ═══════════════════════════════════════════════════════════════════════════ */

:root {
  color-scheme: light dark;

  /* ── PALETTE LAYER: numbered ramps, one role per step ────────────────────
   *
   * ONE DECLARATION PER TOKEN, both schemes inside `light-dark()`
   * (GROUNDUP_5_BRAND.md §5.1 principle 1). Until BRANDLAND every colour was
   * written three times — the dark `:root`, the light attribute block and a
   * light media twin that `tokens.test.ts` had to assert equal because CSS
   * could not. That duplication is what PAYS for this layer: the sheet gains
   * 58 palette steps and still fits the shared render-blocking budget. See
   * `sessions/REPORTbrandland.md` §D for the byte arithmetic.
   *
   * THE LIGHT VALUE IS FIRST, the dark value second. That is `light-dark()`'s
   * argument order and it is the opposite of this sheet's old reading order,
   * where dark came first because dark was the default. Neither scheme is a
   * fallback of the other now (§5.1 principle 3).
   *
   * Neutral ramp, WARM, hue 80. The ground was a cool blue-black at hue 255
   * until BRANDLAND; §5.1 principle 6 makes it a warm near-black. */
  --n1: light-dark(oklch(0.985 0.003 80), oklch(0.190 0.005 80));
  --n2: light-dark(oklch(0.965 0.004 80), oklch(0.225 0.005 80));
  --n3: light-dark(oklch(0.945 0.004 80), oklch(0.255 0.005 80));
  --n4: light-dark(oklch(0.925 0.005 80), oklch(0.285 0.005 80));
  --n5: light-dark(oklch(0.900 0.005 80), oklch(0.315 0.005 80));
  --n6: light-dark(oklch(0.870 0.005 80), oklch(0.455 0.005 80));
  --n7: light-dark(oklch(0.780 0.006 80), oklch(0.520 0.006 80));
  /* ⚠️ n8 AND n9 ARE NOT §5.2's VALUES. Both light steps MOVED in BRANDLAND
   * and the reason is measured, not stylistic (`sessions/REPORTt1b.md` §B):
   *   n9 light was 0.580. As `--ink-muted` on `--bg-page` it read 3.87:1
   *   against a 4.5 floor. At 0.525 it reads 4.87 on the page, 5.17 on the
   *   panel and 4.59 on the element field, so the fine-print floor clears AA
   *   on every ground this tree draws ink on.
   *   n8 light was 0.640. As `--border-hover` on `--bg-element` it read
   *   2.86:1 against a 3.0 floor. At 0.620 it reads 3.10 there, 3.29 on the
   *   page and 3.49 on the panel.
   * THE FLOOR DID NOT MOVE. `tests/app/tokens.test.ts` still holds twenty
   * named text pairs at 4.5 and every mark at 3.0, and it recomputes each one
   * from these bytes. n10 light moved 0.500 -> 0.480 only to keep the ramp
   * evenly stepped once n9 came down; nothing reads n10 below AA either way.
   * The dark steps did not move. Both failures were light-only. */
  --n8: light-dark(oklch(0.620 0.006 80), oklch(0.580 0.006 80));
  --n9: light-dark(oklch(0.525 0.006 80), oklch(0.700 0.006 80));
  --n10: light-dark(oklch(0.480 0.006 80), oklch(0.790 0.006 80));
  --n11: light-dark(oklch(0.440 0.006 80), oklch(0.855 0.006 80));
  --n12: light-dark(oklch(0.220 0.006 80), oklch(0.960 0.004 80));

  /* Status and direction hues. Step 3 tints, step 9 marks at 3:1 or better,
   * step 11 is the text step. §5.2's values, unmoved. */
  --teal-9: light-dark(oklch(0.540 0.120 165), oklch(0.610 0.125 165));
  --vermilion-9: light-dark(oklch(0.600 0.185 45), oklch(0.670 0.170 45));
  --blue-9: light-dark(oklch(0.540 0.150 250), oklch(0.640 0.130 250));
  --amber-3: light-dark(oklch(0.955 0.060 80), oklch(0.295 0.055 80));
  --amber-9: light-dark(oklch(0.620 0.140 80), oklch(0.760 0.150 80));
  --amber-11: light-dark(oklch(0.485 0.120 80), oklch(0.870 0.130 80));
  --red-3: light-dark(oklch(0.945 0.040 25), oklch(0.285 0.050 25));
  --red-11: light-dark(oklch(0.485 0.180 25), oklch(0.860 0.100 25));

  /* Accent ramp, IRIS, hue 290. §5.2's iris-9 (the solid) and iris-11 (the
   * text step). BRANDLAND could not land these: the accent, the chart slots,
   * the venue hues and the in-mark ink were one family, and the ink had no
   * value that clears AA on a §12 slot. CHARTFORM took the ink out of the
   * marks (the table twin carries the values), so the family lands here. */
  --iris-9: light-dark(oklch(0.500 0.190 290), oklch(0.560 0.170 290));
  --iris-11: light-dark(oklch(0.460 0.170 290), oklch(0.860 0.100 290));

  /* Categorical series, §5.2's fixed order. SLOT 4 (violet 305) IS NOT
   * DECLARED: it sits 15 degrees from iris and `tests/app/tokens.test.ts`
   * refuses a series that reads as the brand. Seven slots carry the tree's
   * seven data hues, so the eighth is not needed and a slot nothing may
   * paint is a value nobody measures. */
  --series-1: light-dark(oklch(0.550 0.160 255), oklch(0.620 0.150 255));
  --series-2: light-dark(oklch(0.600 0.180 350), oklch(0.620 0.170 350));
  --series-3: light-dark(oklch(0.620 0.120 70), oklch(0.640 0.120 70));
  --series-5: light-dark(oklch(0.620 0.120 215), oklch(0.660 0.120 215));
  --series-6: light-dark(oklch(0.580 0.120 125), oklch(0.620 0.120 125));
  --series-7: light-dark(oklch(0.450 0.120 330), oklch(0.550 0.130 330));
  --series-8: light-dark(oklch(0.600 0.120 95), oklch(0.670 0.120 95));

  /* ── SEMANTIC LAYER: one token per role ──────────────────────────────────
   * Grounds. §5.1's surfaces: the page is n2 in light and n1 in dark, and the
   * panel is the other way round, so a raised panel is lighter than its page
   * in light and lighter than its page in dark. Elevation is a tone step,
   * never a shadow (§5.1 principle 6). */
  --bg0: light-dark(var(--n2), var(--n1));   /* the page */
  --bg1: light-dark(var(--n1), var(--n2));   /* a raised panel */
  --bg2: var(--n3);                          /* a recessed ground: inputs, code */
  --bg-hover: var(--n4);
  --bg-active: var(--n5);

  /* Edges. The tree carried two, §5.4 carries three: the hairline, the panel
   * outline and the control border. `--line-strong` IS the control border and
   * it keeps the 3:1 graphical-object floor `## OVERNIGHT DISPOSITIONS` (f)
   * lifted it to; the new middle step is the panel outline. */
  --line: var(--n6);
  --border: var(--n7);
  --line-strong: var(--n8);

  /* Ink. The tree carried five tones, §5.2 carries one role per ramp step.
   * The five fold onto four steps: `--soft` and `--body` share n11, which is
   * §14's "two tones fold into the ramp". `--faint` is the palette floor and
   * takes n9, the step §B moved. */
  --ink: var(--n12);
  --soft: var(--n11);
  --body: var(--n11);
  --muted: var(--n10);
  --faint: var(--n9);

  /* ── THE ACCENT IS IRIS, AND THAT IS BRANDLAND'S BLOCK CLEARING ──────────
   *
   * §5.1's hue map puts the accent at iris 290, because the green-blue half of
   * the wheel is spent on up, live and pass and the red-orange half on down,
   * refused and warning. BRANDLAND measured why it could not land alone:
   * `tests/app/tokens.test.ts` refuses a series hue within 30 degrees of the
   * brand, and this tree's violet chart hues sat 10 and 12 degrees away. The
   * accent, the categorical slots, the venue hues and the in-mark ink were one
   * family, and the ink had no value that clears AA on a §12 slot.
   *
   * CHARTFORM took the ink out of the marks — the table twin under every chart
   * carries the values (§8 rule 6) — so the whole family lands together here.
   * Measured on these bytes: iris-9 reads 5.90:1 on the light page and 3.72 on
   * the dark one, both above the 3:1 a fill owes; iris-11 as `--accent-text`
   * reads 6.93 / 7.35 in light and 11.62 / 10.76 in dark on page and panel.
   *
   * `--on-accent` FLIPS TO WHITE, and it has to. The old accent was a pale
   * cyan at L 0.82 carrying a near-black word; iris-9 is a mid-dark solid, and
   * white on it reads 6.53 in light and 4.97 in dark. It is one value for both
   * schemes because a brand fill does not flip when the theme does. */
  --accent: var(--iris-9);
  --accent-text: var(--iris-11);
  --on-accent: oklch(1 0 290);         /* ink on the brand fill */

  /* THE MEASURE FAMILY — amber, and §5.4 keeps it on exactly the freshness
   * roles: stale, partial, bounded, suspended. It lost the series and the
   * ramp when §8 gave the categorical palette its own slots.
   * `--on-measure` IS A ROLE §11 DOES NOT HAVE. §11 declares no in-mark ink
   * at all, and an amber chip with a word on it needs one. It is declared
   * here rather than derived, and it is the same near-black in both schemes
   * because an amber fill does not flip when the theme does: 5.00:1 in light,
   * 8.46 in dark. */
  --measure-fill: var(--amber-9);
  --measure-text: var(--amber-11);
  --measure-soft: var(--amber-11);
  --measure-bg: var(--amber-3);
  --on-measure: oklch(0.190 0.005 80);

  /* THE REFUSAL FAMILY. §5.4 splits what this tree spelled with one token:
   * `--refused` is the 3:1 mark and `--refused-text` is the Lc 75 word.
   * `--refusal` is enrolled as TEXT — it prints the word and its reason — so
   * it takes the text step. Dark red-9 reads 3.10:1 on the dark page, which
   * is a mark and not a word, and aliasing `--refusal` to it would have put a
   * sub-AA red under every refusal sentence on the site. */
  --refusal: var(--red-11);
  --refusal-bg: var(--red-3);

  /* THE DIRECTION PAIR: TEAL AND VERMILION (DIRECTION, 2026-09-03), unmoved
   * by BRANDLAND. These are §5.2's teal-9 and vermilion-9 and they now read
   * from the palette layer instead of repeating its numbers. */
  --pos: var(--teal-9);
  --neg: var(--vermilion-9);

  /* semantic states */
  --warn: var(--measure-fill);
  --stale: var(--measure-soft);

  /* ── THE CATEGORICAL FAMILY LANDS ON §11's SLOTS (CHARTFORM, C) ──────────
   *
   * BRANDLAND left these at hue 255 because §11's slots could not carry the
   * ink this tree wrote inside them. No mark carries ink now, so the only bar
   * a slot has to clear is the one a reader actually meets: 3:1 against the
   * bare page, in both schemes. Every slot below does, worst 3.11 in light
   * (cyan) and 3.55 in dark (plum).
   *
   * ── THE ROLES DECIDE THE MAPPING, NOT THE SLOT NUMBERS ───────────────────
   *
   * §8 rule 2: gray is the context series. §8 rule 3: slot 1 is the screen's
   * OWN series — the emphasized one. So `--chart-6`, which every form paints
   * its context marks in, becomes `--context`, and `--chart-7`, the ranking
   * LEAD, takes slot 1. That pairing is also the one measurement that matters
   * most here, because the lead and the context are the two marks a reader of
   * a served ranking chart must tell apart: worst protan/deutan ΔE 15.7 in
   * light and 15.8 in dark, against §12's PASS target of 8.0.
   *
   * The remaining five take the slots that keep the treemap's own cycle apart,
   * chosen by measuring every arrangement rather than by taste. Adjacent pairs
   * in the cycle `--chart-1` → `--chart-6`, worst of protan and deutan:
   *
   *     1/2 18.4  2/3 17.1  3/4 19.9  4/5 23.3  5/6 11.4      (light)
   *     1/2 19.2  2/3 17.2  3/4 16.8  4/5 21.8  5/6 14.2      (dark)
   *
   * The tree's own worst adjacent pair before this was 7.3 (light), so the
   * cycle got FURTHER apart, not nearer. Slot 4, violet 305, is not used: it
   * is 15 degrees from the brand.
   *
   * ── THE VENUES ARE THE LARGER FIX ────────────────────────────────────────
   *
   * The four venue hues sat at 342, 245, 300 and 150 and two of them were
   * indistinguishable: `--venue-2` against `--venue-3` read ΔE 1.3 in light
   * and 0.6 in dark, a hard §12 failure that nothing in the tree measured
   * because the CVD gate's scope is the direction pair. On §11's slots the
   * worst of the six venue pairs is 9.3. Nearest hue was kept where a slot was
   * free, so a reader who knows a venue by its colour is moved as little as
   * the arithmetic allows. */
  --context: var(--n8);
  --chart-1: var(--series-3);   /* ochre 70 — the fee terrain's own hue */
  --chart-2: var(--series-5);   /* cyan 215 */
  --chart-3: var(--series-6);   /* olive 125 */
  --chart-4: var(--series-7);   /* plum 330 */
  --chart-5: var(--series-8);   /* khaki 95 */
  --chart-6: var(--context);    /* §8 rule 2: gray is the context series */
  --chart-7: var(--series-1);   /* blue 255 — §8 rule 3: the emphasized series */
  /* The near-black stroke of the unfavourable hatch (DIRECTION B). It was
   * `--on-chart`, the in-mark ink, until CHARTFORM retired that role: a hatch
   * is a TEXTURE over a direction bar, not a word on a chart slot, so its bar
   * is the 3:1 a graphical object owes and not AA. Measured on `--neg`, 4.35
   * in light and 5.79 in dark. One value for both schemes, because a direction
   * hue does not flip when the theme does. */
  --on-hatch: oklch(0.190 0.005 80);
  --venue-1: var(--series-7);   /* plum 330 */
  --venue-2: var(--series-1);   /* blue 255 */
  --venue-3: var(--series-5);   /* cyan 215 */
  --venue-4: var(--series-3);   /* ochre 70 */

  /* the mark's second bar (the first is --accent). The mark itself is RETIRED
   * by §14 and its replacement is the wordmark session, not this one. */
  --mark-contrast: light-dark(oklch(0.30 0.02 255), oklch(0.93 0.006 255));

  /* ── legacy aliases (cockpit.css + existing component rules) ───────────── */
  --paper: var(--bg0);
  --panel: var(--bg1);
  --inset: var(--bg2);
  --neutral-bg: var(--bg2);
  --rule: var(--line);
  --rule-strong: var(--line-strong);
  --amber: var(--measure-fill);
  --amber-soft: var(--measure-soft);
  --amber-ink: var(--on-measure);
  --measure: var(--measure-text);
  --no: var(--refusal);
  --no-bg: var(--refusal-bg);

  /* ── type ──────────────────────────────────────────────────────────────── */
  --font-sans: 'Inter Variable', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: 'JetBrains Mono Variable', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --text-2xs: 0.6875rem;  /* 11px — chips, kbd */
  --text-xs: 0.75rem;     /* 12px — fine print, receipt bodies, labels */
  --text-sm: 0.8125rem;   /* 13px — the workbench base: cells, controls */
  --text-base: 0.9375rem; /* 15px — body */
  --text-md: 1.0625rem;   /* 17px — ledes, standfirsts */
  --text-lg: 1.25rem;     /* 20px — panel heads */
  --text-xl: 1.5rem;      /* 24px — section heads */
  --text-2xl: 1.75rem;    /* 28px — page heads */
  --track-label: 0.08em;
  --track-tight: -0.02em;

  /* ── spacing, radii, shell metrics ─────────────────────────────────────── */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;
  --sp-7: 3rem;
  --sp-8: 4rem;
  --r-sm: 4px;
  --r-md: 8px;
  --r-lg: 12px;
  --topbar-h: 3.25rem;
  --rail-w: 13.5rem;

  /* the absence hatch: faint diagonal texture, no request, no image */
  --hatch: repeating-linear-gradient(135deg, transparent 0 5px, light-dark(oklch(0.440 0.006 80 / 0.08), oklch(0.700 0.006 80 / 0.07)) 5px 10px);

  /* the split rule, the brand device: two offset bars as layered gradients */
  --split-rule:
    linear-gradient(to right, var(--accent) 0 38%, transparent 38%) 0 0 / 100% 3px no-repeat,
    linear-gradient(to right, transparent 0 34%, light-dark(oklch(0.30 0.02 255 / 0.55), oklch(0.93 0.006 255 / 0.55)) 34% 96%, transparent 96%) 0 6px / 100% 3px no-repeat;
}

/* The alternative pair: blue against the same vermilion (DIRECTION,
 * 2026-09-03). It always separated under CVD, but both dark values sat at
 * L 0.75, outside the 0.48-0.67 band. These are the brand sheet §5.3's
 * `blue-vermilion` pair, now read from the palette layer. `--neg` is one
 * vermilion in both schemes, which is the brand rule — down stays in the
 * red-orange family, so a glyph keeps its meaning across the switch. */
:root[data-scheme="bo"] {
  --pos: var(--blue-9);
  --neg: var(--vermilion-9);
}

/* ═══════════════════════════════════════════════════════════════════════════
 * THE THEME CHOICE. `light-dark()` above carries both values, so a theme is
 * now a `color-scheme` and nothing else: the attribute the boot script stamps
 * WINS over the system setting, and a reader who has chosen nothing gets the
 * system's answer with no media block at all.
 *
 * THE MEDIA TWIN IS GONE, and that is what paid for the palette layer. Every
 * colour used to be written three times — here, in the attribute block, and
 * again behind `prefers-color-scheme: light` — and `tokens.test.ts` had to
 * assert the last two equal because CSS could not. One declaration cannot
 * drift from itself, so the arm that policed the twins is retired and an arm
 * that refuses a SECOND declaration site stands in its place.
 * ═══════════════════════════════════════════════════════════════════════════ */

:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

/* ═══════════════════════════════════════════════════════════════════════════
 * BASE.
 * ═══════════════════════════════════════════════════════════════════════════ */

html {
  font-size: 100%;
  /* Every numeral on the site is tabular. Set at the root so no component can
   * forget it; the mono is tabular by construction, and Inter's subset keeps
   * the `tnum` feature so this rule is real rather than decorative. */
  font-variant-numeric: tabular-nums;
}

body {
  margin: 0;
  /* THE RAIL WENT IN T1 SHELL. The inset it reserved goes with it. */
  padding: 0;
  background: var(--bg0);
  color: var(--body);
  font: var(--text-base)/1.6 var(--font-sans);
  -webkit-font-smoothing: antialiased;
  scrollbar-color: var(--line-strong) var(--bg0);
}

h1, h2, h3, h4 { color: var(--ink); }

a { color: var(--accent-text); text-decoration-color: color-mix(in oklch, var(--accent) 40%, transparent); text-underline-offset: 2px; }
a:hover { text-decoration-color: var(--accent); }

::selection { background: var(--accent); color: var(--on-accent); }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }

@media (prefers-reduced-motion: no-preference) {
  a, button, summary, input, select, textarea { transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease, filter 120ms ease; }
}

kbd {
  font: 600 var(--text-2xs)/1 var(--font-mono);
  background: var(--bg2);
  border: 1px solid var(--line-strong);
  border-bottom-width: 2px;
  border-radius: 4px;
  padding: 2px 5px;
  color: var(--muted);
}

.wrap { max-width: 880px; margin-inline: auto; padding-inline: var(--sp-5); }

/* Tool pages are wider than the essay: columns need room, a measure does not. */
.product, #disputes { max-width: 1240px; }

/* ═══════════════════════════════════════════════════════════════════════════
 * THE SHELL: sticky topbar, fixed rail, content, footer.
 * ═══════════════════════════════════════════════════════════════════════════ */

.topbar {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  min-height: var(--topbar-h);
  padding: 0 var(--sp-4);
  background: var(--bg0);
  border-bottom: 1px solid var(--line);
}
.topbar-brand { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }
.mark { width: 30px; height: 19px; flex: none; display: block; }
.wordmark { font-size: 1rem; font-weight: 700; letter-spacing: var(--track-tight); margin: 0; line-height: 1; }
.tag {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  border-left: 1px solid var(--line);
  padding-left: var(--sp-3);
}
.topbar-tools { margin-left: auto; display: flex; align-items: center; gap: var(--sp-2); flex: none; position: relative; }

.palette-button {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  font: 600 var(--text-sm)/1 var(--font-sans);
  color: var(--muted);
  background: var(--bg2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  padding: 0.4rem 0.7rem;
  cursor: pointer;
}
.palette-button:hover { color: var(--ink); border-color: var(--accent); }

.prefs { position: relative; }
.prefs > summary {
  list-style: none;
  cursor: pointer;
  font: 600 var(--text-sm)/1 var(--font-sans);
  color: var(--muted);
  background: var(--bg2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  padding: 0.4rem 0.7rem;
}
.prefs > summary::-webkit-details-marker { display: none; }
.prefs > summary:hover { color: var(--ink); border-color: var(--accent); }
.prefs-body {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: 60;
  min-width: 17rem;
  background: var(--bg1);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
  display: grid;
  gap: var(--sp-3);
  box-shadow: 0 10px 30px oklch(0 0 0 / 0.35);
}
.prefs-group { display: grid; gap: var(--sp-1); }
.prefs-group-label {
  font-size: var(--text-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--faint);
}
.prefs-choice { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--text-sm); color: var(--body); cursor: pointer; }
.prefs-choice input { accent-color: var(--accent); }

/* THE TOPBAR NAV. One entry, Desk, beside the wordmark (T1 SHELL; DESIGN §6).
 * The fixed rail it replaces is gone, and so is the body inset it reserved. */
.wordmark a { color: inherit; text-decoration: none; }
.wordmark a:hover { color: var(--accent); }
.topbar-nav { display: flex; align-items: center; gap: var(--sp-2); }
.topbar-nav a {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--muted);
  text-decoration: none;
  border-radius: var(--r-sm);
  padding: 0.32rem 0.55rem;
}
.topbar-nav a:hover { color: var(--ink); background: var(--bg1); }
.topbar-nav a[aria-current="page"] { color: var(--ink); background: var(--bg2); }

/* The intro strips that replaced the old masthead extras. */
.page-intro { padding-top: var(--sp-5); }
.page-intro .lede { font-size: var(--text-md); color: var(--body); max-width: 640px; margin: 0 0 var(--sp-4); }
.page-actions { padding-top: var(--sp-4); }
.cta {
  display: inline-block;
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 700;
  font-size: var(--text-sm);
  text-decoration: none;
  padding: 8px 16px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
}
.cta:hover { filter: brightness(1.08); }
.soon { display: inline-block; margin-left: var(--sp-3); color: var(--muted); font-size: var(--text-sm); }

/* THE FOOTER, ONE MUTED LINE UNDER THE FOLD (T1 SHELL; T0 (a)). It carries the
 * four standing facts and the terms link, and it carries them at the ink floor
 * so the fold ends on prose rather than on a device. The split rule that used
 * to open it went with the second paragraph. */
.site-footer {
  margin-top: var(--sp-7);
  padding: var(--sp-4) var(--sp-5) var(--sp-6);
  border-top: 1px solid var(--line);
  max-width: 880px;
  margin-left: auto;
  margin-right: auto;
}
.site-footer p { color: var(--faint); font-size: var(--text-sm); margin: 0; max-width: 90ch; }
.footer-lead { color: var(--muted); }

/* Small screens: the tagline yields first, as it always did. */
@media (max-width: 64rem) {
  .tag { display: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
 * CONTROLS: buttons, inputs, selects, textareas. Scoped to the product and
 * rail surfaces so the two bare table buttons (`.sort`, `.watch`) keep their
 * own, more specific styling from the shared sheet.
 * ═══════════════════════════════════════════════════════════════════════════ */

.product button, .rail button, #disputes button {
  font: 600 var(--text-sm)/1.4 var(--font-sans);
  color: var(--ink);
  background: var(--bg2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  padding: 0.45rem 0.9rem;
  cursor: pointer;
}
.product button:hover:enabled, .rail button:hover:enabled, #disputes button:hover:enabled {
  border-color: var(--accent);
  color: var(--accent-text);
}
.product button:disabled, .rail button:disabled { opacity: 0.5; cursor: default; }

.product form button[type="submit"], .rail form button[type="submit"] {
  background: var(--accent);
  color: var(--on-accent);
  border-color: transparent;
}
.product form button[type="submit"]:hover:enabled, .rail form button[type="submit"]:hover:enabled {
  color: var(--on-accent);
  border-color: transparent;
  filter: brightness(1.08);
}

.product input[type="text"], .product input[type="search"], .product input:not([type]), .product textarea, .product select,
.rail input[type="text"], .rail input:not([type]) {
  font: var(--text-sm)/1.4 var(--font-sans);
  color: var(--ink);
  background: var(--bg2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  padding: 0.45rem 0.6rem;
}
.product input:focus, .product textarea:focus, .product select:focus, .rail input:focus {
  border-color: var(--accent);
  outline: none;
}
.product ::placeholder, .rail ::placeholder { color: var(--faint); }
.product input[type="checkbox"], .rail input[type="checkbox"] { accent-color: var(--accent); width: 1rem; height: 1rem; }
.product textarea { font-family: var(--font-mono); }

.product label, .rail label { font-size: var(--text-sm); color: var(--muted); font-weight: 600; }

/* ═══════════════════════════════════════════════════════════════════════════
 * THE THREE FAMILIES, plus the two components the design session added on
 * top of them: the REFUSAL CARD and the DATA-AGE CHIP.
 * ═══════════════════════════════════════════════════════════════════════════ */

/* ── REFUSAL. Every sentence that says "this did not hold / did not run". ── */
.refusal-card,
.wall-refusal, .desk-alerts-refusal, .desk-alerts-error,
.threshold-error, .whatif-error, .screen-filter-error,
.watchlist-note, .screen-failed, #disputes .error {
  color: var(--refusal);
  font-weight: 600;
}
.refusal-card,
.wall-refusal, .desk-alerts-refusal, .screen-failed, #disputes .error {
  background: var(--refusal-bg);
  border-left: 4px solid var(--refusal);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  padding: var(--sp-3) var(--sp-4);
  max-width: 80ch;
}
/* The refusal card proper: a heading-weight reason, then the receipt of why.
 * Styled as seriously as a result, because it is one. */
.refusal-card .refusal-reason { display: block; color: var(--refusal); }
.refusal-card .refusal-detail { display: block; margin-top: var(--sp-1); color: var(--body); font-weight: 400; font-size: var(--text-sm); max-width: 72ch; }
.refusal-card details { margin-top: var(--sp-2); font-weight: 400; }

/* Always-present error slots hide while empty; a refusal is loud, an empty
 * placeholder is nothing. */
.whatif-error:empty, .whatif-applied:empty, .desk-alerts-error:empty,
.desk-alerts-refusal:empty, .screen-filter-error:empty { display: none; }

/* ── MEASURE. Clocks, provenance, applied states, withheld figures. ──────── */
.wall-clock, .whatif-applied, .screen-whatif-note {
  background: var(--measure-bg);
  border-left: 4px solid var(--measure-fill);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  padding: var(--sp-3) var(--sp-4);
  color: var(--measure-soft);
  max-width: 80ch;
}
.band-withheld { color: var(--measure-soft); }

/* ── ABSENCE. Muted, italic, hatched — visibly a labelled non-value. ─────── */
.cell-absent, .overview-table td.overview-absent { background: var(--hatch); }
.cell-absent .absent, .export-absent, .live-absent, .depth-absent { color: var(--muted); font-style: italic; }

/* ── THE DATA-AGE CHIP. On every data-bearing surface; amber past its own
 * threshold. The dot is a second encoding beside the text, never the only
 * one; the text is always the age in words. ─────────────────────────────── */
.age-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  font: 600 var(--text-2xs)/1.2 var(--font-mono);
  letter-spacing: 0.03em;
  color: var(--muted);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: 3px 9px;
  /* ── THE CHIP FOLDS, AND IT NEEDS TO (CORRECTED 2026-08-27, CHIPWRAP) ─────
   * `white-space: nowrap` on the whole chip was right while a chip was four
   * words. READER-STATE (h)'s class words made it eight and it ran off the
   * clock bar on a 375px viewport. HALF-REFRESH AND CHIP-WORDS (g) took
   * those words, so the chip is four words again.
   *
   * THE PARAGRAPH ABOVE WAS TRUE AND ITS CONCLUSION WAS WRONG, and the wrong
   * half stood for a fortnight. It removed THIS sheet's `white-space: nowrap`
   * and reported the fold restored. `cockpit.css` carries its own `.age-chip`
   * rule at the same specificity, every public page loads it, and its nowrap
   * was untouched — so the chip stayed unbreakable and `flex-wrap` was dead
   * text. On 2026-08-27 three pages scrolled the whole document sideways to
   * 543 and 568 px at a 390 px viewport. The remedy is in `cockpit.css`, where
   * the declaration lives; read that comment before touching this one.
   *
   * The chip is NOT four words. It is however many the branch and the upstream
   * half produce, and the fault branches run past ninety characters. */
  flex-wrap: wrap;
  row-gap: 0.15em;
  vertical-align: middle;
}
.age-chip > * { white-space: nowrap; }
.age-chip::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--muted); flex: none; }
.age-chip[data-stale="true"] { color: var(--measure-text); border-color: var(--measure-fill); background: var(--measure-bg); }
.age-chip[data-stale="true"]::before { background: var(--measure-fill); }
/* THE CLASS WORDS ARE GONE (HALF-REFRESH AND CHIP-WORDS (g)-(i)). The chip
 * carried a second span, `.age-chip-class`, printing one phrase per clock
 * class in forty places. The threshold already states the class
 * behaviourally, so the rule and its wrapped-row media query came out
 * with the span. Do not restore them, and do not substitute an icon or a
 * colour: the answer is the time and nothing else. */

/* ═══════════════════════════════════════════════════════════════════════════
 * THE CLOCK BARS. One shared treatment: recessed strip, mono clock text.
 * The capture (stale) state keeps cockpit.css's red left bar untouched —
 * red stays the color of "does not hold".
 * ═══════════════════════════════════════════════════════════════════════════ */

.screen-clock, .price-clock {
  border-radius: var(--r-sm);
  background: var(--bg2);
}
.screen-clock { border-left: 4px solid var(--measure-fill); color: var(--muted); }
.screen-clock-line { font-size: var(--text-sm); }
.screen-clock-line, .price-clock-line, .price-clock-measured { font-family: var(--font-mono); }
.screen-clock-warning { font-size: var(--text-sm); }
.screen-clock { padding: var(--sp-4); }
.screen-source-facts { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-2) var(--sp-5); margin-block: var(--sp-3); }
.screen-clock .screen-source-facts p { margin: 0; }
.screen-source-completeness { color: var(--ink); font-weight: 600; }
.screen-freshness[data-catalogue-stale="true"] { color: var(--warn); }
.screen-provenance { border-top: 1px solid var(--line); margin-top: var(--sp-3); padding-top: var(--sp-3); }
.screen-provenance p { margin-top: var(--sp-3); overflow-wrap: anywhere; }
.price-clock-live { border-left: 4px solid var(--measure-fill); background: var(--bg2); }

/* ═══════════════════════════════════════════════════════════════════════════
 * ONE MARKET, AT ITS OWN ADDRESS (ADDRESSABLE).
 *
 * The market page reuses the clock-bar treatment above and the receipt
 * treatment below, so what is left here is the figure list: a label column and
 * a value column, on a page a reader is looking at ONE of rather than two
 * hundred. Nothing here is a new component.
 * ═══════════════════════════════════════════════════════════════════════════ */

.market-clock { border-radius: var(--r-sm); background: var(--bg2); border-left: 4px solid var(--measure-fill); color: var(--muted); }
.market-clock-line, .market-clock-warning { font-size: var(--text-sm); font-family: var(--font-mono); }
.market-figures { display: grid; grid-template-columns: minmax(12rem, 18rem) 1fr; gap: var(--sp-2) var(--sp-5); }
.market-figure { display: contents; }
.market-figure dt { color: var(--muted); font-size: var(--text-sm); }
.market-figure dd { margin: 0; font-family: var(--font-mono); }
/* The reason an absence exists sits under the absence, in prose rather than in
   mono: it is a sentence, not a number. */
.market-figure-why { font-size: var(--text-sm); color: var(--muted); font-family: var(--font-sans); }
/* A closed market's sentence is the loudest thing on the page it appears on,
   because it is the one that stops a reader acting on a dead quote. */
.market-closed { border-left: 4px solid var(--warn, #E5A93C); padding-left: var(--sp-4); }
.market-miss h2 { font-size: var(--text-lg); }

/* ═══════════════════════════════════════════════════════════════════════════
 * RECEIPTS — THE SIGNATURE COMPONENT. Four stages of disclosure on a native
 * <details>, so every stage below the popover works before any script runs:
 *
 *   1  the chip: the value with a dotted underline — "this expands"
 *   2  the popover: hover/focus on the CLOSED summary shows the formula and
 *      operands (they render inside <summary>, so no JS and no open state is
 *      needed — a closed details hides non-summary children, which is why the
 *      popover cannot live in the body)
 *   3  the derivation: open the details, the full tree inline
 *   4  the permalink: every receipt owns an id; the § link in the body is a
 *      stable address for it
 * ═══════════════════════════════════════════════════════════════════════════ */

summary { cursor: pointer; }
summary::marker { color: var(--accent); }

.figure { border: 1px solid var(--line); background: var(--bg1); border-radius: var(--r-sm); position: relative; }
.figure-summary { cursor: pointer; padding: 0.35rem 0.6rem; display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: baseline; }
.figure-summary:hover { background: var(--bg2); }
.figure-label { color: var(--muted); font-size: var(--text-xs); }
.figure-value {
  font-weight: 700;
  font-family: var(--font-mono);
  color: var(--ink);
  border-bottom: 1px dotted var(--accent-text);
}

/* stage 2: the popover, shown while the details is CLOSED and hovered or
 * keyboard-focused. Content lives inside the summary; generated by
 * `figureNode` from the same derivation the body renders. */
.receipt-pop {
  display: none;
  position: absolute;
  left: 0;
  top: calc(100% + 4px);
  z-index: 40;
  min-width: 18rem;
  max-width: 30rem;
  background: var(--bg1);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  padding: var(--sp-2) var(--sp-3);
  box-shadow: 0 10px 28px oklch(0 0 0 / 0.35);
  font-size: var(--text-xs);
  font-weight: 400;
  color: var(--muted);
  white-space: normal;
}
details:not([open]) > summary:hover .receipt-pop,
details:not([open]) > summary:focus-visible .receipt-pop { display: block; }
.receipt-pop .receipt-pop-formula { font-family: var(--font-mono); color: var(--soft); display: block; margin-bottom: 2px; }
.receipt-pop .receipt-pop-input { font-family: var(--font-mono); display: block; }

/* stage 3: the derivation tree */
.derivation { color: var(--muted); border-top: 1px solid var(--line); }
.derivation-formula { color: var(--soft); font-family: var(--font-mono); }
.derivation-inputs li, .derivation-provenance li { font-size: var(--text-xs); }

/* stage 4: the permalink */
.receipt-permalink { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--faint); text-decoration: none; }
.receipt-permalink:hover { color: var(--accent-text); }
:target > .figure-summary, details:target { outline: 2px solid var(--accent); outline-offset: 1px; }

.bound-receipt summary { color: var(--accent-text); font-size: var(--text-xs); }
.bound-receipt summary:hover { text-decoration: underline; }
.bound-receipt-body {
  background: var(--bg2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-3);
  margin-top: var(--sp-1);
  font-size: var(--text-xs);
  color: var(--muted);
}
.bound-receipt-body .assumption { color: var(--body); }
.bound-receipt-body .source { font-family: var(--font-mono); }

.bound-conservative { font-weight: 700; font-family: var(--font-mono); color: var(--ink); }
.bound-interval { color: var(--muted); font-family: var(--font-mono); }

/* ═══════════════════════════════════════════════════════════════════════════
 * TABLES. Semantic tables, styled: sticky uppercase header labels, hairline
 * rows, mono numerals, row hover — and on the market screen, the screener
 * pattern: density toggle, sticky first column, inline micro-bars.
 * ═══════════════════════════════════════════════════════════════════════════ */

.screen-table-wrap, .wall-table-wrap { border-radius: var(--r-sm); border-color: var(--line); }
.screen-table th, .wall-table th {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--muted);
  border-bottom: 1px solid var(--line-strong);
}
.screen-table th { background: var(--bg1); z-index: 2; }
.screen-table button.sort { color: var(--muted); font-size: var(--text-xs); text-transform: uppercase; letter-spacing: var(--track-label); }
.screen-table button.sort:hover { color: var(--accent-text); }
.screen-table tbody tr:hover td { background: var(--bg1); }
.screen-table td { border-bottom-color: var(--line); background: var(--bg0); }
.screen-table .cell-price, .screen-table .cell-spread, .screen-table .cell-mid,
.screen-table .cell-breakeven, .screen-table .cell-fair-value, .screen-table .cell-signature {
  font-family: var(--font-mono);
  text-align: right;
}
.screen-table th.col-numeric { text-align: right; }

/* density: the toggle swaps 40px rows for 32px rows; state is per-tab
 * (sessionStorage), set as an attribute on the wrap so CSS owns the metrics */
.screen-table td { padding: 0.55rem 0.6rem; }
.screen-table-wrap[data-density="compact"] .screen-table td { padding: 0.28rem 0.5rem; }
.screen-table-wrap[data-density="compact"] .screen-table { font-size: var(--text-xs); }

/* the first column stays put while the table scrolls sideways */
.screen-table th:first-child, .screen-table td:first-child {
  position: sticky;
  left: 0;
  z-index: 1;
}
.screen-table th:first-child { z-index: 3; }

/* the micro-bar: after-fee edge as length, sign as color, number as text —
 * the bar is the second encoding, never the only one */
.micro-bar {
  display: block;
  height: 3px;
  border-radius: 2px;
  background: var(--line);
  position: relative;
  overflow: hidden;
  margin-top: 4px;
  min-width: 3.5rem;
}
/* THE MICRO-BAR'S SECOND CUE IS ITS ORIGIN (DIRECTION, 2026-09-03). Both
 * directions grew rightward from the same edge and differed in fill alone, so
 * a greyscale column of these said nothing. A negative bar now grows LEFTWARD
 * from the right edge: position, no glyph, no copy. */
.micro-bar::before { content: ""; position: absolute; left: 0; right: auto; top: 0; bottom: 0; width: calc(var(--bar, 0) * 1%); background: var(--pos); }
.micro-bar[data-dir="neg"]::before { left: auto; right: 0; background: var(--neg); }

/* a whole row is a click target for its receipt */
.screen-table tbody tr[data-receipt-row] { cursor: pointer; }

.wall-table { border-collapse: collapse; width: 100%; font-size: var(--text-sm); }
.wall-table th, .wall-table td { padding: 0.3rem 0.6rem; border-bottom: 1px solid var(--line); text-align: left; }
.wall-table td { font-family: var(--font-mono); }
.wall-table tbody tr:hover { background: var(--bg1); }

/* ── THE GRID SCROLLS INSIDE ITSELF (DESIGN-1, NO-OVERFLOW) ────────────────
 *
 * Every table on this site sat in an `overflow-x: auto` container except this
 * one, and `/overview` was the page that scrolled sideways for it. The wrap
 * div is the same rule `.release-overlay-wrap`, `.pipeline-wrap`,
 * `.coherence-table-wrap` and `.history-table-wrap` already state.
 *
 * `min-width: 100%` on the table, not `width: 100%`: the table must still fill
 * a wide container, and must be allowed to exceed a narrow one instead of
 * crushing its own columns to fit.
 */
.overview-table-wrap { overflow-x: auto; margin: var(--sp-2) 0 var(--sp-3); }
.overview-table-wrap .overview-table { min-width: 100%; }
.overview-table th { background: transparent; font-size: var(--text-xs); text-transform: uppercase; letter-spacing: var(--track-label); color: var(--muted); }
.overview-table td { border-radius: 2px; font-family: var(--font-mono); }
.overview-swatch { border-radius: var(--r-sm); }

/* ═══════════════════════════════════════════════════════════════════════════
 * PANELS. One panel grammar: raised ground, hairline border, a heading that
 * reads as a desk label.
 * ═══════════════════════════════════════════════════════════════════════════ */

.screen-controls, .screen-whatif, .desk-alerts, .wall-section {
  background: var(--bg1);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}
.screen-controls { padding: var(--sp-4); }
.screen-search { display: flex; flex-wrap: wrap; align-items: end; gap: var(--sp-2) var(--sp-3); margin-bottom: var(--sp-4); }
.screen-search .form-field { flex: 1 1 22rem; max-width: 44rem; }
.screen-search input { width: 100%; min-width: 0; }

.screen-whatif {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-3);
  align-items: center;
  background: var(--bg2);
  border-left: 4px solid var(--measure-fill);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  padding: var(--sp-3) var(--sp-4);
  margin-top: var(--sp-3);
}
.whatif-legend { flex: 1 1 100%; margin: 0; color: var(--measure-soft); font-size: var(--text-sm); font-weight: 600; }
.whatif-error, .whatif-applied { flex: 1 1 100%; margin: 0; }
.whatif-applied { font-weight: 400; font-size: var(--text-sm); }

.screen-filters { gap: var(--sp-2) var(--sp-3); }

/* ── THE FIELD UNIT (DESIGN-1, FIELD-LABEL) ────────────────────────────────
 *
 * A label and its control are ONE flex item. The forms on `/screen` were a
 * wrapping row of alternating labels and controls, so the row broke BETWEEN a
 * label and the field it names: two pairs split at 1280 and three inputs
 * carried the next field's label at 390
 * (`docs/design/CRITIQUE_ADDENDUM_2026-08-25.md` §A3.5).
 *
 * `min-width: 0` and the input's `max-width` are what let the unit shrink
 * instead of pushing the form sideways: without them a unit is as wide as its
 * label plus a 7ch input and the narrow viewport pays for it.
 */
.form-field { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.form-field-check { gap: var(--sp-1); }
.form-field > input, .form-field > select { max-width: 100%; min-width: 0; }
.form-field-check > input { flex: none; }
.screen-filter-error { width: 100%; }

.desk-alerts { padding: var(--sp-4); margin-top: var(--sp-4); }
.desk-alerts-label {
  display: inline-block;
  font-size: var(--text-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--accent-text);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: 2px 10px;
  margin: 0 0 var(--sp-2);
}
.desk-alerts-permission, .desk-alerts-note, .desk-alerts-dropped { color: var(--muted); font-size: var(--text-sm); max-width: 80ch; }
.desk-alerts-form { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-3); align-items: center; margin: var(--sp-3) 0; }
.desk-alert-condition, .desk-alert-quarantined, .desk-alert-firing {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  background: var(--bg2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-3);
  margin: var(--sp-2) 0;
  font-size: var(--text-sm);
}
.desk-alert-condition { color: var(--body); }
.desk-alert-quarantined { border-style: dashed; border-color: var(--line-strong); color: var(--measure-soft); }
.desk-alert-firing { border-left: 4px solid var(--measure-fill); color: var(--ink); }
.desk-alerts-firings-heading { font-weight: 700; color: var(--ink); margin: var(--sp-3) 0 0; }
.desk-alert-delete { flex-shrink: 0; }

.hypotheticals-note { color: var(--muted); font-size: var(--text-sm); max-width: 80ch; }
.hypothetical {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  background: var(--bg2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-3);
  margin: var(--sp-2) 0;
  font-size: var(--text-sm);
}
.hypothetical button { flex-shrink: 0; }

/* ═══════════════════════════════════════════════════════════════════════════
 * THE CALCULATOR WALL.
 * ═══════════════════════════════════════════════════════════════════════════ */

.wall-lead { color: var(--body); max-width: 80ch; margin: 0 0 var(--sp-5); font-size: var(--text-md); }

.wall-section { padding: var(--sp-5); margin: 0 0 var(--sp-5); }
.wall-section h2 {
  font-size: var(--text-sm);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--ink);
  border-left: 3px solid var(--accent);
  padding-left: var(--sp-2);
  margin: 0 0 var(--sp-2);
}
.wall-blurb { color: var(--muted); font-size: var(--text-sm); max-width: 80ch; margin: 0 0 var(--sp-4); }

.wall-form {
  display: grid;
  grid-template-columns: minmax(16ch, auto) minmax(0, 28rem);
  gap: var(--sp-2) var(--sp-3);
  align-items: center;
  margin: 0 0 var(--sp-4);
}
.wall-form label { text-align: left; }
.wall-form button[type="submit"] { grid-column: 2; justify-self: start; margin-top: var(--sp-1); }
.wall-form textarea { resize: vertical; }
@media (max-width: 40rem) {
  .wall-form { grid-template-columns: minmax(0, 1fr); }
  .wall-form button[type="submit"] { grid-column: 1; }
}

.wall-result { margin-top: var(--sp-2); }
.wall-result > p { max-width: 80ch; }
.wall-bound { margin: var(--sp-2) 0; }
.wall-bound-label { color: var(--muted); font-size: var(--text-sm); }
.wall-note, .wall-walk-note, .wall-reading { color: var(--muted); font-size: var(--text-sm); max-width: 80ch; }
.wall-converted p { margin: 0 0 var(--sp-1); font-family: var(--font-mono); color: var(--ink); }

.devig-no-the {
  color: var(--measure-soft);
  font-size: var(--text-sm);
  border-left: 3px solid var(--measure-fill);
  padding-left: var(--sp-3);
  max-width: 80ch;
}
.devig-probabilities { color: var(--ink); font-family: var(--font-mono); }

/* ═══════════════════════════════════════════════════════════════════════════
 * THE COHERENCE SCANNER.
 * ═══════════════════════════════════════════════════════════════════════════ */

.coherence-framing, .coherence-key, .coherence-counts { max-width: 90ch; }
.coherence-key, .coherence-counts { color: var(--muted); font-size: var(--text-sm); }

.coherence-flag {
  background: var(--bg1);
  border: 1px solid var(--line);
  border-left: 4px solid var(--measure-fill);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  padding: var(--sp-3) var(--sp-4);
  margin: var(--sp-3) 0;
}
.coherence-family { font-weight: 700; color: var(--ink); }

.coherence-table-wrap { overflow-x: auto; margin: var(--sp-2) 0 var(--sp-3); }
.coherence-table { border-collapse: collapse; width: 100%; font-size: var(--text-sm); }
.coherence-table th {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--muted);
  text-align: left;
  border-bottom: 1px solid var(--line-strong);
  padding: 0.25rem 0.6rem;
}
.coherence-table td { padding: 0.25rem 0.6rem; border-bottom: 1px solid var(--line); text-align: left; vertical-align: baseline; }
.coherence-table tbody tr:hover { background: var(--bg2); }
.coherence-table td.coherence-leg-quote, .coherence-table td.coherence-bound { font-family: var(--font-mono); }
.coherence-table td.coherence-bound { white-space: nowrap; }
.coherence-table tfoot td { border-bottom: 0; border-top: 2px solid var(--line-strong); color: var(--ink); font-weight: 600; }
.coherence-table .bound-receipt-body { font-weight: 400; }
.coherence-gap { font-family: var(--font-mono); }
.coherence-assumption { color: var(--measure-soft); font-size: var(--text-sm); max-width: 80ch; }
.coherence-bound { font-family: var(--font-mono); }
.coherence-empty { border: 1px solid var(--line); border-radius: var(--r-md); background: var(--bg1); padding: var(--sp-4); max-width: 90ch; }

.coherence-depth { border-top: 1px solid var(--line); margin-top: var(--sp-3); padding-top: var(--sp-3); font-size: var(--text-sm); }
.coherence-depth[data-depth="fails"] { color: var(--refusal); }
.coherence-depth[data-depth="none"] { background: var(--hatch); }
.coherence-depth-note, .coherence-depth-cap { color: var(--muted); }

/* ═══════════════════════════════════════════════════════════════════════════
 * THE COCKPIT (tool page): rows, verdicts, rail — and on `/`, the status
 * wall: the summary, clocks, provenance and gates rendered as a dense grid
 * of labelled tiles rather than a column of paragraphs.
 * ═══════════════════════════════════════════════════════════════════════════ */

.product { padding-block: var(--sp-5) var(--sp-2); border-bottom: 1px solid var(--line); }
.no-script { color: var(--muted); max-width: 62ch; }

.rows { max-height: 70vh; overflow-y: auto; border: 1px solid var(--line); border-radius: var(--r-sm); }

.verdict { border-radius: 3px; font-size: var(--text-xs); font-family: var(--font-mono); }
.title { color: var(--ink); }
.row-face .optimism, .row-figures .optimism { color: var(--ink); font-family: var(--font-mono); }
.row-face .pm-fee-short { font-family: var(--font-mono); }

.rail .controls { margin-bottom: var(--sp-4); }
.rail .threshold, .rail .live-controls {
  background: var(--bg1);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}
.rail .live-controls { padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-4); }
.rail .live-age, .rail .export-note, .rail .export-absent { color: var(--muted); font-size: var(--text-sm); margin: 0.4rem 0 0; }

.status-strip, .provenance, .gates { border-radius: var(--r-sm); }
.status-strip { background: var(--bg1); }
.status-strip .status-line { font-family: var(--font-mono); font-weight: 600; font-size: var(--text-sm); }
.provenance { border-left-color: var(--measure-fill); }
.gates { background: var(--bg2); }

/* the summary counts as a wall of labelled tiles */
.summary { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.summary p { margin: 0; }
.summary .verdict-summary, .summary .counterpart-summary {
  background: var(--bg1);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--text-sm);
  flex: 1 1 100%;
}
.summary .verdict-summary { color: var(--ink); }

/* ═══════════════════════════════════════════════════════════════════════════
 * THE DISPUTE LIST (editorial, on the tool page).
 * ═══════════════════════════════════════════════════════════════════════════ */

#disputes { padding-block: var(--sp-6); border-bottom: 1px solid var(--line); }
#disputes h2 { font-size: var(--text-xl); letter-spacing: var(--track-tight); text-transform: none; margin-bottom: var(--sp-3); }
#disputes .reading-note { color: var(--muted); max-width: 80ch; font-size: var(--text-sm); }
#disputes .incident-list { margin: var(--sp-4) 0; padding-left: 1.2rem; }
#disputes .incident { border-bottom: 1px solid var(--line); padding: var(--sp-3) 0; max-width: 90ch; }
#disputes .incident:last-child { border-bottom: 0; }
#disputes .incident-line { margin: 0 0 var(--sp-1); font-size: var(--text-sm); color: var(--muted); }
#disputes .incident-line:first-child { font-size: var(--text-base); font-weight: 600; color: var(--ink); }
#disputes .bound { color: var(--faint); font-size: var(--text-xs); }
/* ── THE ONE PROSE HABITAT THAT CARRIES A BARE URL (DESIGN-1, NO-OVERFLOW) ──
 *
 * The dispute seed list is EXTERNAL editorial content and its citations are
 * written as bare URLs. A URL is one unbreakable token, so a line holding one
 * is wider than a 390px column and the whole document scrolled sideways with
 * it: `/cockpit` measured 432px against a 390px viewport
 * (`docs/design/CRITIQUE_ADDENDUM_2026-08-25.md` §A4.1; reproduced at 432 by
 * `tools/viewport-width.ts`).
 *
 * `anywhere`, not `break-word`: `anywhere` lets the long word count against
 * the intrinsic minimum width too, so a table or a flex item holding one stops
 * demanding room for it. `break-word` breaks the line and keeps the demand.
 *
 * SCOPED TO THIS PANEL RATHER THAN TO ALL PROSE, and the reason is that
 * `tools/viewport-width.ts` sweeps all eighteen pages: the next habitat that
 * grows a URL reds there rather than being covered here in advance by a rule
 * nobody measured. The panel is the habitat NOW.md's raised-not-fixed list
 * already names as outside both prose census arms.
 */
#disputes .incident-line, #disputes .reading-note { overflow-wrap: anywhere; }

/* ═══════════════════════════════════════════════════════════════════════════
 * THE EDITORIAL (essay pages): kickers, cards, strips, worked examples, code.
 * ═══════════════════════════════════════════════════════════════════════════ */

.editorial { padding-block: var(--sp-6); border-bottom: 1px solid var(--line); }
.editorial h2 { font-size: var(--text-xl); letter-spacing: var(--track-tight); margin-bottom: var(--sp-4); text-transform: none; }
.editorial h3 { font-size: var(--text-md); margin: var(--sp-5) 0 var(--sp-2); color: var(--soft); }
.editorial p { margin-bottom: var(--sp-3); color: var(--body); }
.editorial p.kicker, .kicker {
  color: var(--accent-text);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 2px;
  margin-bottom: var(--sp-1);
  font-family: var(--font-mono);
}


/* ── THE STATUS LINE (GROUNDUP_2_DESIGN.md §1; T1B A) ───────────────────────
 *
 * Six tiles above the wall on `/`. Design §1 gives the line 48 px and one row
 * at 1440; below that it WRAPS rather than scrolls, because a status line that
 * scrolls sideways hides tiles a reader cannot know are there — the defect T1
 * measured at 2,027 px inside 1,440 on the mockups.
 *
 * ONE CHIP PER HALF. `.status-chips` is a row, and a tile with two clocks draws
 * two chips inside it. The rule is enforced in the generator and in
 * `tests/compliance/status-line.test.ts`; this only has to leave room for both.
 *
 * The hover card is the provenance card design §9 describes. It opens on hover
 * AND on keyboard focus — the tile carries `tabindex="0"` for that reason, so a
 * reader who never uses a mouse reaches the same denominators.
 */
.status-line { display: flex; align-items: center; gap: var(--sp-4); padding: var(--sp-2) var(--sp-4);
  border-bottom: 1px solid var(--line); background: var(--bg0); }
.status-tiles { display: flex; flex-wrap: wrap; align-items: stretch; gap: var(--sp-2) var(--sp-5); min-width: 0; }
.status-tile { position: relative; display: flex; flex-direction: column; justify-content: center;
  gap: 2px; padding: var(--sp-1) 0; white-space: nowrap; }
.status-label { font-family: var(--font-mono); font-size: var(--text-2xs); letter-spacing: var(--track-label);
  text-transform: uppercase; color: var(--muted); }
.status-val { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--ink);
  font-variant-numeric: tabular-nums slashed-zero lining-nums; }
.status-word { color: var(--muted); }
.status-figure { font-weight: 600; }
/* ABSENCE IS A WORD AND IT IS QUIET (design §5). It is muted rather than
 * refusal-coloured: a lane that has not answered yet is not a refusal, and
 * painting it red would make the ordinary first paint look like a fault. */
.status-absent { color: var(--faint); font-style: normal; }
.status-chips { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.status-chip { font-family: var(--font-mono); font-size: var(--text-2xs); color: var(--muted); }
.status-readagain { margin-left: auto; }
.status-hover { position: absolute; top: 100%; left: 0; z-index: 5; display: none; margin: var(--sp-1) 0 0;
  min-width: 22rem; max-width: 34rem; padding: var(--sp-3); white-space: normal;
  background: var(--bg1); border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  font-size: var(--text-xs); color: var(--body); }
.status-tile:hover .status-hover, .status-tile:focus-within .status-hover, .status-tile:focus .status-hover { display: grid;
  grid-template-columns: auto 1fr; gap: var(--sp-1) var(--sp-3); }
.status-hover dt { color: var(--muted); }
.status-hover dd { margin: 0; }

/* ── `/filter`'S MODE TOGGLES (DESIGN §3 S2; T5 FILTER, 2026-09-03) ──────────
 *
 * Three switches over one table. Each carries its own state WORD, because a
 * switch that looks the same on and off is a control a reader cannot read back
 * — the reason every absence on this site is a word rather than a blank. The
 * pressed state is a border and a background, never colour alone. */
.screen-modes { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-2); }
.screen-mode { display: inline-flex; align-items: baseline; gap: var(--sp-2); }
.screen-mode-state { font-family: var(--font-mono); font-size: var(--text-2xs);
  text-transform: uppercase; letter-spacing: var(--track-label); color: var(--muted); }
.screen-mode[aria-checked="true"] { border-color: var(--accent); background: var(--bg2); }
.screen-mode[aria-checked="true"] .screen-mode-state { color: var(--ink); font-weight: 600; }
.screen-mode-note { color: var(--body); }
/* The last-look disclosure is quiet: it is a standing fact about where the
 * store lives, not a state the reader has to act on. */
.lastlook-note { color: var(--muted); font-size: var(--text-xs); }
.screen-export { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-2); }
.screen-export-note { color: var(--muted); font-size: var(--text-xs); }

/* ── THE 390 COLLAPSE (DESIGN §4; T5 FILTER, 2026-09-03) ─────────────────────
 *
 * MEASURED, NOT GUESSED: at 390 the six tiles stacked to 463 px on this build,
 * so the first panel of `/` started below the fold. T4 DESK noted it and ruled
 * it T5's.
 *
 * DESIGN §4 says a panel "collapses to its lead and chip" at this width. A
 * status tile has no rows to shed, so what collapses is its SHAPE: label, value
 * and chips stop stacking and sit on one line each. Six lines, not six blocks.
 *
 * NOTHING IS HIDDEN. A line too long is clipped with an ellipsis and the tile's
 * hover carries it in full, which is §4's own move for a table's columns.
 * Hiding a chip would take provenance off a public screen to buy height this
 * layout gets for free. The hover spans the line here: a 22rem minimum inside
 * 390 px overflows, and §4 forbids a body that scrolls sideways. */
@media (max-width: 390px) {
  .status-line { flex-wrap: wrap; align-items: stretch; }
  .status-tiles { display: flex; flex-direction: column; flex-wrap: nowrap; gap: var(--sp-1); width: 100%; }
  /* `min-width: 0` on the children is what lets the ellipsis fire in a flex row. */
  /* `justify-content` is reset because the base rule centres a COLUMN tile,
   * and that was invisible while this row filled its own width. With the
   * chip row on its own line the label and value stopped filling it and
   * drifted to the middle. Caught in the eye pass at 390, not by a gate. */
  .status-tile { flex-direction: row; align-items: baseline; flex-wrap: wrap;
    justify-content: flex-start; gap: var(--sp-2); padding: 0;
    white-space: nowrap; min-width: 0; overflow: hidden; }
  .status-tile > * { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  /* THE CHIP ROW WRAPS HERE, AND THE ELLIPSIS ABOVE IS WHY IT MUST.
   * `text-overflow: ellipsis` reaches inline text and never a flex ITEM, so a
   * `nowrap` chip row inside `.status-tile { overflow: hidden }` cut its second
   * chip mid-glyph with no mark that anything was cut. Measured at 390 on `/`:
   * the two-clock tiles read 149->351 and 191->310. A tile with two clocks is
   * the design's own case (see the ONE CHIP PER HALF note above), so the row
   * wraps and buys one line of height rather than hiding a clock. */
  .status-chips { flex-wrap: wrap; flex-basis: 100%; }
  .status-hover { left: 0; right: 0; min-width: 0; max-width: none; white-space: normal; }
  .status-readagain { margin-left: 0; }
}

/* Desk cards use the established 280px column floor. Breakpoints include
 * the container padding and gaps. Content determines each card height. */

.signin-band { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--line); background: var(--bg0); }
.signin-band p { margin: 0; flex: 1 1 20rem; min-width: 0; color: var(--muted); font-size: var(--text-sm); }

.desk-bound { margin: var(--sp-4) var(--sp-4) 0; color: var(--warn); font-size: var(--text-sm);
  font-family: var(--font-mono); }
.desk { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-4); padding-top: var(--sp-5); align-items: start; }
@media (min-width: 39rem) { .desk { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: stretch; } }
@media (min-width: 57.5rem) { .desk { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.panel { display: flex; flex-direction: column; gap: var(--sp-2); min-width: 0;
  background: var(--bg1); border: 1px solid var(--line); border-radius: var(--r-lg); padding: var(--sp-5); }
.panel-head h2 { margin: 0; font-size: var(--text-xs); font-weight: 700; letter-spacing: 1px;
  text-transform: uppercase; color: var(--muted); }
.panel-head a { color: inherit; }
.panel-lead { margin: 0; font-family: var(--font-mono); color: var(--ink);
  font-variant-numeric: tabular-nums slashed-zero lining-nums; }
.panel-label { display: block; color: var(--muted); font-size: var(--text-2xs);
  letter-spacing: var(--track-label); text-transform: uppercase; }
.panel-figure { font-size: var(--text-md); }
.panel-figure b { font-size: var(--text-xl); font-weight: 700; }
/* THE FIGURE AND ITS WORDS ARE SEPARATE ELEMENTS, so the gap has to be a rule.
 * `0of 0` was what the eye pass read before this line existed. */
.panel-figure > * + * { margin-left: 0.5ch; }
.panel-rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-3); }
.panel-row { display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-1);
  font-size: var(--text-sm); line-height: 1.5; border-top: 1px solid var(--line); padding-top: var(--sp-2); }
.panel-row-title { min-width: 0; max-width: 100%; color: var(--body); overflow-wrap: anywhere; }
.panel-row-meta { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-3); }
.panel-row-figure { font-family: var(--font-mono); color: var(--ink);
  font-variant-numeric: tabular-nums slashed-zero lining-nums; }
/* THE REASON RIDES THE ROW, never a colour alone: a verdict a reader cannot
 * read out loud is not a verdict (DESIGN §0 rule 1). */
.panel-row-status { color: var(--muted); font-size: var(--text-xs); overflow-wrap: anywhere; }
.panel-caption, .panel-preview { margin: 0; color: var(--muted); font-size: var(--text-xs); }
.panel-nav { align-self: flex-start; display: inline-flex; flex-wrap: wrap; gap: var(--sp-2);
  padding: var(--sp-2) 0; font-size: var(--text-sm); font-weight: 600; line-height: 1.5; }
.panel :is(a, summary):hover { color: var(--ink); text-decoration-thickness: 2px; }
.panel :is(a, summary):focus-visible { outline: 2px solid var(--accent-text); outline-offset: 3px; border-radius: var(--r-sm); }
.panel-foot { margin: 0; }
/* The row sets the card height. Actions and clocks share its lower edge. */
.panel-nav { margin-top: auto; }
.panel-foot:first-child { margin-top: auto; }
.field-notes-link { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-3); padding-block: var(--sp-5); font-size: var(--text-sm); }
.field-notes-link span { color: var(--muted); }
@media (min-width: 39rem) {
  .desk > .panel { min-block-size: 23rem; }
  .desk > .panel:not(:has(.panel-nav)) .panel-foot { margin-top: auto; }
}

code, pre { font-family: var(--font-mono); font-size: var(--text-sm); }
pre {
  background: var(--bg2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--sp-4) var(--sp-5);
  overflow-x: auto;
  margin: var(--sp-3) 0;
  color: var(--soft);
  line-height: 1.7;
}
.hit { color: var(--accent-text); font-weight: 700; }

.ex {
  background: var(--bg1);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--r-lg) var(--r-lg) 0;
  padding: var(--sp-4) var(--sp-5);
  margin: var(--sp-4) 0;
}
.ex p { margin-bottom: var(--sp-2); }
.ex b { color: var(--ink); }

/* ═══════════════════════════════════════════════════════════════════════════
 * THE SCHEDULE PAGES (stats.html, compare.html).
 * ═══════════════════════════════════════════════════════════════════════════ */

.editorial .receipt {
  color: var(--muted);
  font-size: var(--text-sm);
  border-left: 3px solid var(--measure-fill);
  padding-left: var(--sp-3);
  max-width: 80ch;
}

.editorial blockquote {
  background: var(--bg2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--body);
  font-size: var(--text-sm);
  margin: var(--sp-3) 0;
  padding: var(--sp-3) var(--sp-4);
  max-width: 80ch;
}

pre.derived-table { line-height: 1.5; padding: var(--sp-3) var(--sp-4); }

#pending-doors {
  background: var(--hatch);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--muted);
  font-style: italic;
  font-size: var(--text-sm);
  padding: var(--sp-3) var(--sp-4);
  max-width: 80ch;
}

/* ═══════════════════════════════════════════════════════════════════════════
 * ⚠️ THE VENUE-DIRECTORY BLOCK WAS REMOVED 2026-09-03 (T3 LADDER §B). It styled
 * `.venue-cards`, `.venue`, `.venue-operator`, `.venue-tier`, `.venue-facts`
 * and the five `.pill.tier-*` ramps, and `/directory` was the only page that
 * ever carried any of them. The page is cut by rule, so the rules are dead
 * bytes every reader on this site pays for on first paint. `.pill` and
 * `.pill.dim` STAY: `/` still uses both.
 * ═══════════════════════════════════════════════════════════════════════════ */

.pipeline-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-sm); margin: var(--sp-4) 0; }
table.pipeline { border-collapse: collapse; width: 100%; font-size: var(--text-sm); }
table.pipeline th, table.pipeline td { padding: 0.35rem 0.7rem; border-bottom: 1px solid var(--line); text-align: left; vertical-align: top; }
table.pipeline th {
  background: var(--bg1);
  color: var(--muted);
  font-size: var(--text-xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  white-space: nowrap;
}
table.pipeline td:nth-child(2), table.pipeline td:nth-child(3) { white-space: nowrap; font-family: var(--font-mono); }
table.pipeline tbody tr:hover { background: var(--bg1); }
table.pipeline td.unnamed { color: var(--faint); font-style: italic; }

/* ── THE RELEASE-DAY OVERLAY (Wave 5 (e), 2026-08-13) ────────────────────────
 *
 * The settlement calendar's join between the venue's published maturities and
 * the agencies' published release days. Modelled on `.pipeline` above rather
 * than sharing it: that rule forces mono and nowrap on its second and third
 * columns, and this table's third column is a release title that has to wrap.
 *
 * The wrap div is the 390px requirement stated as a rule, the same way
 * `figure.chart` and `.pipeline-wrap` state it: a wide table scrolls inside its
 * own container and never pushes the page sideways.
 * ─────────────────────────────────────────────────────────────────────────── */
.release-overlay-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-sm); margin: var(--sp-4) 0; }
table.release-overlay { border-collapse: collapse; width: 100%; font-size: var(--text-sm); }
table.release-overlay caption {
  caption-side: bottom;
  padding: var(--sp-3) var(--sp-4);
  color: var(--muted);
  font-size: var(--text-xs);
  text-align: left;
  max-width: 80ch;
}
table.release-overlay th, table.release-overlay td {
  padding: 0.35rem 0.7rem;
  border-bottom: 1px solid var(--line);
  text-align: left;
  vertical-align: top;
}
table.release-overlay th {
  background: var(--bg1);
  color: var(--muted);
  font-size: var(--text-xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  white-space: nowrap;
}
table.release-overlay td.release-when { font-family: var(--font-mono); white-space: nowrap; }
table.release-overlay td.release-count { font-family: var(--font-mono); white-space: nowrap; text-align: right; }
table.release-overlay tbody tr:hover { background: var(--bg1); }
/* A day the schedules name nothing on is a labelled non-value, so it wears the
 * absence family: muted italic on the hatch, never an empty cell. */
table.release-overlay td .absent { color: var(--muted); font-style: italic; }
/* The publisher, labelled rather than punctuated. See the generator for why. */
table.release-overlay td .release-agency {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  margin-right: 0.35em;
}
ul.release-provenance { margin: var(--sp-2) 0 var(--sp-4) var(--sp-5); }
ul.release-provenance li { font-family: var(--font-mono); font-size: var(--text-xs); }

/* ═══════════════════════════════════════════════════════════════════════════
 * THE COMMAND PALETTE (⌘K). A native <dialog>; the index is built at build
 * time from the pages themselves and served same-origin.
 * ═══════════════════════════════════════════════════════════════════════════ */

.palette {
  margin: 8vh auto 0;
  width: min(40rem, 92vw);
  background: var(--bg1);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-lg);
  padding: 0;
  color: var(--body);
  box-shadow: 0 24px 60px oklch(0 0 0 / 0.45);
}
.palette::backdrop { background: oklch(0 0 0 / 0.5); }
.palette-input {
  width: 100%;
  box-sizing: border-box;
  font: var(--text-md)/1.4 var(--font-sans);
  padding: var(--sp-3) var(--sp-4);
  background: var(--bg2);
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  color: var(--ink);
}
.palette-input:focus { outline: none; }
.palette-results { max-height: 50vh; overflow-y: auto; margin: 0; padding: var(--sp-2) 0; list-style: none; }
.palette-item { padding: var(--sp-2) var(--sp-4); display: grid; gap: 2px; cursor: pointer; }
.palette-item[aria-selected="true"], .palette-item:hover { background: var(--bg2); }
.palette-item-label { color: var(--ink); font-size: var(--text-sm); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.palette-item-detail { color: var(--faint); font-size: var(--text-xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.palette-empty { padding: var(--sp-3) var(--sp-4); color: var(--muted); font-size: var(--text-sm); }

/* ═══════════════════════════════════════════════════════════════════════════
 * THE FEE-TRUTH SURFACES (S3): build-time chart figures and receipt stacks.
 *
 * The SVG is emitted by `src/app/charts/constitution.ts` with `var(--…)` fills,
 * so one build serves both themes and both pos/neg schemes — nothing here sets
 * a data colour. Wide charts scroll inside their own figure rather than pushing
 * the page sideways, which is the 390px requirement stated as a rule.
 * ═══════════════════════════════════════════════════════════════════════════ */

figure.chart { margin: var(--sp-5) 0; max-width: 100%; overflow-x: auto; }
/* THE MINIMUM WIDTH MOVED TO THE EMITTER (DESIGN-3, CHART-TYPE;
   `## OVERNIGHT DISPOSITIONS` (h)). It was a flat 320 px here, which is under
   the 342 px phone column and so never bit. The floor a chart actually needs
   depends on its own viewBox — `minChartWidthPx()` in
   `src/app/charts/constitution.ts` solves it from `--text-xs` and the smallest
   font-size a form writes — and this sheet cannot read a viewBox. So each SVG
   carries its own `min-width` inline and the figure above scrolls when the
   column is narrower. A rule here would have to be one number for two viewBox
   widths, which is a second list of the kind the emitter already owns. */
/* The picture scrolls sideways when the column is narrower than the minimum
   its own labels need; the table twin beside it never does. See
   `chartFigureBody` in `src/app/charts/constitution.ts` for why they are two
   boxes. `figure.chart` keeps its own `overflow-x` as the outer backstop. */
figure.chart .chart-frame { overflow-x: auto; }
figure.chart svg { display: block; width: 100%; height: auto; }
/* THE TABLE TWIN (CHARTFORM). It carries the values the marks no longer do, so
   it is ordinary visible markup — no disclosure, no `hidden`, nothing a reader
   has to open. It borrows `.wall-table` whole; these three declarations are the
   figure-specific part: the gap under the picture, the caption naming the chart
   it twins, and a top rule tying the two together. */
figure.chart .chart-twin { margin-top: var(--sp-4); border-top: 1px solid var(--line); }
figure.chart .chart-twin caption {
  caption-side: top;
  text-align: left;
  color: var(--muted);
  font-size: var(--text-xs);
  padding: var(--sp-2) 0;
}
figure.chart figcaption {
  color: var(--muted);
  font-size: var(--text-xs);
  margin-top: var(--sp-2);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  align-items: baseline;
}

/* ── EVERY MARK ANSWERS (Wave 7, A1) ────────────────────────────────────────
 *
 * The detail used to be an SVG `<title>`, which is the operating system's own
 * tooltip: it arrives after a hold, at the pointer rather than at the mark, and
 * on a moving cursor it never arrives. Hovering said nothing. The answer is now
 * drawn into the chart by `src/app/charts/constitution.ts` — a plate, the value
 * in strong type, its label and the denominator its share is of — and these
 * four rules are the whole mechanism.
 *
 * NO LISTENER, ANYWHERE. `:hover` and `:focus-within` are the events. The pages
 * carrying these charts are baked HTML with zero event listeners on them, and
 * `tests/app/chart-craft.test.ts` pins that count at zero.
 *
 * The popover carries `opacity="0"` as a presentation attribute in the SVG, so
 * a page that loaded without this stylesheet hides them rather than stacking
 * every popover over its own chart. These rules are CSS properties and beat the
 * attribute, which is the cascade working the way round we want.
 * ─────────────────────────────────────────────────────────────────────────── */

.chart-mark { outline: none; }
/* The lift is an OUTLINE around the mark, and the first cut was a filled copy of
 * it. That covered the mark's own label: the interactive layer paints above the
 * data, so a hovered tile lost the value written on it at the moment a reader
 * pointed at the tile. An outline in --accent reads in both themes and hides
 * nothing. */
.chart-lift { fill: none; pointer-events: none; stroke: transparent; stroke-width: 2; }
.chart-mark:hover .chart-lift,
.chart-mark:focus-within .chart-lift { stroke: var(--accent); }
/* The oversized transparent target: at least 24px in the narrow dimension, so
 * the slivers a reader most wants to ask about stop being the hardest to hit. */
.chart-hit { fill: transparent; }
.chart-pop { pointer-events: none; transition: opacity 90ms ease-out; }
.chart-mark:hover .chart-pop,
.chart-mark:focus-within .chart-pop { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .chart-pop { transition: none; }
}

.receipts { display: grid; gap: var(--sp-2); margin: var(--sp-4) 0; }

/* ── THE EARLY-ACCESS CTA IN THE TOPBAR (S3, task 5) ─────────────────────────
 *
 * The button moved from two per-page strips into the chrome, so it now sits in
 * a sticky bar that also has to hold the wordmark, the tagline and two tool
 * controls at 390px. The tagline already hides under 64rem; below 40rem the
 * CTA becomes its label alone at a smaller size rather than disappearing —
 * the ONE thing on the bar a reader without JavaScript can act on should not
 * be the thing the narrow layout drops. Nothing here can overflow: the topbar
 * wraps instead of scrolling the page sideways.
 * ─────────────────────────────────────────────────────────────────────────── */

.topbar { flex-wrap: wrap; row-gap: var(--sp-2); }
.topbar-tools .cta { padding: 6px 12px; white-space: nowrap; }

/* ── THE TAGLINE YIELDS BEFORE THE BAR WRAPS (Wave 2 eyeball finding) ────────
 *
 * THE DEFECT. `flex-wrap: wrap` above buys the 390px guarantee — nothing
 * overflows — and cost the desktop one. Flex breaks lines using each item's
 * HYPOTHETICAL size before it shrinks anything, and `.topbar-brand` was sized
 * by its content: mark plus wordmark plus the WHOLE tagline. On the three
 * pages with long taglines (directory, compare, overview) brand + tools no
 * longer fit on one line at 1280-1440px, so the tools row dropped to a second
 * line — where `margin-left: auto` parked it on the right, under nothing, and
 * the eyeball called it misaligned. `.tag` already had `text-overflow:
 * ellipsis`, and it never fired, because the bar wrapped instead of squeezing.
 *
 * THE FIX, which is one property. `flex: 1 1 0` gives the brand a ZERO flex
 * basis: its hypothetical main size no longer includes the tagline, so it can
 * never be the reason a line breaks. It then grows into whatever the tools
 * leave, and the shrink lands on the one item that can absorb it — the
 * ellipsis finally does the job it was written for.
 *
 * WHAT IS NOT LOST. The tagline is approved copy and no byte of it changes:
 * the full text stays in the DOM (an ellipsis is paint, not truncation), the
 * source pages the claim sweeps read are untouched, and `page-chrome.ts` puts
 * the same bytes in a `title` so a reader can reach them. Below 64rem the
 * tagline is `display: none` and the brand is two flex:none children, so the
 * 390px layout is bit-for-bit the one that shipped.
 * ─────────────────────────────────────────────────────────────────────────── */
.topbar-brand { flex: 1 1 0; }
.wordmark { flex: none; }
.tag { min-width: 0; flex: 0 1 auto; }

@media (max-width: 40rem) {
  .topbar { gap: var(--sp-3); padding-inline: var(--sp-3); }
  .topbar-tools { gap: var(--sp-1); }
  .topbar-tools .cta { padding: 5px 9px; font-size: var(--text-xs); }
}

/* ── THE BRAND CANNOT BE CRUSHED BY THE TOOLS (MOBILE-GATE, task 4) ──────────
 *
 * THE DEFECT, from the owner's phone: the wordmark sat UNDER the CTA. The Wave 2
 * fix above gave `.topbar-brand` a zero flex basis so it could never force a
 * line break, and `min-width: 0` so the tagline's ellipsis could fire. Together
 * those say the brand box may be sized to ZERO — and its children are
 * `flex: none`, so they do not shrink with it. Below about 1024px, where the
 * tools row is CTA + Search + Display at its content width, what is left for the
 * brand can be less than the mark and the wordmark need, and the two overlap.
 *
 * The band nobody pinned. Wave 3 measured 1024-1600 and exactly 390, and the
 * collision lives between them: at 390 the narrow rules shrink the CTA and both
 * the palette button and the prefs control ship `hidden` until JavaScript
 * reveals them, so the bar fits; from ~430 to 1024 they are revealed, the CTA is
 * full size and the tagline is display:none, and the arithmetic goes negative
 * with no rule to stop it.
 *
 * THE FIX IS IN THE SAME LINEAGE AS THE DEFECT: `flex: 1 1 0` is kept, because
 * the desktop wrap it prevents is a real defect too, and a `min-width` floor is
 * put under it. `min-content` is exactly the mark plus the wordmark plus their
 * gap — no magic number, no font assumption — so the brand can still absorb all
 * the shrink the tagline has to give and can never be squeezed past its own
 * unshrinkable children. Being narrower than that floor now makes the topbar
 * WRAP, which `flex-wrap: wrap` above already handles and which the 390px
 * guarantee already relies on.
 * ─────────────────────────────────────────────────────────────────────────── */
.topbar-brand { min-width: min-content; }

/* ── THE FLOOR IS LIFTED WHERE THE TAGLINE RENDERS (SHELL-1, third fix) ──────
 *
 * THE DEFECT, reported by the owner on `/directory` and `/fees`: the tagline
 * collides with the early-access button. It is the Wave 2 defect back, and the
 * rule above put it back.
 *
 * WHY. `min-content` on `.topbar-brand` is NOT "the mark plus the wordmark plus
 * their gap", which is what the block above claims. `.topbar-brand` has THREE
 * children. `.tag` carries `white-space: nowrap`, so its min-content size is its
 * whole text; it is `flex: 0 1 auto`, so its flex base size is that same width
 * and clamps nothing down; and `min-width: 0` on it is a floor, not a cap. So
 * the brand's min-content contains the entire tagline. A flex item's
 * hypothetical main size is clamped up by its own `min-width`, which is the
 * figure line breaking uses — so the tagline was back inside the calculation
 * `flex: 1 1 0` had taken it out of, and the tools row dropped to a second line.
 * Measured in a browser at 1280 on `/directory`: bar 64.6px tall (two rows),
 * tools row at y=28.8, tagline unellipsised at its full 764px.
 *
 * THE FIX IS A FOURTH RULE, NOT THE REMOVAL OF THE THIRD. The floor is what
 * stops the phone defect and it is still needed at every width where the tagline
 * is hidden and the three tool controls are the whole bar. Above that width the
 * tagline is present and IS the thing that yields, so the floor has nothing to
 * do and taking it away costs nothing.
 *
 * THE BREAKPOINT IS THE TAGLINE'S OWN, NOT A NEW ONE. `.tag` is hidden at
 * `max-width: 64rem`; this lifts the floor at `min-width: 64rem`. The two
 * OVERLAP at exactly 64rem rather than leaving a gap, and the overlap is the
 * safe direction: at 64rem the tagline is hidden AND the floor is lifted, which
 * `tests/compliance/topbar-cascade.test.ts` prices as no crush because the bar
 * there is 992px against a tools row under 400px. A complementary pair with a
 * gap would leave a band of viewports carrying the defect.
 * ─────────────────────────────────────────────────────────────────────────── */
@media (min-width: 64rem) {
  .topbar-brand { min-width: 0; }
}


/* In the footer the note is the whole sign-off, so it carries no left margin
 * of its own — the middot before it already does that spacing. */
.site-footer .soon { margin-left: 0; }

/* ═══════════════════════════════════════════════════════════════════════════
 * THE HISTORY CHARTS (HISTORY-LANE). This site's first time-series.
 *
 * SEQUENTIAL, NOT CATEGORICAL: the three band marks are three DEPTHS of one
 * quantity, so they are one hue at three opacities rather than three hues —
 * the same mechanism the fee heatmap uses, and it inherits the theme and the
 * pos/neg scheme for free. Every fill is set in the emitted SVG so that one
 * rule set serves both themes.
 *
 * WIDE CONTENT SCROLLS INSIDE ITS OWN BOX. The table can outgrow a phone; the
 * page body must never scroll sideways.
 * ═══════════════════════════════════════════════════════════════════════════ */

.history-reading { color: var(--muted); font-style: italic; }

.history-figure { margin: 0 0 var(--sp-5); }
.history-takeaway {
  font: 600 var(--text-sm)/1.35 var(--font-sans);
  color: var(--ink);
  margin: 0 0 var(--sp-2);
}
.history-chart { width: 100%; height: auto; display: block; }
.history-chart-host { overflow-x: auto; }
.history-tick, .history-unit { font: 500 var(--text-2xs)/1 var(--font-mono); fill: var(--muted); }
.history-direct { font: 600 var(--text-2xs)/1 var(--font-mono); fill: var(--ink); }
/* A hit target wider than the mark, and it never paints. */
.history-hit { cursor: crosshair; }
.history-band { stroke: none; }

/* The legend is always present: there is more than one mark, so identity is
 * never left to color alone. */
.history-legend {
  list-style: none;
  margin: 0 0 var(--sp-5);
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4em 1.2em;
  font: 500 var(--text-2xs)/1.4 var(--font-mono);
  color: var(--muted);
}
.history-legend li { display: flex; align-items: center; gap: 0.45em; }
.history-swatch { width: 14px; height: 10px; flex: none; border-radius: 2px; }
.history-swatch-outer { background: var(--chart-2); opacity: 0.18; }
.history-swatch-inner { background: var(--chart-2); opacity: 0.34; }
.history-swatch-line { background: var(--chart-2); height: 2px; }
.history-swatch-gap { background: var(--hatch); border: 1px solid var(--line-strong); }
.history-swatch-bounded { background: var(--measure-fill); width: 4px; }

/* An absence is stated in words as well as drawn, and it is not styled as an
 * error: a lane that did not run is a fact about the record, not a fault on
 * this page. */
.history-gap-note, .history-bound-note, .history-degradation {
  color: var(--muted);
  font-size: var(--text-xs);
  margin: 0 0 var(--sp-2);
}
.history-refusal-line { color: var(--measure-text); font-size: var(--text-xs); }
.history-framing { color: var(--muted); font-size: var(--text-xs); }

.history-refusal { border: 1px solid var(--line-strong); border-radius: 8px; padding: var(--sp-5); }
.history-refusal h2 { margin-top: 0; }

.history-table-wrap { overflow-x: auto; }
.history-table { width: 100%; border-collapse: collapse; font: 500 var(--text-2xs)/1.4 var(--font-mono); }
.history-table th, .history-table td { padding: 4px 10px; text-align: right; border-bottom: 1px solid var(--line); white-space: nowrap; }
.history-table th[scope="row"] { text-align: left; color: var(--muted); font-weight: 500; }
.history-table thead th { text-align: right; color: var(--muted); border-bottom: 1px solid var(--line-strong); }

/* ── THE LADDER'S CLOSINGS PANEL (GROUNDUP_2_DESIGN.md §3 S4 panel 1; T3) ───
 *
 * Fourteen dates on one axis. It WRAPS rather than scrolls, for the reason the
 * status line wraps: an axis that scrolls sideways hides dates a reader cannot
 * know are there. At 390 the cells wrap to two or three rows and every date
 * keeps its own count, which is the §4 collapse for this panel.
 *
 * Figures are tabular so the counts line up column to column. The panel wears
 * ONE chip, in the receipt line under it, because every count in it comes out
 * of one walk with one stamp.
 */
.closings-grid { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); margin: var(--sp-3) 0;
  padding: 0; list-style: none; }
.closings-cell { display: flex; flex-direction: column; gap: 2px; min-width: 4.5rem; }
.closings-date { font-family: var(--font-mono); font-size: var(--text-2xs); letter-spacing: var(--track-label);
  color: var(--muted); }
.closings-figure { font-family: var(--font-mono); font-size: var(--text-sm); font-weight: 600; color: var(--ink);
  font-variant-numeric: tabular-nums slashed-zero lining-nums; }
/* ABSENCE IS A WORD AND IT IS QUIET, exactly as on the status line: a walk that
 * has not answered yet is not a refusal. */
.closings-absent { color: var(--faint); font-family: var(--font-mono); font-size: var(--text-sm); }

/* A retired receipt address, standing where the figure it named is served.
 * The rule is `receipt-view.ts` `receiptAliasHtml`: an empty anchor, no box, no
 * space of its own. */
.receipt-alias { display: block; height: 0; }

/* Status text must remain readable at narrow widths. Tables scroll in their own containers. */
.status-val { white-space: normal; overflow-wrap: anywhere; }
@media (max-width: 48rem) {
  .status-tile { overflow: visible; white-space: normal; }
  .status-tile > * { overflow: visible; text-overflow: clip; }
  .status-hover { z-index: 20; }
}
.market-workflow { padding: var(--sp-3) 0; }
.market-workflow form { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: end; }
.market-workflow details { margin-block: var(--sp-3); }

.status-details > summary, .script-note { padding: var(--sp-3) var(--sp-4); }
