/* The SPA's design tokens: the `:root` custom properties, the Steel ramp
 * from `docs/ui-template/style-guide.md` (`--seq-100` through `--seq-700`
 * over a `--surface-1` ground), the light/dark theme swap, and the base
 * reset every page needs regardless of what widgets it holds. It is not the
 * house palette.
 */

/* ------------------------------------------------------------------------
 * The Steel ramp, in two themes.
 *
 * The values are `docs/ui-template/style-guide.md` §1.1's, with §4's three
 * corrections applied, and the dark ramp is §4.3's, which the guide does not
 * carry because the guide is light-only. That ramp is authored here, not
 * ported: the SPA keeps a dark theme, so somebody had to write it, and
 * `web/tests/contrast.test.js` is the only thing standing behind it.
 *
 * Three blocks. Every token is DEFINED in the light block and the two dark
 * blocks only redefine, because a colour whose sole definition sits inside a
 * media query has no value when the query does not match.
 *
 * The semantic aliases the widget palette reads are `--good`, `--warning`,
 * `--serious`, `--critical` and `--seq-*`. They keep their names and point at
 * the ramp, so a renderer reads the same name whatever the ramp holds.
 */

:root {
  color-scheme: light;

  /* Steel. `steel-a` is the canvas; the numbered ramp runs light to dark. */
  /* `steel-600` is the one step the guide NAMES (§3.1's UNKNOWN figure) and
   * gives no hex for. The prototype uses #697782, which measures 4.33:1 on
   * the canvas and 4.07:1 on the sunken step, both under the guide's own
   * 4.5:1 rule for body text. This ramp takes the nearest step that clears
   * all three surfaces, so muted text is readable wherever it lands and the
   * UNKNOWN figure needs no minimum size to be legible. */
  --steel-a:#F7F8F9; --steel-100:#F0F1F2; --steel-200:#DEE2E4;
  --steel-300:#C7CDD1; --steel-400:#A7AFB6; --steel-500:#85919A;
  --steel-600:#616E78; --steel-700:#5A6166; --steel-800:#434C53;
  --steel-900:#30363B;

  /* Colour carries meaning only: blue action, red failure, orange
   * unmeasured, green clear. `-a` names are washes. */
  --blue-a:#EBF3FB; --blue-100:#D1E4F6; --blue-600:#135FB3;
  --blue-700:#0C4995;
  --red-a:#FFEFEF; --red-600:#C30000; --red-700:#9B0000;
  --orange-a2:#FFF1DC; --orange-100:#FFDCA7; --orange-500:#B35F00;
  --ac-warn:#E09A4B; --ac-logo-ground:#FFFFFF;
  --orange-ink:#612200;
  --green-a2:#E6F6E8; --green-600:#0C6F2E; --green-700:#04572B;
  /* The system palette: the colour palette's ramps only. Light: tint 100, ink 700, marker 600.
   * Red, orange and green are left out, since they are the outcome rings. */
  --sys-indigo-tint:#DEE0F4; --sys-indigo-ink:#353DAB; --sys-indigo-marker:#4854C4;
  --sys-yellow-tint:#FEEB7A; --sys-yellow-ink:#614700; --sys-yellow-marker:#7D5A00;
  --sys-purple-tint:#EEDAF5; --sys-purple-ink:#742591; --sys-purple-marker:#9132B5;
  --sys-pink-tint:#FDD7E9; --sys-pink-ink:#8A1D59; --sys-pink-marker:#AE286C;
  --sys-aqua-tint:#ACECF2; --sys-aqua-ink:#004E7D; --sys-aqua-marker:#00678F;
  --sys-brown-tint:#EBE0CF; --sys-brown-ink:#69430E; --sys-brown-marker:#825722;
  --sys-olive-tint:#E8EDC8; --sys-olive-ink:#4A5410; --sys-olive-marker:#667316;
  --sys-teal-tint:#CCF2EC; --sys-teal-ink:#0F4F4A; --sys-teal-marker:#11706A;
  --sys-blue-tint:#D6E6FA; --sys-blue-ink:#0F3F80; --sys-blue-marker:#1F5FBF;
  /* The system palette default: no ramp row, or more than one
   * system on a producer, reads Steel. */
  --sys-neutral-tint:var(--steel-100); --sys-neutral-ink:var(--steel-700);
  --sys-neutral-marker:#697782;

  /* §4.2. A separator and a card edge are decorative and keep the ramp
   * above; the border that IDENTIFIES a control is a UI component boundary
   * at a 3:1 floor, and `steel-300` measures 1.61:1 on white. Inputs,
   * buttons and popovers take this instead. Worst surface 3.20:1. */
  --control-border:#7D8890;

  /* Surfaces, named for what they are rather than for a step number. */
  --plane:var(--steel-a); --raised:#FFFFFF; --surface-1:var(--steel-100);
  --heat-1:#FBEFC4; --heat-2:#FFDCA7; --heat-3:#F7BE8C; --heat-4:#EE9B93; --heat-5:#B9382F; --heat-ink:var(--steel-900); --heat-5-ink:#FFFFFF;
  --text-primary:var(--steel-900); --text-secondary:var(--steel-700);
  /* §4.1: muted text is `steel-600`, not the guide's `steel-500`, which
   * measures 3.22:1 on white against the guide's own 4.5:1 rule. */
  --muted:var(--steel-600);
  --grid:var(--steel-200); --baseline:var(--steel-300);
  --border:var(--steel-200);
  /* A divider between a detail card's rows is
   * decorative, and needs its own step because the dark ramp has
   * no step between `--raised` and `--border`; using `--border`
   * there would draw the card's own edge colour as every row's
   * separator too. */
  --divider:var(--steel-100);

  /* The sequential ramp the meters and bars read. */
  --seq-100:#D1E4F6; --seq-250:#9CC4EA; --seq-400:#5C97D8;
  --seq-450:var(--blue-600); --seq-550:#12539B; --seq-600:var(--blue-700);
  --seq-700:#08305F;

  --good:var(--green-600); --warning:var(--orange-500);
  --serious:#8F2E00; --critical:var(--red-600);

  /* §1.3: elevation only for overlays. One shadow, not a scale. */
  --shadow:0 4px 12px rgba(0,0,0,.08);

  /* §1.2's type ramp and §1.3's two radii and spacing scale, as tokens, so
   * a later stage cannot invent a third radius without editing this block. */
  --font-sans:Roboto,system-ui,-apple-system,"Segoe UI",sans-serif;
  --font-display:Inter,Roboto,system-ui,sans-serif;
  --font-mono:"Roboto Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
  --fs-eyebrow:10.5px; --fs-mono:11.5px; --fs-caption:11.5px;
  --fs-body:12.5px; --fs-nav:13px; --fs-h2:15px; --fs-h1:28px;
  --radius-chip:2px; --radius:4px;
  /* Chips, badges and status pills take this 20px pill
   * radius. `--radius-chip` (2px) stays: meters, bars and the
   * register id chip still use it. */
  --radius-pill:20px;
  --sp-1:2px; --sp-2:4px; --sp-3:8px; --sp-4:12px; --sp-5:16px;
  --sp-6:20px; --sp-7:28px; --sp-8:32px;

  /* The register editor (the style guide's §3.8). Sizes only, so neither
   * dark block redefines them. A control is 32px; the panel is 400px wide
   * and starts 48px down; a selected row's bar is 3px where the
   * navigation pane's accent is 2px. */
  --control-h:32px; --panel-w:400px; --bar-h:48px; --selected-bar:3px;

  /* The stage funnel. Pure `var()`
   * forwards onto the ramp above, at `:root`, so
   * both dark blocks recolour it with no rule of their own. `--sf-1` to
   * `--sf-5` shade the slices by position through the blue sequence, light
   * to dark in the light theme, nearest the card first in both. The slices
   * are blue. No absence face takes a `--seq-*`
   * token: the hatch is `--plane` over `--surface-1`, the not-assessing
   * fill is `--surface-1`, the vacuous outline is `--raised` with
   * `--control-border`. */
  --sf-1:var(--seq-250); --sf-2:var(--seq-400); --sf-3:var(--seq-450);
  --sf-4:var(--seq-550); --sf-5:var(--seq-700);
  --sf-ink:var(--text-primary); --sf-ink-2:var(--text-secondary);
  --sf-ink-3:var(--muted);
  --sf-divider:var(--divider); --sf-border:var(--border);
  --sf-surface:var(--raised); --sf-stroke:var(--raised);
  --sf-reported-ink:var(--blue-700); --sf-unknown-ink:var(--muted);
  --sf-hatch-bg:var(--plane); --sf-hatch-line:var(--surface-1);
  --sf-na-fill:var(--surface-1);
  --sf-vacuous-fill:var(--raised); --sf-vacuous-edge:var(--control-border);
  --sf-chip-bg:var(--surface-1); --sf-chip-ink:var(--text-primary);
  --sf-btn-border:var(--control-border); --sf-btn-bg:var(--raised);
  --sf-btn-hover:var(--plane); --sf-btn-disabled:var(--steel-400);
  --sf-drop-name:var(--text-secondary); --sf-drop-pct:var(--muted);
}

/* The dark ramp: §4.3. Same blue-grey hue family, lightness order reversed
 * so a surface gets LIGHTER as it rises and "sunken" still reads as sunken.
 * Deriving it by inverting the light values numerically produces the wrong
 * relationship, which is why it is written out. */
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;
    --steel-a:#14171A; --steel-100:#1C2023; --steel-200:#23282C;
    --steel-300:#313840; --steel-400:#3F4750; --steel-500:#68737C;
    --steel-600:#8B96A0; --steel-700:#A7B2BB; --steel-800:#C5CED5;
    --steel-900:#E6EBEF;
    --blue-a:#16293D; --blue-100:#1B3A5C; --blue-600:#6FA8E4;
    --blue-700:#8CBCEC;
    --red-a:#3A1B1B; --red-600:#F1706B; --red-700:#FF8A85;
    --orange-a2:#33240F; --orange-100:#6B4A1E; --orange-500:#E09A4B;
    --ac-warn:var(--orange-500); --ac-logo-ground:#FFFFFF;
    --orange-ink:#F0C38A;
    --green-a2:#14301E; --green-600:#5CC078; --green-700:#7BD293;
    /* The system palette, dark: tint 800, ink 100, marker 400.
     * The neutral takes the colour palette's Steel ramp literally, since this theme's own
     * --steel-* ramp is authored for surfaces, not markers. */
    --sys-indigo-tint:#282983; --sys-indigo-ink:#DEE0F4; --sys-indigo-marker:#828AD7;
    --sys-yellow-tint:#463300; --sys-yellow-ink:#FEEB7A; --sys-yellow-marker:#BE8D00;
    --sys-purple-tint:#55186D; --sys-purple-ink:#EEDAF5; --sys-purple-marker:#BC73D7;
    --sys-pink-tint:#651344; --sys-pink-ink:#FDD7E9; --sys-pink-marker:#F74F9D;
    --sys-aqua-tint:#00366A; --sys-aqua-ink:#ACECF2; --sys-aqua-marker:#009EBA;
    --sys-brown-tint:#4F2F00; --sys-brown-ink:#EBE0CF; --sys-brown-marker:#B38855;
    --sys-olive-tint:#3A4108; --sys-olive-ink:#E8EDC8; --sys-olive-marker:#A3B03A;
    --sys-teal-tint:#0E3D3A; --sys-teal-ink:#CCF2EC; --sys-teal-marker:#3BB8AC;
    --sys-blue-tint:#0D2C5C; --sys-blue-ink:#D6E6FA; --sys-blue-marker:#5B95E6;
    --sys-neutral-tint:#434C53; --sys-neutral-ink:#F0F1F2;
    --sys-neutral-marker:#A7AFB6;
    --control-border:#6E7A83;
    --raised:var(--steel-100); --surface-1:var(--steel-200);
    --heat-1:#3A3216; --heat-2:#4A3518; --heat-3:#5E3A1C; --heat-4:#74302B; --heat-5:#F08A7E; --heat-ink:var(--steel-900); --heat-5-ink:#14171A;
    --divider:var(--steel-200);
    --seq-100:#1B3A5C; --seq-250:#2C5B8C; --seq-400:#4A82BE;
    --seq-450:var(--blue-600); --seq-550:#8CBCEC; --seq-600:#A9CFF1;
    --seq-700:#CBE2F8;
    --serious:#E8703A;
    --shadow:0 4px 12px rgba(0,0,0,.45);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --steel-a:#14171A; --steel-100:#1C2023; --steel-200:#23282C;
  --steel-300:#313840; --steel-400:#3F4750; --steel-500:#68737C;
  --steel-600:#8B96A0; --steel-700:#A7B2BB; --steel-800:#C5CED5;
  --steel-900:#E6EBEF;
  --blue-a:#16293D; --blue-100:#1B3A5C; --blue-600:#6FA8E4;
  --blue-700:#8CBCEC;
  --red-a:#3A1B1B; --red-600:#F1706B; --red-700:#FF8A85;
  --orange-a2:#33240F; --orange-100:#6B4A1E; --orange-500:#E09A4B;
  --ac-warn:var(--orange-500); --ac-logo-ground:#FFFFFF;
  --orange-ink:#F0C38A;
  --green-a2:#14301E; --green-600:#5CC078; --green-700:#7BD293;
  /* The system palette, dark: tint 800, ink 100, marker 400.
   * The neutral takes the colour palette's Steel ramp literally, since this theme's own
   * --steel-* ramp is authored for surfaces, not markers. */
  --sys-indigo-tint:#282983; --sys-indigo-ink:#DEE0F4; --sys-indigo-marker:#828AD7;
  --sys-yellow-tint:#463300; --sys-yellow-ink:#FEEB7A; --sys-yellow-marker:#BE8D00;
  --sys-purple-tint:#55186D; --sys-purple-ink:#EEDAF5; --sys-purple-marker:#BC73D7;
  --sys-pink-tint:#651344; --sys-pink-ink:#FDD7E9; --sys-pink-marker:#F74F9D;
  --sys-aqua-tint:#00366A; --sys-aqua-ink:#ACECF2; --sys-aqua-marker:#009EBA;
  --sys-brown-tint:#4F2F00; --sys-brown-ink:#EBE0CF; --sys-brown-marker:#B38855;
  --sys-olive-tint:#3A4108; --sys-olive-ink:#E8EDC8; --sys-olive-marker:#A3B03A;
  --sys-teal-tint:#0E3D3A; --sys-teal-ink:#CCF2EC; --sys-teal-marker:#3BB8AC;
  --sys-blue-tint:#0D2C5C; --sys-blue-ink:#D6E6FA; --sys-blue-marker:#5B95E6;
  --sys-neutral-tint:#434C53; --sys-neutral-ink:#F0F1F2;
  --sys-neutral-marker:#A7AFB6;
  --control-border:#6E7A83;
  --raised:var(--steel-100); --surface-1:var(--steel-200);
  --heat-1:#3A3216; --heat-2:#4A3518; --heat-3:#5E3A1C; --heat-4:#74302B; --heat-5:#F08A7E; --heat-ink:var(--steel-900); --heat-5-ink:#14171A;
  --divider:var(--steel-200);
  --seq-100:#1B3A5C; --seq-250:#2C5B8C; --seq-400:#4A82BE;
  --seq-450:var(--blue-600); --seq-550:#8CBCEC; --seq-600:#A9CFF1;
  --seq-700:#CBE2F8;
  --serious:#E8703A;
  --shadow:0 4px 12px rgba(0,0,0,.45);
}

* { box-sizing:border-box; }

/* `hidden` HIDES, and it did not. The user agent's `[hidden] { display:
 * none }` is the weakest rule in the cascade, so any author rule naming
 * `display` silently beats it and the attribute becomes decoration. An
 * element toggled that way did not hide:
 *
 *   - `.prov-runset`'s disclosure sets `box.hidden` and moves
 *     `aria-expanded` with it (`web/views.js`), and `display: grid` won.
 *     The button announced the panel collapsed while it stayed on screen,
 *     which is the worse of the two: the accessible name and the rendering
 *     disagreed.
 *
 * `.wb-foot[hidden]` and `.wb-empty[hidden]` carry the same fix class by
 * class, and are redundant with this rule rather than wrong.
 *
 * `!important` is what makes it beat an author `display`, and it costs the
 * print stylesheet nothing. The two rules that deliberately force a hidden
 * element visible for a PDF both carry `!important` AND are more specific,
 * so they still win where they apply. Checked rather than assumed, and
 * `web/tests/hidden-hides.test.js` holds both halves. */
[hidden] { display: none !important; }

body {
  margin:0; background:var(--plane); color:var(--text-primary);
  font:15px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif;
}
.wrap { max-width:1180px; margin:0 auto; padding:32px 24px 72px; }
a { color:var(--seq-450); }
h1 { font-size:26px; margin:0 0 4px; letter-spacing:-.01em; }
/* h2/h3: a section heading is `renderSectionHtml`'s, not a widget's; `.nav`,
 * `h2`/`h3` and `.prov` are the router's and the page renderer's. */
h2 { font-size:18px; margin:40px 0 12px; letter-spacing:-.005em; }
h3 { font-size:15px; margin:24px 0 8px; }
.sub { color:var(--text-secondary); font-size:13px; margin:0 0 24px; }
/* A layout page's intro, under its heading; the sidebar clamps its own. */
.page-intro { color:var(--text-secondary); font-size:14px; line-height:1.5; margin:0 0 24px; max-width:72ch; }
/* A section's assurance-level badge, above its note. */
.sec-level { margin:0 0 8px; }

/* Shell chrome: the provenance banner. Raised whenever this
 * service is not serving the real estate, or did not say what it is serving.
 * `--critical` rather than `--warning`: a figure that is invented is not a
 * degraded version of a real one, it is a different kind of claim, and the
 * palette's most emphatic colour is the honest one for it. Deliberately not
 * `position:fixed`. A banner that floats over content can be scrolled past
 * without ever being read, and this one has to be read once.
 *
 * These are the banner's own properties. Where it SITS is a second rule, in
 * the SHELL anchor beside the regions it sits between, because that is a
 * claim about `#spa-main`'s flex column and reads correctly only next to
 * the column's other sizing rules. */
/* The ink is a TOKEN and not `#fff`, and the reason is the whole of this
 * rule's history. `--critical` is a light red in the light theme and a pale
 * one in the dark, so a fixed white ink gets darker relative to its fill in
 * one theme and lighter in the other. It measured 6.32:1 light and 2.89:1
 * dark, which fails AA and fails even the 3:1 large-text floor, on the one
 * control that says every figure on this page is invented.
 *
 * `--raised` is the panel surface, `#FFFFFF` light and `#1C2023` dark, so it
 * flips with the theme and the ink darkens as the fill lightens: 6.32:1 and
 * 5.68:1. Three other rules over a themed fill already do this
 * (`.signin-btn`, `.wb-split button` and its icon). Anything reaching for a
 * literal white here reintroduces the defect, and
 * `web/tests/contrast.test.js` derives this pair from the stylesheet and
 * holds it to 4.5:1 in both themes. */
.provenance-banner { margin:0 0 20px; padding:10px 14px; border-radius:7px;
  font-size:13px; font-weight:600; color:var(--raised);
  background:var(--critical); border:1px solid var(--critical); }

/* Shell chrome: the router's own nav.
 * Layout only, no new token. It reuses tokens the block above already
 * defines rather than naming its own. */
.spa-nav-list { list-style:none; margin:0; padding:0; display:flex; flex-wrap:wrap;
  gap:6px 14px; }
.spa-nav-list li { margin:0; }
.spa-nav-list a { text-decoration:none; }
.spa-nav-list a[aria-current="page"] { color:var(--text-primary); font-weight:600;
  text-decoration:underline; }
/* One group's heading in the navigation. Small, uppercase and quiet: it is a
   label over a row of links, not a heading in the document's own outline, and
   at the size `h2` renders elsewhere it would compete with the page title
   directly beneath it. `h2` rather than a `div` all the same, because a
   screen reader moving by heading is how a 43-link navigation becomes
   navigable at all, which is the whole point of grouping it. */
.spa-nav-group { font-size:11px; font-weight:600; letter-spacing:.08em;
  text-transform:uppercase; color:var(--text-secondary);
  margin:18px 0 7px; }
.spa-nav-group:first-child { margin-top:0; }

/* ------------------------------------------------------------------------
 * The widget palette. The `:root` block above holds the tokens. What follows
 * is component-level rules only: `.badge`, `.tile`/`.kpis`, `.meter`,
 * `.card`/`.chart-row` (hbar), `.funnel`/`.fn-*` (query_funnel, opaque_derivation), `.tools`/table
 * styling (data_table), `.estate` (the three declared empty states),
 * `.banner`, `.stripbar` (link_bar) and `.xsmark` (cross_surface_mark).
 * `.nav`, `h2`/`h3` and `.prov` are the router's and the page renderer's and
 * are left out of this block.
 */

/* status badge: icon + label, never colour alone */
.badge { display:inline-flex; align-items:center; gap:5px; font-size:11.5px;
  font-weight:600; padding:2px 8px 2px 6px; border-radius:var(--radius-pill);
  border:1px solid var(--border); white-space:nowrap; }
.badge .ic { width:1em; height:1em; vertical-align:-0.15em; }
.badge.critical { color:var(--critical); border-color:var(--critical); }
.badge.serious  { color:var(--serious);  border-color:var(--serious); }
.badge.warning  { color:var(--warning);  border-color:var(--warning); }
.badge.info     { color:var(--text-secondary); }
.badge.good     { color:var(--good); border-color:var(--good); }
.badge.unknown  { color:var(--text-secondary); border-style:dashed;
                  border-color:var(--text-secondary); }
/* vacuous: a count about no subject, its own state beside clear, breach and
 * Info. Solid outline in `--control-border` on a `--raised` fill, so it is
 * neither the dashed UNKNOWN nor the Info pill. Both tokens are in the style
 * guide's ramp and are redefined in each dark block. */
.badge.vacuous  { color:var(--text-secondary); background:var(--raised);
                  border-color:var(--control-border); }
.vac-why        { color:var(--text-secondary); }

/* The verdict line and the why block of a detection row. */
.verdict-line { margin: 0 0 12px; font-size: var(--fs-body); line-height: 1.5; color: var(--text-primary); text-wrap: pretty; }
.ik .verdict-line { margin: 0; padding: 8px 16px 0; font-size: var(--fs-caption); }
a.verdict-id, span.verdict-id { font-family: var(--font-mono); }
.det-row { outline-offset: 2px; }
.det-why { margin-top: 8px; color: var(--text-secondary); font-size: var(--fs-caption); line-height: 1.5; }
.det-why p { margin: 0 0 4px; }
.det-by { color: var(--text-primary); font-weight: 600; }
.det-buckets { list-style: none; margin: 0 0 8px; padding: 0; display: grid; gap: 2px 16px; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
.det-buckets li { display: flex; gap: 8px; align-items: baseline; border-bottom: 1px solid var(--divider); padding: 2px 0; }
.det-bucket-label { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.det-bucket-count { font-variant-numeric: tabular-nums; color: var(--text-primary); }
.det-note { color: var(--muted); }
/* Warning ink is darkened to #8a6100 on light surfaces only. The
 * `--warning` step measures 1.79:1 as ink on `--surface-1`, below the 3:1
 * floor, and hue cannot be the carrier here (§7). The border keeps the
 * token step; only the ink darkens. */
:root[data-theme="light"] .badge.warning, :root:not([data-theme]) .badge.warning,
:root[data-theme="light"] .banner.warning strong,
:root:not([data-theme]) .banner.warning strong {
  color:#8a6100;
}
@media (prefers-color-scheme: light) {
  .badge.warning, .banner.warning strong { color:#8a6100; }
}
:root[data-theme="dark"] .badge.warning,
:root[data-theme="dark"] .banner.warning strong { color:var(--warning); }

/* link strip: link_bar */
.stripbar { display:flex; gap:8px; align-items:center; flex-wrap:wrap;
  margin:0 0 14px; font-size:12px; color:var(--muted); }
.stripbar .lb-label { text-transform:uppercase; letter-spacing:.06em;
  font-size:10.5px; color:var(--muted); margin-right:2px; }
.stripbar a { color:var(--seq-450); text-decoration:none; }
.stripbar a:hover { text-decoration:underline; }
.stripbar .lb-current { color:var(--text-primary); font-weight:600; }
.stripbar .lb-sep { color:var(--baseline); }

/* stat_tile / kpi_row */
.kpis { display:grid; gap:12px; grid-template-columns:repeat(auto-fit,minmax(230px,1fr)); }
.tile { background:var(--raised); border:1px solid var(--border); border-radius:10px;
  padding:16px 18px; box-shadow:var(--shadow); }
.tile .q { font-size:12px; color:var(--muted); text-transform:uppercase;
  letter-spacing:.06em; margin-bottom:10px; }
.tile .v { font-size:44px; line-height:1; font-weight:600; letter-spacing:-.02em; }
.tile .v.unknown { font-size:22px; font-weight:600; }
.tile .cap { font-size:13px; color:var(--text-secondary); margin-top:8px; }
.tile .target { font-size:12px; color:var(--muted); margin-top:2px; }
.tile .excepted { font-size:12px; color:var(--muted); margin-top:2px; font-style:italic; }
.tile .drill { font-size:12px; color:var(--seq-450); margin-top:10px; font-weight:500; }
a.tile { display:block; text-decoration:none; color:inherit; }
a.tile.linked:hover { border-color:var(--seq-450); }
a.tile.linked:hover .drill { text-decoration:underline; }
/* The whole card gets a hover and a focus state, linked or a
 * failure row alike, not only the drill line's own underline. */
a.tile.linked:focus, a.tile.linked:focus-visible {
  border-color:var(--seq-450); outline:2px solid var(--seq-450);
  outline-offset:2px; }
a.tile.failrow.linked:hover, a.tile.failrow.linked:focus,
a.tile.failrow.linked:focus-visible { border-color:var(--seq-450); }
a.tile.failrow.linked:hover .drill, a.tile.failrow.linked:focus .drill {
  text-decoration:underline; }

/* Disclosure: "How this is counted", one per tile and one per section
 * note. `<details>`/`<summary>` rather than a script-driven toggle, so the
 * open state is native and needs no JS to persist or restore. */
.tile .disc, .sec-disc { margin-top:10px; font-size:12px; }
.tile .disc summary, .sec-disc summary { cursor:pointer; color:var(--seq-450);
  font-weight:500; list-style:none; }
.tile .disc summary::-webkit-details-marker,
.sec-disc summary::-webkit-details-marker { display:none; }
.tile .disc summary::before, .sec-disc summary::before {
  content:"▸ "; display:inline-block; }
.tile .disc[open] summary::before, .sec-disc[open] summary::before {
  content:"▾ "; }
.tile .disc p, .sec-disc p { color:var(--text-secondary); margin:6px 0 0; }
.tile .disc .disc-query, .sec-disc .disc-query { color:var(--muted); }

/* meter */
.meter { background:var(--raised); border:1px solid var(--border); border-radius:10px;
  padding:18px; box-shadow:var(--shadow); }
.meter .track { height:14px; border-radius:7px; background:var(--grid);
  overflow:hidden; display:flex; gap:2px; margin:12px 0 10px; }
.meter .seg { height:100%; }
.meter .seg:first-child { border-radius:7px 0 0 7px; }
.meter .seg:last-child { border-radius:0 7px 7px 0; }
.meter .keys { display:flex; gap:18px; flex-wrap:wrap; font-size:12.5px;
  color:var(--text-secondary); }
.meter .key { display:flex; align-items:center; gap:6px; }
.meter .sw { width:10px; height:10px; border-radius:3px; }
/* A zero-count bucket reads "none" in muted text rather than "0 (0%)". */
.meter .key-none { color:var(--muted); }
/* The visible headline that leads with the meter's total; the plain
 * title stays in the DOM (see `meter()`) but is visually-hidden. */
.meter .mt-lead { display:block; }

/* hbar: single-series magnitude, one sequential hue, no legend */
.card { background:var(--raised); border:1px solid var(--border); border-radius:10px;
  padding:18px; box-shadow:var(--shadow); }
.chart-row { display:flex; align-items:center; gap:10px; margin:0 0 7px; font-size:13px; }
.chart-row .lbl { flex:0 0 232px; color:var(--text-secondary); text-align:right;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.chart-row .bar-outer { flex:1; height:18px; position:relative; }
/* `display:block` because `.bar` is a bare `<span>`: an inline element's
 * `width`/`height` do not apply, which is why the bars drew as an empty
 * gutter in real Chrome. */
.chart-row .bar { display:block; height:100%; border-radius:0 4px 4px 0; background:var(--seq-450); }
.chart-row .num { flex:0 0 46px; font-variant-numeric:tabular-nums;
  font-weight:600; font-size:12.5px; }
.chart-row:hover .bar { filter:brightness(1.1); }
a.chart-row { text-decoration:none; color:inherit; }
a.chart-row.linked:hover .lbl { color:var(--seq-450); }
a.chart-row.linked:hover .bar { filter:brightness(1.12); }

/* The assertion register's Instances cell: the typed squares and "k of n"
 * (`.ct-items`, below), then a note counting the stated instances, which
 * are outside n. */
.ins-cell { display:inline-flex; align-items:center; gap:6px; }
.ins-stated { font-size:11.5px; color:var(--muted); font-style:italic; }

/* query_funnel / opaque_derivation: the multi-stage funnel itself renders
 * through .sf-* below. These rules stay for the single-stage and opaque
 * shells, and the exclusion line a funnel column still reads. */
.fn-reason { margin:4px 0 0; font-size:12px; color:var(--muted); }
.fn-exclusion { margin:4px 0 0 0; font-size:12px; color:var(--muted); }
.fn-exclusion a { color:var(--seq-450); text-decoration:none; }
.fn-exclusion a:hover { text-decoration:underline; }
.fn-single { display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
.fn-single .fn-what { margin:0; font-size:13px; color:var(--text-secondary); }
.fn-single .fn-single-value { font-variant-numeric:tabular-nums;
  font-weight:600; font-size:15px; }
.fn-single .fn-reason { margin:0; flex-basis:100%; }
/* the opaque funnel: a row whose derivation is declared, not rendered. It has
 * the same flex shell as .fn-single so it reads as a sibling state, and not as
 * a different component. */
.fn-opaque { display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
.fn-opaque .fn-opaque-label { margin:0; font-size:13px; font-style:italic;
  color:var(--text-secondary); }
.fn-opaque .fn-opaque-value { font-variant-numeric:tabular-nums;
  font-weight:600; font-size:15px; }
.fn-opaque .fn-reason { margin:0; flex-basis:100%; }

/* ---------------------------------------------------------- stage funnel
 *
 * Class-based, no `style` attribute: the
 * visible column count is `data-cols` with one rule per value up to
 * SF_MAX_VISIBLE (`web/palette.js`), the chart is paged by its `viewBox`, and an
 * unknown slice takes its hatch through the SVG `fill` attribute. No hex and
 * no fallback colour: every colour is a `--sf-*` forward declared at
 * `:root`, and `web/tests/contrast.test.js` measures them in both themes. */
.sf {
  --sf-chart-h: 190px;
  font-family: var(--font-sans); color: var(--sf-ink-2);
  background: var(--sf-surface); border: 1px solid var(--sf-border);
  border-radius: var(--radius); padding: 24px 24px 0;
  display: flex; flex-direction: column; gap: 20px;
}
.sf-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.sf-title { margin: 0; font: 300 24px/1.2 var(--font-display); letter-spacing: -.015em; color: var(--sf-ink-2); }
.sf-tools { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.sf-pager { display: flex; align-items: center; gap: 8px; flex: none; }
.sf-page-status { font: 400 12px/1 var(--font-sans); color: var(--sf-ink-3); margin-right: 4px; }
.sf-page-btn {
  width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--sf-btn-border); border-radius: var(--radius);
  background: var(--sf-btn-bg); color: var(--sf-ink-2); cursor: pointer; padding: 0;
}
.sf-page-btn:hover:not(:disabled) { background: var(--sf-btn-hover); }
.sf-page-btn:disabled { color: var(--sf-btn-disabled); cursor: default; }
.sf-wrap-toggle {
  height: 28px; padding: 0 10px; cursor: pointer;
  border: 1px solid var(--sf-btn-border); border-radius: var(--radius);
  background: var(--sf-btn-bg); color: var(--sf-ink); font: 500 12px/1 var(--font-sans);
}
.sf-wrap-toggle:hover { background: var(--sf-btn-hover); }

.sf-body { position: relative; margin: 0 -24px; padding: 0 24px; }
.sf-cols { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(1, minmax(0, 1fr)); }
.sf[data-cols="1"] .sf-cols { grid-template-columns: repeat(1, minmax(0, 1fr)); }
.sf[data-cols="2"] .sf-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.sf[data-cols="3"] .sf-cols { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.sf[data-cols="4"] .sf-cols { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.sf[data-cols="5"] .sf-cols { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.sf[data-cols="6"] .sf-cols { grid-template-columns: repeat(6, minmax(0, 1fr)); }
.sf-col {
  display: flex; flex-direction: column; gap: 14px; min-width: 0;
  padding: 0 14px calc(var(--sf-chart-h) + 10px);
  border-left: 1px solid var(--sf-divider);
}
.sf-top { display: flex; flex-direction: column; gap: 6px; }
.sf-name { font: 500 14px/1.25 var(--font-sans); color: var(--sf-ink-2); }
.sf-count { font: 500 30px/1.1 var(--font-sans); letter-spacing: -.01em; color: var(--sf-ink); overflow-wrap: anywhere; }
.sf-tag { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-height: 22px; }
.sf-reported { font: 500 11px/1 var(--font-sans); letter-spacing: .04em; color: var(--sf-reported-ink); }
.sf-col[data-state="unknown"] .sf-count,
.sf-col[data-state="not-assessing"] .sf-count,
.sf-col[data-state="withheld"] .sf-count,
.sf-col[data-state="vacuous"] .sf-count { color: var(--sf-unknown-ink); font-size: 20px; }

.sf-pred { min-width: 0; display: flex; gap: 6px; align-items: flex-start; }
.sf-col[data-state="root"] .sf-pred { flex-direction: column; gap: 6px; }
.sf-pred .ic { flex: none; margin-top: 2px; color: var(--sf-ink-3); }
.sf-chip {
  display: inline-block; background: var(--sf-chip-bg); color: var(--sf-chip-ink);
  font: 400 12px/1 var(--font-mono); padding: 4px 8px; border-radius: var(--radius);
}
.sf-desc { font: 400 12px/1.4 var(--font-sans); color: var(--sf-ink-3); }
.sf-code { font: 400 11.5px/1.45 var(--font-mono); color: var(--sf-ink-3); min-width: 0; overflow-wrap: anywhere; }
/* The query part of a tile title or a stage heading (`title_stage`,
 * `stage_label`), set as query text so the reader can tell it from the
 * prose around it. The colour is the heading's own, so a tile title
 * keeps its contrast. */
.q-stage {
  font: 400 0.92em/1.45 var(--font-mono);
  white-space: normal; overflow-wrap: anywhere;
}
/* The wrap toggle's truncated state: `wireStageFunnel` sets
 * `data-truncate` on the `.sf` the button belongs to. Predicates wrap in
 * full otherwise. */
.sf[data-truncate] .sf-code {
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.sf-note { margin: 0; font: 400 12px/1.45 var(--font-sans); color: var(--sf-ink-3); text-wrap: pretty; }

.sf-drop { display: flex; flex-direction: column; gap: 3px; }
.sf-drop-name { font: 400 12.5px/1.3 var(--font-sans); color: var(--sf-drop-name); }
.sf-drop-line { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.sf-drop-val { font: 500 15px/1.2 var(--font-sans); color: var(--sf-ink-2); overflow-wrap: anywhere; }
.sf-drop-pct { font: 400 13px/1.2 var(--font-sans); color: var(--sf-drop-pct); }
.sf-drop .fn-exclusion { margin: 4px 0 0; }

.sf-chart { position: absolute; left: 24px; right: 24px; bottom: 0; width: calc(100% - 48px); height: var(--sf-chart-h); display: block; }
.sf-seg { stroke: var(--sf-stroke); stroke-width: 2; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.sf-seg-1 { fill: var(--sf-1); }
.sf-seg-2 { fill: var(--sf-2); }
.sf-seg-3 { fill: var(--sf-3); }
.sf-seg-4 { fill: var(--sf-4); }
.sf-seg-5 { fill: var(--sf-5); }
.sf-seg[data-state="not-assessing"] { fill: var(--sf-na-fill); }
.sf-seg[data-state="vacuous"] { fill: var(--sf-vacuous-fill); stroke: var(--sf-vacuous-edge); }
.sf-hatch-bg { fill: var(--sf-hatch-bg); }
.sf-hatch-line { fill: var(--sf-hatch-line); }

.sf-legend {
  display: flex; flex-wrap: wrap; gap: 16px; margin: 0 -24px; padding: 12px 24px; list-style: none;
  border-top: 1px solid var(--sf-border); font: 400 11.5px/1.4 var(--font-sans); color: var(--sf-ink-2);
}
.sf-legend li { display: inline-flex; align-items: center; gap: 6px; }
.sf-key { width: 14px; height: 10px; border-radius: 2px; border: 1px solid var(--sf-vacuous-edge); }
.sf-key--unknown {
  background: repeating-linear-gradient(135deg, var(--sf-hatch-bg) 0 2px, var(--sf-hatch-line) 2px 5px);
}
.sf-key--not-assessing { background: var(--sf-na-fill); }
.sf-key--withheld { background: none; border-style: dashed; }
.sf-key--vacuous { background: var(--sf-vacuous-fill); }

/* Before `wireStageFunnel` has measured the container (`data-wired` is set
 * by its first layout), the viewport stands in for it, so the first paint
 * does not show six columns at 375. */
@media (max-width: 560px) {
  .sf:not([data-wired])[data-cols="3"] .sf-cols,
  .sf:not([data-wired])[data-cols="4"] .sf-cols,
  .sf:not([data-wired])[data-cols="5"] .sf-cols,
  .sf:not([data-wired])[data-cols="6"] .sf-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sf:not([data-wired]) .sf-col:nth-child(n+3) { display: none; }
}
@media (min-width: 561px) and (max-width: 1000px) {
  .sf:not([data-wired])[data-cols="5"] .sf-cols,
  .sf:not([data-wired])[data-cols="6"] .sf-cols { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .sf:not([data-wired]) .sf-col:nth-child(n+5) { display: none; }
}
@media (max-width: 560px) {
  .sf { padding: 16px 16px 0; }
  .sf-body { margin: 0 -16px; padding: 0 16px; }
  .sf-chart { left: 16px; right: 16px; width: calc(100% - 32px); }
  .sf-legend { margin: 0 -16px; padding: 12px 16px; }
}

/* ---------------------------------------------------------- instance cards
 *
 * The mini-funnel and instance-card stylesheets, each kept as the design team
 * delivered it. The funnel's three state selectors read the keys
 * the code writes. */
/* mini-funnel.css. Every class is mf-. Colours are tokens only: no hex, no fallback.
 * Fills forward to the site's own --sf-1..--sf-5, so both dark blocks theme them.
 * Width comes from the container (floor 240px). Height is fixed. */
.mf-name { margin: 0; font-size: var(--fs-h2); font-weight: 500; line-height: 1.3; color: var(--text-primary); text-wrap: pretty; }
.mf-reason { margin: 0; font-size: var(--fs-caption); line-height: 1.5; color: var(--text-secondary); text-wrap: pretty; }
.mf { position: relative; box-sizing: border-box; min-width: 240px; padding-top: var(--sp-1); }
.mf-steps { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
.mf-svg { position: absolute; left: 0; bottom: 0; z-index: 0; width: 100%; height: 46px; pointer-events: none; }
.mf-svg path { stroke: var(--sf-stroke); stroke-width: 2; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.mf-s0 { fill: var(--sf-1); } .mf-s1 { fill: var(--sf-2); } .mf-s2 { fill: var(--sf-3); } .mf-s3 { fill: var(--sf-4); } .mf-s4 { fill: var(--sf-5); }
.mf-na { fill: var(--sf-na-fill); }
.mf-h-bg { fill: var(--sf-hatch-bg); } .mf-h-ln { fill: var(--sf-hatch-line); }
.mf-step { display: flex; flex-direction: column; align-items: center; gap: var(--sp-1); min-width: 0; outline: none; cursor: default; }
.mf-t, .mf-n { position: relative; z-index: 1; }
.mf-step:focus-visible .mf-t { box-shadow: 0 0 0 3px var(--blue-a); }
.mf-t { display: inline-grid; place-items: center; flex: none; width: 22px; height: 22px; border-radius: var(--radius-pill); border: 1px solid var(--control-border); background: var(--sys-neutral-tint); color: var(--text-primary); font-size: var(--fs-caption); font-weight: 500; line-height: 1; letter-spacing: 0; }
.mf-t[data-type="source"] { background: var(--sys-aqua-tint); }
.mf-t[data-type="scope"] { background: var(--sys-indigo-tint); }
.mf-t[data-type="exclusion"] { background: var(--sys-yellow-tint); }
.mf-t[data-type="exception"] { background: var(--sys-purple-tint); }
.mf-t[data-type="distinct"] { background: var(--sys-pink-tint); }
.mf-n { align-self: center; display: inline-flex; align-items: center; justify-content: center; margin: auto 0 var(--sp-2); max-width: 100%; box-sizing: border-box; padding: var(--sp-1) var(--sp-3); border-radius: var(--radius-pill); background: var(--steel-100); color: var(--text-primary); border: 1px solid var(--steel-300); font-size: var(--fs-caption); font-weight: 500; line-height: 1.1; text-align: center; font-variant-numeric: tabular-nums; }
.mf-steps { grid-template-rows: auto; }
.mf-step { min-height: 70px; }
.mf-n[data-state="u"] { color: var(--muted); }
.mf-n[data-state="n"] { color: var(--text-secondary); }
.mf-n[data-state="w"] { color: var(--text-secondary); }
.mf-tip { display: none; position: absolute; top: 100%; z-index: 5; width: min(300px, 100%); box-sizing: border-box; margin-top: var(--sp-2); padding: var(--sp-4) var(--sp-5); flex-direction: column; gap: var(--sp-3); background: var(--raised); color: var(--text-primary); border: 1px solid var(--control-border); border-radius: var(--radius); box-shadow: var(--shadow); text-align: left; pointer-events: none; }
.mf-tip[data-side="l"] { left: 0; } .mf-tip[data-side="r"] { right: 0; }
.mf-tip[data-side="m"] { left: 50%; translate: -50% 0; }
.mf-step:hover > .mf-tip, .mf-step:focus > .mf-tip { display: flex; }
.mf-step.mf-off > .mf-tip { display: none; }
.mf-tip-h { display: flex; align-items: center; gap: var(--sp-3); font-size: var(--fs-body); }
.mf-tip-l { font-weight: 500; font-size: var(--fs-nav); }
.mf-tip-k { margin-left: auto; font-style: normal; color: var(--text-secondary); }
.mf-tip-n { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-3); font-family: var(--font-display); font-size: 24px; font-weight: 300; letter-spacing: -.02em; line-height: 1.1; }
.mf-pred { font-family: var(--font-mono); font-size: var(--fs-caption); color: var(--text-secondary); background: none; padding: 0; overflow-wrap: anywhere; white-space: normal; line-height: 1.45; }
.mf-tip-m { margin: 0; display: grid; grid-template-columns: 1fr; gap: var(--sp-2); padding-top: var(--sp-3); border-top: 1px solid var(--divider); font-size: var(--fs-body); }
.mf-tip-m > div { display: flex; justify-content: space-between; gap: var(--sp-3); }
.mf-tip-m dt { color: var(--text-secondary); }
.mf-tip-m dd { margin: 0; font-weight: 500; font-variant-numeric: tabular-nums; white-space: nowrap; }
.mf-tip-d { display: flex; justify-content: space-between; gap: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--divider); font-size: var(--fs-body); color: var(--sf-drop-name); }
.mf-tip-d b { font-weight: 500; color: var(--text-primary); white-space: nowrap; }
.mf-tip-d em { font-style: normal; font-weight: 400; color: var(--sf-drop-pct); }
@media print { .mf-tip { display: none; } }
/* instance-cards.css. Every class is ik-. Colours are tokens only: no hex, no fallback.
 * The funnel inside a card is the mini-funnel widget and carries its own css. */
.ik-list { display: grid; gap: var(--sp-6); }
.ik { display: flex; flex-direction: column; background: var(--raised); border: 1px solid var(--border); border-radius: var(--radius); }
.ik-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-6); padding: var(--sp-5) var(--sp-6) var(--sp-4); }
.ik-id { display: flex; flex-direction: column; gap: var(--sp-2); min-width: 0; }
.ik-code { font-family: var(--font-mono); font-size: var(--fs-mono); color: var(--text-secondary); background: none; padding: 0; overflow-wrap: anywhere; }
.ik-head .badge { flex: none; }
.ik-reason { margin: 0; font-size: var(--fs-caption); line-height: 1.5; color: var(--text-secondary); text-wrap: pretty; }
.ik-pad { padding: 0 var(--sp-6) var(--sp-4); }
.ik-facts { margin: var(--sp-5) var(--sp-6) 0; padding: var(--sp-4) 0 var(--sp-5); border-top: 1px solid var(--divider); display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--sp-5) var(--sp-7); }
.ik-facts > div { display: flex; flex-direction: column; gap: var(--sp-2); min-width: 0; }
.ik-facts dt { font-size: var(--fs-eyebrow); font-weight: 500; letter-spacing: .09em; text-transform: uppercase; color: var(--text-secondary); }
.ik-facts dd { margin: 0; font-size: var(--fs-nav); line-height: 1.3; color: var(--text-primary); overflow-wrap: anywhere; }
.ik-facts .ik-pop { font-family: var(--font-display); font-size: 22px; font-weight: 300; letter-spacing: -.02em; line-height: 1.1; font-variant-numeric: tabular-nums; }
.ik-facts .ik-code { white-space: nowrap; overflow-wrap: normal; color: inherit; }
.ik-empty { color: var(--text-secondary); }
.ik-unknown { color: var(--text-secondary); font-size: var(--fs-nav); font-family: var(--font-sans); letter-spacing: 0; font-weight: 500; }
.ik-foot { display: flex; justify-content: space-between; align-items: center; gap: var(--sp-5); padding: var(--sp-3) var(--sp-6); border-top: 1px solid var(--divider); background: var(--plane); border-radius: 0 0 var(--radius) var(--radius); }
.ik-link { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-nav); font-weight: 500; }
.ik-link .ic { width: 1.1em; height: 1.1em; }
.ik-fnwrap { margin: 0 var(--sp-6); }
@media (max-width: 560px) { .ik-facts .ik-code { white-space: normal; overflow-wrap: anywhere; } }
@media print { .ik { break-inside: avoid; } }

/* data_table */
.tools { display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin:0 0 10px; }
.tools input[type=search] { flex:1; min-width:220px; padding:7px 11px; font:inherit;
  font-size:13px; border:1px solid var(--border); border-radius:7px;
  background:var(--surface-1); color:var(--text-primary); }
.tools .count { font-size:12.5px; color:var(--muted); font-variant-numeric:tabular-nums; }
.tools button { padding:7px 11px; font:inherit; font-size:12.5px; cursor:pointer;
  border:1px solid var(--border); border-radius:7px; background:var(--surface-1);
  color:var(--text-primary); }
.tools button:hover { background:var(--grid); }
.tbl-scroll { overflow-x:auto; border:1px solid var(--border); border-radius:10px;
  background:var(--raised); }
/* THE OVERFLOW EDGE. A scroller with no scroll affordance says nothing on
 * screen about a 31-column table that has 27 more columns to the right.
 * `.has-overflow` is toggled from `web/table.js` (never written into
 * markup, so this is a class applied through the CSSOM the way every
 * dynamic state in this file is, not a figure on `data-width`) whenever
 * the box's own `scrollWidth` exceeds its `clientWidth`, and it draws an
 * inset shadow down the right edge for exactly as long as scrolling right
 * would still reveal more. */
.tbl-scroll.has-overflow {
  box-shadow: inset -8px 0 8px -8px rgba(0, 0, 0, .25);
}
table { border-collapse:collapse; width:100%; font-size:13px; }
th, td { text-align:left; padding:8px 12px; border-bottom:1px solid var(--grid);
  vertical-align:top; }
th { position:sticky; top:0; background:var(--raised); font-size:11.5px;
  text-transform:uppercase; letter-spacing:.05em; color:var(--muted);
  cursor:pointer; user-select:none; white-space:nowrap; z-index:1; }
th:hover { color:var(--text-primary); }
th .arrow { opacity:.35; font-size:9px; }
tbody tr:hover { background:var(--surface-1); }
tbody tr:last-child td { border-bottom:none; }
td.num { font-variant-numeric:tabular-nums; }
td.wrap-cell { white-space:normal; min-width:220px; }
/* THE STICKY FIRST COLUMN. A class, never an
 * inline style: CLAUDE.md's rule, and this is a constant rather than a
 * figure, so it belongs here rather than on data-width/data-color-var.
 * The risk is the sticky column over a wrap-cell.
 * A wrap-cell scrolls with the rest of the row while this column stays
 * pinned at the scroller's left edge, and with no background of its own a
 * sticky td is transparent, so the wrap-cell's text shows through from
 * underneath as it scrolls past. The opaque background is the fix; the
 * raised z-index is what keeps it above that content rather than merely
 * even with it. th.col-sticky is the corner cell, sticky on BOTH axes,
 * with top from the th rule above and left from here, so its z-index has to
 * clear a plain sticky-top th as well as a sticky-left td. */
.col-sticky { position:sticky; left:0; z-index:2; background:var(--raised); }
th.col-sticky { z-index:3; }
/* Hover is a fact about the ROW, painted on tbody tr above; this column's
 * own opaque background would otherwise sit on top of that and read as
 * one cell the reader's hover forgot. */
tbody tr:hover .col-sticky { background:var(--surface-1); }
.mono { font-family:var(--font-mono); font-size:11.5px;
  color:var(--text-secondary); }
/* A register id under its title: the title is what a reader reads, and the
 * id stays on a muted mono line for copying and citing. `.titled-id` is a
 * title whose id is in its tooltip, dotted so the tooltip can be found. */
.sub-id { font-family:var(--font-mono); font-size:11px;
  color:var(--muted); background:none; padding:0; }
.titled-id { text-decoration:underline dotted var(--muted);
  text-underline-offset:2px; }
/* a detection's dataset, on its own line under the detection's title */
.ds-line { display:block; color:var(--text-secondary); font-size:var(--fs-caption); }
.fill { color:var(--muted); font-style:italic; }
.empty { padding:28px; text-align:center; color:var(--muted); font-size:13px; }
/* the "Load the rest →" paging control `truncationNote` renders, and the
 * rows view's own full-population export control. They have the same
 * visual treatment and distinct classes, because `wireTablePaging` looks up
 * `.load-more` specifically and must never find the export button instead. */
button.load-more, button.export-full { padding:7px 11px; font:inherit;
  font-size:12.5px; cursor:pointer; border:1px solid var(--border);
  border-radius:7px; background:var(--surface-1); color:var(--seq-450);
  font-weight:500; margin:8px 0 0; }
button.load-more:hover, button.export-full:hover { background:var(--grid); }
button.load-more:disabled, button.export-full:disabled { color:var(--muted);
  cursor:default; }

/* the exception-draft panel, see api/exceptions.py. It composes a row
 * and never writes anything, so nothing here is styled as a destructive or a committing
 * action. `.draft-exception` reuses the same button treatment as
 * `.load-more`/`.export-full` rather than inventing a fourth. */
button.draft-exception { padding:5px 9px; font:inherit; font-size:12px;
  cursor:pointer; border:1px solid var(--border); border-radius:7px;
  background:var(--surface-1); color:var(--seq-450); }
button.draft-exception:hover { background:var(--grid); }
.exception-draft { margin:20px 0 0; padding:16px 18px; border:1px solid var(--border);
  border-radius:10px; background:var(--raised); }
.exception-draft h3 { margin:0 0 4px; }
.exception-draft .ed-picked { font-size:12.5px; color:var(--text-secondary);
  margin:0 0 14px; }
.exception-draft-form { display:flex; flex-direction:column; gap:12px; max-width:520px; }
.exception-draft-form fieldset { border:1px solid var(--border); border-radius:8px;
  padding:10px 12px; margin:0; }
.exception-draft-form legend { font-size:12px; color:var(--text-secondary);
  padding:0 4px; }
.exception-draft-form label.ed-col { display:block; font-size:12.5px;
  font-weight:400; margin:2px 0; }
.exception-draft-form > label { display:flex; flex-direction:column; gap:4px;
  font-size:12.5px; font-weight:600; color:var(--text-secondary); }
.exception-draft-form input[type=text], .exception-draft-form input[type=date] {
  font:inherit; font-size:13px; padding:7px 11px; border:1px solid var(--border);
  border-radius:7px; background:var(--surface-1); color:var(--text-primary); }
.exception-draft-form select { font:inherit; font-size:13px; padding:7px 11px;
  border:1px solid var(--border); border-radius:7px; background:var(--surface-1);
  color:var(--text-primary); }
.exception-draft-form button[type=submit] { align-self:flex-start; padding:8px 14px;
  font:inherit; font-size:13px; font-weight:500; cursor:pointer;
  border:1px solid var(--seq-450); border-radius:7px; background:var(--surface-1);
  color:var(--seq-450); }
.exception-draft-form button[type=submit]:hover { background:var(--grid); }
.exception-draft-form button[type=submit]:disabled { color:var(--muted);
  border-color:var(--border); cursor:default; }
.ed-result:empty { display:none; }
.ed-result { margin:16px 0 0; }
.ed-result.ed-error ul { margin:8px 0 0; padding-left:18px; font-size:12.5px;
  color:var(--critical); }
.ed-result .ed-warning { font-size:12.5px; color:var(--warning); margin:8px 0 0; }
.ed-result pre { margin:10px 0 0; padding:10px 12px; border:1px solid var(--border);
  border-radius:8px; background:var(--surface-1); font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:12px; white-space:pre-wrap; word-break:break-all; }

/* the selection tray's toolbar on a rows view, and the entry form at
 * `#/exception-entry`.
 * Selection lives in `.row-select`, a plain checkbox needing no rule of its
 * own. The toolbar and the form reuse the draft panel's own tokens, since
 * both are the same kind of surface: a form that composes register rows
 * and shows what the server did with them. */
.exception-batch-toolbar { display:flex; align-items:center; flex-wrap:wrap;
  gap:12px; margin:14px 0 10px; }
.exception-batch-toolbar .eb-count { font-size:12.5px; color:var(--text-secondary); }
a.eb-open { font-size:12.5px; font-weight:500; color:var(--seq-450); }
.exception-batch { margin:20px 0 0; padding:16px 18px; border:1px solid var(--border);
  border-radius:10px; background:var(--raised); }
.exception-batch h3 { margin:0 0 4px; }
.exception-entry { margin:12px 0 0; }
.ee-field { display:flex; flex-direction:column; gap:12px; }
.ee-field > label { display:flex; flex-direction:column; gap:4px;
  font-size:12.5px; font-weight:600; color:var(--text-secondary); }
.ee-need { font-weight:400; }
.ee-detection > legend { font-size:13px; font-weight:600; color:var(--text-primary); }
.ee-detection > fieldset { margin:10px 0 0; }
.ee-detection .sub { margin:0 0 6px; font-size:12.5px; }
.ee-rows { margin:0; padding:0; list-style:none; font-size:12.5px; }
.ee-rows li { display:flex; align-items:center; justify-content:space-between;
  gap:10px; padding:4px 0; border-bottom:1px solid var(--border); }
.ee-row { min-width:0; overflow-wrap:anywhere; }
button.ee-remove { flex:none; padding:3px 9px; font:inherit; font-size:12px;
  cursor:pointer; border:1px solid var(--control-border); border-radius:6px;
  background:var(--surface-1); color:var(--text-primary); }
button.ee-remove:hover { background:var(--grid); }
.exception-batch-form { display:flex; flex-direction:column; gap:12px; max-width:520px; }
.exception-batch-form fieldset { border:1px solid var(--border); border-radius:8px;
  padding:10px 12px; margin:0; }
.exception-batch-form legend { font-size:12px; color:var(--text-secondary);
  padding:0 4px; }
.exception-batch-form label.ed-col { display:block; font-size:12.5px;
  font-weight:400; margin:2px 0; }
.exception-batch-form > label { display:flex; flex-direction:column; gap:4px;
  font-size:12.5px; font-weight:600; color:var(--text-secondary); }
.exception-batch-form input[type=text], .exception-batch-form input[type=date] {
  font:inherit; font-size:13px; padding:7px 11px; border:1px solid var(--border);
  border-radius:7px; background:var(--surface-1); color:var(--text-primary); }
.exception-batch-form select { font:inherit; font-size:13px; padding:7px 11px;
  border:1px solid var(--border); border-radius:7px; background:var(--surface-1);
  color:var(--text-primary); }
.exception-batch-form button[type=submit] { align-self:flex-start; padding:8px 14px;
  font:inherit; font-size:13px; font-weight:500; cursor:pointer;
  border:1px solid var(--seq-450); border-radius:7px; background:var(--surface-1);
  color:var(--seq-450); }
.exception-batch-form button[type=submit]:hover { background:var(--grid); }
.exception-batch-form button[type=submit]:disabled { color:var(--muted);
  border-color:var(--border); cursor:default; }
.exc-amend summary { cursor:pointer; font-size:12.5px; color:var(--seq-450); }
.exc-amend-form { display:flex; flex-direction:column; gap:8px; margin:8px 0 0;
  min-width:260px; max-width:420px; }
.exc-amend-form label { display:flex; flex-direction:column; gap:4px;
  font-size:12.5px; font-weight:600; color:var(--text-secondary); }
.exc-amend-form input[type=text], .exc-amend-form input[type=date],
.exc-amend-form textarea { font:inherit; font-size:13px; padding:6px 10px;
  border:1px solid var(--border); border-radius:7px;
  background:var(--surface-1); color:var(--text-primary); }
.exc-amend-form button[type=submit] { align-self:flex-start; padding:6px 12px;
  font:inherit; font-size:12.5px; font-weight:500; cursor:pointer;
  border:1px solid var(--seq-450); border-radius:7px;
  background:var(--surface-1); color:var(--seq-450); }
.exc-amend-form button[type=submit]:disabled { color:var(--muted);
  border-color:var(--border); cursor:default; }
button.exc-history-open { padding:6px 12px; font:inherit; font-size:12.5px;
  font-weight:500; cursor:pointer; border:1px solid var(--seq-450);
  border-radius:7px; background:var(--surface-1); color:var(--seq-450); }
.exc-history-panel:empty { display:none; }
.exc-history-panel { margin:8px 0 0; width:min(46rem, 70vw); }
.exc-history-cond { margin:0 0 8px; font-size:12.5px;
  color:var(--text-secondary); }
.eb-result:empty { display:none; }
.eb-result { margin:16px 0 0; }
.eb-result ul { margin:8px 0 0; padding-left:18px; font-size:12.5px; }
.eb-result.eb-error ul { color:var(--critical); }
button.eb-confirm { margin:12px 0 0; padding:7px 13px; font:inherit;
  font-size:12.5px; font-weight:500; cursor:pointer; border:1px solid var(--seq-450);
  border-radius:7px; background:var(--surface-1); color:var(--seq-450); }
button.eb-confirm:hover { background:var(--grid); }
button.eb-confirm:disabled { color:var(--muted); border-color:var(--border);
  cursor:default; }
.eb-confirm-result:empty { display:none; }
.eb-confirm-result { margin:10px 0 0; }

/* the parameter-propose panel, for the second mutable table. `.propose-parameter` reuses `.draft-exception`'s
 * own button treatment; the panel and result classes reuse the
 * exception-draft/-batch panels' own tokens, the same reason the batch
 * panel reused the draft panel's: this is the same kind of surface, a
 * form composing a register row and showing what the server did with
 * it, gated behind the writer group's claim the same way. */
button.propose-parameter { padding:5px 9px; font:inherit; font-size:12px;
  cursor:pointer; border:1px solid var(--border); border-radius:7px;
  background:var(--surface-1); color:var(--seq-450); }
button.propose-parameter:hover { background:var(--grid); }
.parameter-propose { margin:20px 0 0; padding:16px 18px; border:1px solid var(--border);
  border-radius:10px; background:var(--raised); }
.parameter-propose h3 { margin:0 0 4px; }
.parameter-propose .pp-picked { font-size:12.5px; color:var(--text-secondary);
  margin:0 0 14px; }
.parameter-propose-form { display:flex; flex-direction:column; gap:12px; max-width:520px; }
.parameter-propose-form > label { display:flex; flex-direction:column; gap:4px;
  font-size:12.5px; font-weight:600; color:var(--text-secondary); }
.parameter-propose-form input[type=text] {
  font:inherit; font-size:13px; padding:7px 11px; border:1px solid var(--border);
  border-radius:7px; background:var(--surface-1); color:var(--text-primary); }
.parameter-propose-form button[type=submit] { align-self:flex-start; padding:8px 14px;
  font:inherit; font-size:13px; font-weight:500; cursor:pointer;
  border:1px solid var(--seq-450); border-radius:7px; background:var(--surface-1);
  color:var(--seq-450); }
.parameter-propose-form button[type=submit]:hover { background:var(--grid); }
.parameter-propose-form button[type=submit]:disabled { color:var(--muted);
  border-color:var(--border); cursor:default; }
.pp-result:empty { display:none; }
.pp-result { margin:16px 0 0; }
.pp-result ul { margin:8px 0 0; padding-left:18px; font-size:12.5px; }
.pp-result.pp-error ul { color:var(--critical); }
.pp-result .pp-warning { font-size:12.5px; color:var(--warning); margin:8px 0 0; }
button.pp-confirm { margin:12px 0 0; padding:7px 13px; font:inherit;
  font-size:12.5px; font-weight:500; cursor:pointer; border:1px solid var(--seq-450);
  border-radius:7px; background:var(--surface-1); color:var(--seq-450); }
button.pp-confirm:hover { background:var(--grid); }
button.pp-confirm:disabled { color:var(--muted); border-color:var(--border);
  cursor:default; }
.pp-confirm-result:empty { display:none; }
.pp-confirm-result { margin:10px 0 0; }

/* `#/activity`. It lists open proposals, so a second writer confirms one
 * without typing an id into a standalone form.
 * `.activity-table`/`.activity-overdue-table` reuse the base `table`
 * rule; the confirm control reuses `.eb-confirm`'s/
 * `.pp-confirm`'s own button treatment under a shared class, since it
 * is the same act on either mutable table. */
.activity-filter { margin:14px 0 18px; }
.activity-filter label { display:flex; flex-direction:column; gap:4px;
  font-size:12.5px; font-weight:600; color:var(--text-secondary); max-width:240px; }
.activity-filter select { font:inherit; font-size:13px; padding:7px 11px;
  border:1px solid var(--border); border-radius:7px; background:var(--surface-1);
  color:var(--text-primary); }
.activity-table, .activity-overdue-table { margin:6px 0 0; }
.activity-table th, .activity-overdue-table th { text-align:left;
  font-size:12px; font-weight:600; color:var(--text-secondary);
  padding:6px 10px; border-bottom:1px solid var(--border); }
.activity-table td, .activity-overdue-table td { padding:6px 10px;
  border-bottom:1px solid var(--border); font-size:12.5px; vertical-align:top; }
.activity-row.activity-malformed td { color:var(--critical); }
button.activity-confirm { margin:0 0 0 6px; padding:5px 9px; font:inherit;
  font-size:12px; font-weight:500; cursor:pointer; border:1px solid var(--seq-450);
  border-radius:7px; background:var(--surface-1); color:var(--seq-450); }
button.activity-confirm:hover { background:var(--grid); }
button.activity-confirm:disabled { color:var(--muted); border-color:var(--border);
  cursor:default; }
.activity-confirm-result:empty { display:none; }
.activity-confirm-result { margin:6px 0 0; }
.activity-confirm-result ul { margin:6px 0 0; padding-left:16px; font-size:12px; }

/* declared empty state: which of the three zeros this is.
 * Colour is never the carrier. The badge glyph and the state label are,
 * and the not-collected border is dashed to match `.badge.unknown`. */
/* A grid, so the
 * gap alone spaces a leading badge and the lines under it. Any margin on
 * a direct child would add to the gap, since grid items do not collapse
 * margins, so every paragraph drops to 0. A grid item stretches to its
 * column, so the badge is held at its own width. */
.estate { display:grid; gap:var(--sp-5); padding:var(--sp-4) var(--sp-5);
  border:1px solid var(--border); border-radius:var(--radius);
  background:var(--raised); font-size:13px; }
.estate > p { margin:0; }
.estate > .badge { justify-self:start; }
.estate .es-what { margin:0; color:var(--text-primary); }
.estate .es-why { margin:0; color:var(--text-secondary); font-size:12.5px; }
.estate .es-ds { font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:11.5px; }
.estate.es-not-collected { border-style:dashed; border-color:var(--text-secondary); }
.estate.es-filtered { border-left-width:4px; border-left-color:var(--seq-450); }

/* A badged panel of one or two paragraphs gets a two-column layout,
 * the badge held to column 1 across both rows and centred on them, wherever
 * there is room for it. The neutral panel (no badge, `emptyStateHtml`'s
 * `EMPTY_KIND.NEUTRAL` branch) stays stacked, as today. A panel with a third
 * paragraph also stays stacked: `es-not-collected` and `es-withheld` are
 * usually this shape (built by `renderHbarWidget`'s own not-collected and
 * withheld branches, one fixed paragraph plus one per named class), but
 * either can render with only one named class, at two paragraphs, and then
 * this rule reaches it the same as any other two-paragraph panel.
 *
 * Room is the panel's own width, not the viewport's: a badged panel that
 * lands directly in a `.kpis` grid (the `.kpis` rule above this comment) can
 * be as narrow as 230px even on a wide screen, and the two-column layout
 * leaves it 60-90px for text. No `.tile` ever holds an `.estate` panel;
 * `statTile` always returns its own `.tile`, never the empty-state markup.
 *
 * A container query needs a container, and needs it on the parent, because
 * a container cannot query its own size. `.estate-host` is that container:
 * the palette scripts add it, as a wrapper or as an added class, on the element
 * that directly holds an `.estate` panel, at every site that renders one:
 * meter's and hbar's empty branches, `renderHbarWidget`'s withheld and
 * not-collected branches, the declared-empty widget, a workbench table's
 * empty state, an unmeasured explorer, `renderUnrecognisedWidget`, the
 * rest of `renderUnservableRows`'s and `renderExcludedRows`'s unmeasured
 * branches, and the three register-table pages' own empty states
 * (`scopesPageHtml`, `vocabulariesPageHtml`, `permissionTiersPageHtml`).
 * `.am-section` stays uncontained.
 *
 * The container never sits on a shared utility or attribute selector
 * instead, because those are wider than the panel. A universal
 * `*:has(> .estate)` selector once put `.am-section` into containment:
 * containment is a new formatting context, so it stopped an `<h2>`'s top
 * margin collapsing through the section, an 8px regression the review
 * caught. Putting it on `[data-table]` directly reached the workbench's
 * Confirm button too, which carries the same attribute for an unrelated
 * reason, and containment collapsed that button from 63px to 20px. Putting
 * it on `.mt-12`/`.mt-14` reached every other use of those spacing
 * utilities, including a populated hbar's own wrapper: containment kept the
 * last `.chart-row`'s bottom margin inside the box instead of letting it
 * collapse against the note below, growing several pages. `.estate-host` is
 * never a flex item or `inline-block`, because containment collapses either
 * of those to 0px. The `@container` below reads the named parent's width,
 * so the layout depends on the room the panel actually has rather than on
 * the window it happens to share. */
.estate-host { container-type: inline-size; }
@container (min-width: 480px) {
  .estate:has(> .badge):not(:has(> p:nth-of-type(3))) {
    grid-template-columns: max-content minmax(0,1fr);
    column-gap: var(--sp-5); row-gap: var(--sp-1);
  }
  .estate:has(> .badge):not(:has(> p:nth-of-type(3))) > .badge {
    grid-column: 1; grid-row: 1 / span 2; align-self: center;
  }
  .estate:has(> .badge):not(:has(> p:nth-of-type(3))) > p { grid-column: 2; }
  /* A one-paragraph panel leaves the second row's auto track at its own
   * minimum, 1.67px, plus this rule's 2px `row-gap`: a row and a gap exist,
   * but at a combined 3.67px neither one shows. */
}
/* Below 480px of the panel's own room (the whole rule above unmatched, not
 * just its `grid-template-columns`): the base `.estate` rule's single
 * column and `gap:var(--sp-5)` stand untouched, so a badge stacks 16px
 * above its lines the same width it always has, on a phone or in any
 * narrow parent alike. Item 1 of this round's review found the previous version of this
 * rule leaving its 2px two-column `row-gap` in effect after a viewport
 * media query reset only the columns back to one; a container query has no
 * such seam to leave open, because it is the one place that gap is set. */

/* banner tones. The base rule is neutral on purpose: a tone with no rule of its
 * own must not fall through to a critical-red border. */
.banner { border:1px solid var(--baseline); border-left-width:4px; border-radius:8px;
  padding:12px 16px; margin:0 0 22px; font-size:13.5px; background:var(--raised); }
.banner.critical { border-color:var(--critical); }
.banner.critical strong { color:var(--critical); }
.banner.note { border-color:var(--seq-450); }
.banner.note strong { color:var(--seq-450); }
.banner.info { border-color:var(--baseline); }
.banner.info strong { color:var(--text-secondary); }
.banner.warning { border-color:var(--warning); }
.banner.good { border-color:var(--good); }
.banner.good strong { color:var(--good); }

/* cross_surface_mark / marked_principal / cross_surface_note. Never colour
 * alone: the surface count is the carrier and the colour is reinforcement. */
.xsmark { display:inline-block; margin-left:6px; padding:1px 6px; font-size:11px;
  font-weight:600; white-space:nowrap; border:1px solid var(--baseline);
  border-radius:999px; color:var(--text-secondary); text-decoration:none; }
.xsmark:hover { border-color:var(--text-secondary); text-decoration:none; }
.xsmark.priv { border-color:var(--critical); color:var(--critical); }

/* ------------------------------------------------------------------------
 * The generic page renderer's own chrome. Not a
 * widget: `.am-section` wraps one `report_sections.csv` row's worth of
 * markup (nothing to style beyond spacing; a section is layout, not a
 * component) and `.prov` is the provenance footer.
 */
.am-section { margin:0 0 8px; }
.prov { margin-top:44px; padding-top:16px; border-top:1px solid var(--border);
  color:var(--text-secondary); font-size:12.5px; }
.prov dl { display:grid; grid-template-columns:auto 1fr; gap:2px 14px; margin:8px 0 0; }
.prov dt { color:var(--text-secondary); }
.prov dd { margin:0; font-variant-numeric:tabular-nums; }

/* ------------------------------------------------------------------------
 * The explorer views' own chrome. Everything the four explorers need
 * beyond the widget palette above: a breadcrumb line, a badge strip, the
 * cross-surface panel body (`.xsmark` itself is styled above, and this is
 * the box the explorer views build around it), a tab strip
 * (admin-roles' findings/decisions views), a property list matching
 * `.prov dl`'s own look, a filter toggle, and the row-link cursor
 * `dataTable`'s `rowHref` option renders as `.am-rowlink`.
 */
.crumb { font-size:13px; color:var(--muted); margin:0 0 10px; }
.crumb a { text-decoration:none; }
.crumb a:hover { text-decoration:underline; }
.am-rowlink { cursor:pointer; }
.am-rowlink:hover { background:var(--surface-1); }
/* The row's own anchor, in its
 * first cell. The guide's row identity is a `blue-600` link. Its focus
 * ring is the 3px `blue-a` halo every control on the page uses, drawn
 * inside the cell so the sticky column's edge cannot clip it. */
.am-rowlink-a { color:var(--blue-600); text-decoration:none; border-radius:var(--radius-chip); }
.am-rowlink-a:hover { color:var(--blue-700); text-decoration:underline; }
/* An id such as A-02 is one token; a narrow first column broke it at the
 * hyphen. A name in a plain cell still wraps. */
td.mono .am-rowlink-a { white-space:nowrap; }
.am-rowlink-a:focus-visible { outline:2px solid var(--blue-600); outline-offset:1px;
  box-shadow:0 0 0 3px var(--blue-a); }
tbody tr.am-rowlink:focus-within { background:var(--surface-1); }
tbody tr.am-rowlink:focus-within .col-sticky { background:var(--surface-1); }
/* Visually hidden, still read: a column definition's description, which a
 * header names by `aria-describedby`. */
.vh { position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0; }
/* One line per column that is empty on every row for one reason,
 * above the table and tied to its header. */
.tbl-notes { list-style:none; margin:0 0 8px; padding:0; font-size:var(--fs-caption);
  color:var(--text-secondary); }
.tbl-note + .tbl-note { margin-top:2px; }
/* One badge and its definition per served status/bucket word. */
.tbl-legend-list { list-style:none; margin:0; padding:0; display:flex;
  flex-wrap:wrap; gap:6px 16px; }
.tbl-legend-list li { display:flex; align-items:center; gap:6px; }
.chips { display:flex; gap:6px; flex-wrap:wrap; margin:8px 0 18px; }
.tabs { display:flex; gap:4px; margin:0 0 16px; flex-wrap:wrap; }
.tabs button { padding:6px 12px; font:inherit; font-size:13px; cursor:pointer;
  border:1px solid var(--border); border-radius:7px; background:var(--raised);
  color:var(--text-secondary); }
/* Same correction as `.provenance-banner`, and this site was recorded
 * nowhere: `color:#fff` on `--seq-450` measured 2.50:1 in the dark theme,
 * the worst ink-over-fill pair in the file. `--raised` gives 6.33:1 light
 * and 6.56:1 dark, which is the figure `.signin-btn` and `.wb-split button`
 * already measure on the same fill. The ink is a token because the fill is
 * one; a literal cannot follow a theme. */
.tabs button[aria-selected=true] { background:var(--seq-450);
  color:var(--raised); border-color:var(--seq-450); }
.pol { display:grid; gap:4px 18px; grid-template-columns:auto 1fr; font-size:13px;
  margin:10px 0 0; }
.pol dt { color:var(--text-secondary); }
.pol dd { margin:0; font-variant-numeric:tabular-nums; }
.toggle { display:flex; align-items:center; gap:6px; font-size:13px;
  color:var(--text-secondary); white-space:nowrap; cursor:pointer;
  padding:7px 11px; border:1px solid var(--border); border-radius:8px;
  background:var(--raised); }
.toggle input { margin:0; cursor:pointer; }
.xsbox { border:1px solid var(--border); border-left:3px solid var(--warning);
  border-radius:10px; padding:14px 16px; margin:14px 0 18px;
  background:var(--surface-1); }
.xsbox.priv { border-left-color:var(--critical); }
.xsbox h3 { margin:0 0 8px; font-size:14px; font-weight:600; }
.xsbox ul { margin:0 0 10px; padding-left:20px; }
.xsbox li { margin:3px 0; font-size:13px; }
.xsbox p { margin:6px 0 0; font-size:13px; color:var(--text-secondary); }
.xstier { font-size:11px; font-weight:600; padding:0 5px; border-radius:4px;
  border:1px solid var(--border); margin-left:4px; }
.xstier.t0 { color:var(--critical); border-color:var(--critical); }
.xstier.t1 { color:var(--warning); border-color:var(--warning); }

/* --------------------------------------------------- presentational utilities
 *
 * WHY THESE EXIST, because a utility class in a hand-written stylesheet
 * otherwise reads as a shortcut. `api/web.py` sends
 * `Content-Security-Policy: default-src 'self'` on every response, `style-src`
 * falls back to `default-src`, and an inline `style` attribute therefore needs
 * `'unsafe-inline'` to apply at all. It is not there, so every `style="..."`
 * this application's JavaScript emitted was BLOCKED in a real browser,
 * silently, with the attribute still present in the markup and the declaration
 * never applied. `tools/ui_dev.py probe` finds that.
 *
 * Each class below carries exactly the declaration the attribute it replaced
 * carried, at the same value, and they are named after that value rather than
 * after a role on purpose: two sites with the same visual job and different
 * numbers (`.m-10-0-0` in a meter's trailing note, `.m-12-0-0` in a card's)
 * must not be quietly unified into one, because that would be a rendering
 * change smuggled in under a CSP fix.
 *
 * LAST IN THE FILE, and that is load-bearing. `.sub { margin:0 0 24px }` above
 * is a single-class selector, so is `.m-10-0-0`, and equal specificity is
 * settled by source order. Declared earlier, these would lose every tie they
 * exist to win.
 *
 * The DYNAMIC half of the same defect is a meter segment's width, an hbar or
 * funnel bar's width and a segment's colour. It cannot become a class, because the
 * value is a figure. Those are carried as `data-width`/`data-color-var` and
 * applied through the CSSOM by `AM.applyDynamicStyle`, which CSP does not
 * govern. See `web/palette.js`. */
.fs-14 { font-size:14px; }
.mt-8 { margin-top:8px; }
.mt-10 { margin-top:10px; }
.mt-12 { margin-top:12px; }
.mt-14 { margin-top:14px; }
.mt-18 { margin-top:18px; }
.m-6-0-0 { margin:6px 0 0; }
.m-8-0-0 { margin:8px 0 0; }
.m-10-0-0 { margin:10px 0 0; }
.m-12-0-0 { margin:12px 0 0; }
.pre-wrap { white-space:pre-wrap; }

@media print {
  /* The stage funnel. `wireStageFunnel`'s
   * `beforeprint` handler unhides every column and spans the chart over
   * them, so a printed funnel carries every stage; the paging controls do
   * not print. This sits in the print block that carries
   * `.provenance-banner`, which `web/tests/contrast.test.js` finds by that
   * rule. */
  .sf { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
  .sf-pager, .sf-wrap-toggle { display: none; }
  .sf[data-printing] .sf-cols { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
  .nav, .tools, .exception-draft, .exception-batch,
  .exception-batch-toolbar, .parameter-propose,
  .parameter-confirm-standalone, .re-panel, .re-picker,
  .re-toast { display:none; }
  body { background:#fff; }
  /* The banner is NOT in the hide list above, and this rule is what keeps it
   * readable once printed. Browsers drop background colours by default when
   * printing, which would reduce white-on-red text to white-on-white, and a
   * printed page of invented figures with its warning silently erased is the
   * single worst artefact this whole control exists to prevent. So the print
   * form carries the claim in ink rather than in a background. */
  .provenance-banner { color:var(--critical); background:none;
    border:2px solid var(--critical); }
  .card, .tile, .tbl-scroll, .meter { box-shadow:none; break-inside:avoid; }
  th { position:static; }
}

/* ==========================================================================
 * SECTION ANCHORS. One change writes one anchor, and no other.
 *
 * The anchors exist so that parallel changes can append without colliding.
 * Branches appending to the end of one file conflict every time, while
 * branches writing named regions do not.
 *
 * Add rules INSIDE the anchor of their component. Do not append to the end of
 * the file, and do not reach into another anchor's region to tidy it.
 * ========================================================================== */

/* --- ANCHOR: FONTS ------------------------------------------------------
 * `@font-face` for Inter 300, Roboto 400/500 and Roboto Mono 400, served
 * from `web/fonts/`. No `@import`, no URL: the CSP is `default-src 'self'`
 * and `tools/ci.sh` refuses a URL under `web/`. */
/* Three files for four faces: Google Fonts serves Roboto as a variable font
 * and its 400 and 500 URLs are byte-identical, so both weights declare
 * against one `src`. Latin subsets only. See `web/fonts/PROVENANCE.md` for
 * versions, licences and the command that fetched them.
 *
 * `font-display: swap` deliberately: this tool's figures are read, and a
 * blocked paint that shows nothing is worse than a paint in the fallback
 * face that reflows. The fallback stacks below are metric-adjacent so the
 * reflow is small. */
@font-face {
  font-family: Inter;
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("/fonts/inter-300.woff2") format("woff2");
}
@font-face {
  font-family: Roboto;
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/roboto-400.woff2") format("woff2");
}
@font-face {
  font-family: Roboto;
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/fonts/roboto-400.woff2") format("woff2");
}
@font-face {
  font-family: "Roboto Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/roboto-mono-400.woff2") format("woff2");
}

/* The body family. The prototype inherits this from its bundler and the real
 * shell will not, so it is set here. `--font-sans` is a token of the ramp block above. */
body { font-family: var(--font-sans); }

/* The sprite itself never paints. Its `<symbol>` children do not render on
 * their own, but the `<svg>` holding them is a replaced element and would
 * take layout space at the top of the body. A rule rather than an attribute,
 * because an inline `style` is what the CSP blocks. */
#ic-sprite { display: none; }

/* One icon. Sized in `em` off the parent font-size per §1.4, so an icon in
 * 13px chrome and one in a 20px rail are both proportionate without either
 * naming a pixel size. */
.ic {
  width: 1.15em; height: 1.15em;
  flex: none; display: inline-block; vertical-align: -0.18em;
  /* THE STROKE PROPERTIES LIVE HERE, NOT ON THE SPRITE, and that is not a
   * preference. `<use>` clones the referenced `<symbol>` into a shadow
   * tree whose inherited properties come from the `<use>` element's own
   * context, not from the sprite's root `<svg>`. So `fill="none"
   * stroke="currentColor"` written on the sprite never reaches the copy:
   * the paths fall back to `fill: black; stroke: none` and every icon
   * renders as a filled blob. Measured that way in Chrome before this rule
   * was written.
   *
   * `currentColor` is what lets an icon take the colour of the text it
   * sits beside; 1.75 is §1.4's stroke width. */
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* --- END: FONTS --------------------------------------------------------- */

/* --- ANCHOR: SHELL ------------------------------------------------------
 * The 288px grid, the three regions, and the one tall scroller. */
/* docs/ui-template/style-guide.md §2. Three sizing rules make this work and
 * each one has a symptom when it is missing, so they are named:
 *
 *   1. The content region is the ONLY tall scroller. `min-height:0` is what
 *      lets a flex child shrink below its content; without it the region
 *      refuses to shrink, the page grows past the viewport, and the sidebar
 *      scrolls away with it.
 *   2. The provenance strip is one line in the scroller's own flow. The
 *      full panel is a slide-over with its own scroller, so it never
 *      squeezes the content region.
 *   3. The sidebar has its own scroller, independent of the content.
 *
 * `min-height:620px` alongside `100vh` is the guide's own, and it is load
 * bearing rather than decorative: a 100vh grid with no floor collapses every
 * region on a short viewport and the symptom reads as a rendering bug. */
#app {
  display: grid;
  grid-template-columns: 288px minmax(0, 1fr);
  height: 100vh;
  min-height: 620px;
  font-size: var(--fs-nav);
  color: var(--text-primary);
  background: var(--plane);
}

#spa-sidebar {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow-y: auto;
  background: var(--plane);
}

#spa-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}

/* Not a fourth sizing rule, and deliberately not added to the three named
 * above: those three are the grid's, and `web/index.html` counts them. This
 * one is here because the provenance banner is now one of this column's
 * children, so it has to answer to the same `flex:1` content region they
 * do. `paintProvenanceBanner` used to insert it into `#app`,
 * which is a two-column grid, so the banner was a grid item: measured at
 * 1440x900 it took cell 1/1 at 288px wide, pushed `#spa-sidebar` into the
 * second column and wrapped `#spa-main` onto a second row. It goes into
 * `#spa-main` above `#spa-content` instead, and that function's comment
 * carries the rest of the reasoning.
 *
 * Edge to edge and square, because in a padded content column a rounded
 * card with a margin reads as one more panel on the page, and this one has
 * to read as the frame making a claim about everything inside it. Its left
 * padding is `--sp-6`, which is `#spa-content`'s, so the text starts on the
 * same line as the `h1` below it.
 *
 * `flex:none` for the same reason rules 1 to 3 exist: `#spa-content` is
 * `flex:1`, and a warning that shrank away on a short viewport would be a
 * control that disappears exactly when the page is hardest to read.
 *
 * The border is left alone deliberately. The print block replaces it with
 * ink so the claim survives a printer dropping backgrounds, and this
 * selector is more specific than that one, so anything said about the
 * border here would win on paper too. */
#spa-main > .provenance-banner {
  flex: none;
  margin: 0;
  border-radius: 0;
  padding: 10px var(--sp-6);
}

/* The stale-estate marker (`paintRefreshMarker` in `web/app.js`) sits in the
 * same column for the same reasons, so it takes the same four properties.
 * Its wash, border and ink are `.banner.warning`'s, the §3.4 treatment,
 * because a served estate older than the bucket is a narrowed view of real
 * evidence and not a different kind of claim. Square and edge to edge, the
 * 1px box drops its sides so two stacked banners read as one frame. */
#spa-main > .refresh-marker {
  flex: none;
  margin: 0;
  border-radius: 0;
  border-width: 0 0 1px;
  padding: 10px var(--sp-6);
}
.refresh-marker .bn-body p { margin: 0; }
.refresh-marker .bn-body p + p { margin-top: 2px; }
.refresh-marker .rm-lead {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.refresh-marker:has(details[open]) .rm-lead { white-space: normal; }
.refresh-marker .bn-body { min-width: 0; }
.refresh-marker summary { cursor: pointer; }
.refresh-marker details[open] summary { margin-bottom: 2px; }

/* Rule 1. The scroller is `#spa-scroll`, not `#spa-content`. It holds the
 * content and then the provenance line, so the line is the last thing in
 * the page's own flow and nothing is pinned. */
#spa-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  /* A table's header is carried down over its rows as this region
   * scrolls (`refreshStickyHeads`), so a row brought into view by the
   * keyboard stops below the header's height rather than under it. */
  scroll-padding-top: 48px;
}
#spa-content {
  padding: 22px var(--sp-6) var(--sp-7);
}

/* Rule 2. In flow, after the content, with nothing capped: the full
 * panel is a slide-over (the PROVENANCE anchor), so this region is only
 * ever as tall as its one line. */
#spa-provenance {
  background: var(--raised);
  border-top: 1px solid var(--border);
}

/* `.wrap` was the old shell: one centred column with a max-width, which the
 * grid replaces. Kept, because `renderHome` and the 404 view still use it as
 * a content-width wrapper INSIDE the content region, where a max-width is
 * still what a paragraph of prose wants. The margin and the tall bottom
 * padding go: the region supplies both now. */
.wrap { max-width: 1180px; margin: 0; padding: 0; }

/* Shell chrome: the sign-in screen's one button. It has a class of its
 * own, with its own focus ring, because every focus style in this file is
 * class-scoped and the one interactive control on the service's front door
 * would otherwise fall back to the user agent's own.
 *
 * Filled rather than bordered, because it is the only action on an
 * otherwise empty screen. `--raised` text on a `--blue-600` fill is the
 * treatment `.wb-split button` already gives the house's other filled
 * button, and it inverts correctly in the dark theme where `--raised` is
 * the dark step. The 30px toolbar box is the wrong size for a front door,
 * so this one is 36px with a wider gutter and the nav type size. */
.signin-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  height: 40px;
  padding: 0 18px;
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  color: var(--raised);
  background: var(--blue-600);
  border: 1px solid var(--blue-600);
  border-radius: var(--radius);
}
.signin-btn:hover {
  background: var(--blue-700);
  border-color: var(--blue-700);
}
/* The house focus ring is a 3px `--blue-a` halo, and
 * `--blue-a` is a wash that reads against neither a `--blue-600` fill nor
 * the plane behind it. `.wb-split button` is the other filled button and
 * rings in `--blue-700`, which clears both surfaces in both themes. Outset
 * where `.wb-split` insets, because that ring is drawn inside a shared
 * border and this button stands alone. */
.signin-btn:focus-visible {
  outline: 2px solid var(--blue-700);
  outline-offset: 2px;
}

/* The sign-in screen is a CLEARED `#app` holding one `.signin-shell`, so the
 * shell's own 288px/1fr grid is switched off for the one screen that has no
 * navigation pane or provenance strip to lay out. The mock-up at
 * `docs/ui-template/carl-sign-in.html` is rebuilt as classes because the
 * CSP blocks the mock-up's own inline `style=`.
 *
 * `> ` is load bearing. It matches only a shell that is a CHILD of `#app`,
 * which is what `renderSignIn` appends today. A later change that paints
 * the sign-in screen inside `#spa-content` keeps the four regions and needs
 * the grid, and this rule stops matching rather than flattening it. */
#app:has(> .signin-shell) {
  display: block;
}

/* `.signin-shell` is `body` in the mock-up: the full-height column that
 * centers the panel in `main` and pins the footer below it. `#app` already
 * gives the SPA its background and text colour (this file's base rules), so
 * this class repeats neither. */
.signin-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
.signin-main {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-6);
}
.signin-panel {
  width: 100%;
  max-width: 400px;
  background: var(--raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 32px;
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
}
/* `h1` and `.sub` are the base rules every other page already uses for its
 * heading and its secondary paragraph (this file's CONTENT TYPE section);
 * `.signin-intro` only stacks the two, at the mock-up's 8px gap. */
.signin-intro { display: flex; flex-direction: column; gap: 8px; }
.signin-intro h1 { margin: 0; }
.signin-intro .sub { margin: 0; }
.signin-note {
  border-top: 1px solid var(--surface-1);
  padding-top: 16px;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--text-secondary);
}
/* The Microsoft four-square mark. Its four colours are the vendor's own
 * brand hex from Microsoft's published sign-in mark. They are the one
 * exception to writing every mockup colour as a Steel token. */
.signin-ms {
  display: grid;
  grid-template-columns: 7px 7px;
  gap: 1px;
  flex: none;
}
.signin-ms span { width: 7px; height: 7px; }
.signin-ms-1 { background: #F25022; }
.signin-ms-2 { background: #7FBA00; }
.signin-ms-3 { background: #00A4EF; }
.signin-ms-4 { background: #FFB900; }
/* The one Steel token the mock-up's footer text names, `--steel-500`,
 * written directly because nothing in the semantic alias list points at
 * that step and the mock-up's own hex (#85919A) is exactly it. */
.signin-footer {
  padding: 16px var(--sp-6);
  font-size: 11.5px;
  color: var(--steel-500);
}

/* THE BREAKPOINT. Below 1000px the fixed 288px sidebar would leave the
 * content clipped off-screen with no way to reach it.
 * `#app` drops to one column and `#spa-sidebar` becomes an overlay,
 * hidden until `web/app.js` adds `.sidebar-open` to `#app`. The control
 * that opens it is the navigation pane's own opener, `.nv-open`. Above
 * 1000px the class is inert: the grid rule two columns up already gives
 * the sidebar its own track, and nothing below this point applies.
 *
 * `position: fixed` rather than `absolute`, so the overlay's own height
 * is the viewport's and not `#app`'s current scroll extent, and
 * `transform: translateX` rather than `left`, so the closed state costs
 * no layout and the open transition is one compositor-only property. */
@media (max-width: 1000px) {
  #app {
    grid-template-columns: minmax(0, 1fr);
  }
  #spa-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 40;
    width: 288px;
    max-width: 85vw;
    height: 100vh;
    box-shadow: var(--shadow);
    transform: translateX(-100%);
    transition: transform .15s ease;
  }
  #app.sidebar-open #spa-sidebar {
    transform: translateX(0);
  }
  /* The scrim: what tells a reader the rest of the page is inert while
   * the overlay is open, and `web/app.js`'s outside-click close treats a
   * click on it exactly like a click on the toggle. A literal colour,
   * not a token. `--shadow` two rules up carries the same kind of
   * literal for the same reason: an overlay's own shading is never a
   * themed surface a reader looks AT. */
  #app.sidebar-open::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 39;
    background: rgba(15, 23, 42, .4);
  }
}
/* The pane's opener is fixed at the top left, so on a screen the content
 * starts below its strip. Print hides the opener and takes no strip. */
@media screen and (max-width: 1000px) {
  #spa-main {
    padding-top: 64px;
  }
}
/* --- END: SHELL --------------------------------------------------------- */

/* --- ANCHOR: SIDEBAR ---------------------------------------------------- */
/* The navigation pane, `AM.wireNavPane` in `web/register-editors.js`, drawn into
 * `#spa-sidebar`. Its rules read the site's tokens and every class is
 * `nv-`. The pane draws its own border and radius, so the host draws none.
 * It folds to a 60px rail, and the shell's grid column follows it through
 * `#app.nav-rail`. Above the SHELL anchor's 1000px breakpoint only: below it
 * the pane is a drawer and the grid is one column, whatever mode the pane
 * was folded to before the window narrowed. */
@media (min-width: 1001px) {
  #app.nav-rail { grid-template-columns: 60px minmax(0, 1fr); }
}

/* `.brand`: the mark plus `.sb-titles`, built by `brandBlock()` in
 * `web/app.js` for the sign-in screen. This is its only layout rule, so
 * `.signin-panel` can place it without repeating the gap. */
.brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
.sb-mark { width: 28px; height: 28px; object-fit: contain; flex: none; }
.sb-titles { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.sb-name {
  font: 500 14px/1.2 var(--font-sans);
  color: var(--text-primary);
}
.sb-eyebrow {
  font: 500 var(--fs-eyebrow)/1 var(--font-sans);
  letter-spacing: .09em;
  color: var(--text-secondary);
}

.nv { position: relative; display: flex; flex-direction: column; box-sizing: border-box; width: 288px; height: 100%; padding: 14px 10px 10px; background: var(--raised); color: var(--text-primary); border: 1px solid var(--border); border-radius: var(--radius); font-size: var(--fs-nav); }
.nv[data-mode="rail"] { width: 60px; padding-left: 8px; padding-right: 8px; }
.nv-wrap { display: contents; }
.nv button, .nv input { font: inherit; color: inherit; }
.nv .ic { width: 18px; height: 18px; flex: none; }
.nv-head { display: flex; align-items: center; gap: 10px; padding: 2px 6px 12px; min-height: 36px; }
.nv-mark { width: 30px; height: 30px; flex: none; border-radius: var(--radius); }
.nv-name { display: flex; flex-direction: column; min-width: 0; line-height: 1.2; }
.nv-name b { font-size: var(--fs-h2); font-weight: 500; letter-spacing: .02em; }
.nv-name span { font-size: var(--fs-eyebrow); letter-spacing: .09em; text-transform: uppercase; color: var(--muted); }
.nv-fold { position: absolute; top: 20px; right: 10px; z-index: 5; width: 22px; height: 22px; display: grid; place-items: center; padding: 0; border: 1px solid var(--control-border); border-radius: var(--radius-pill); background: var(--raised); color: var(--text-secondary); cursor: pointer; transition: color .12s ease-out, background .12s ease-out; }
.nv-fold:hover { background: var(--surface-1); color: var(--text-primary); }
.nv-fold .ic { width: 12px; height: 12px; }
.nv[data-mode="rail"] .nv-fold { position: relative; top: auto; right: auto; align-self: center; margin: -4px 0 8px; }
.nv[data-mode="rail"] .nv-fold .ic { transform: rotate(180deg); }
.nv:not([data-drawer]) .nv-head { padding-right: 36px; }
.nv[data-mode="rail"] .nv-head { padding-right: 3px; }
.nv-close { position: absolute; top: 14px; right: 10px; display: grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 0; border-radius: var(--radius); background: none; color: var(--text-secondary); cursor: pointer; }
.nv-close:hover { background: var(--plane); color: var(--text-primary); }
.nv-crumbs { flex: none; margin: 0 2px 8px; padding: 0 6px; }
.nv-trail { margin: 0; padding: 0; list-style: none; font-size: var(--fs-caption); line-height: 1.5; color: var(--muted); }
.nv-trail li { display: inline; }
.nv-trail li + li::before { content: "/"; margin: 0 5px; }
.nv-trail a { color: var(--text-secondary); text-decoration: none; }
.nv-trail a:hover { color: var(--text-primary); text-decoration: underline; }
.nv-trail [aria-current="page"] { color: var(--text-primary); }
.nv-search { display: flex; align-items: center; gap: 8px; height: 34px; margin: 0 2px 6px; padding: 0 8px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--plane); color: var(--muted); cursor: text; }
.nv-search:focus-within { border-color: var(--blue-600); box-shadow: 0 0 0 3px var(--blue-a); }
.nv-search input { flex: 1; min-width: 0; border: 0; outline: 0; background: none; color: var(--text-primary); }
.nv-search input::placeholder { color: var(--muted); }
.nv-kbd { padding: 1px 6px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--raised); font-family: var(--font-mono); font-size: var(--fs-caption); color: var(--muted); }
.nv button.nv-search { width: calc(100% - 4px); text-align: left; cursor: text; }
.nv[data-mode="rail"] .nv-search { justify-content: flex-start; padding: 0 12px; cursor: pointer; }
.nv-tree { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.nv-scroll { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; margin: 0 -4px; padding: 0 4px; scrollbar-width: thin; }
.nv-eyebrow { margin: 14px 0 4px; padding: 0 8px; font-size: var(--fs-eyebrow); font-weight: 500; letter-spacing: .09em; text-transform: uppercase; color: var(--muted); }
.nv[data-mode="rail"] .nv-eyebrow { height: 1px; margin: 10px 8px; padding: 0; background: var(--border); font-size: 0; }
.nv-gh { margin: 0; font: inherit; }
.nv-grp { position: relative; }
.nv-row { display: flex; align-items: center; gap: 10px; width: 100%; box-sizing: border-box; height: 34px; margin: 1px 0; padding: 0 8px; border: 0; border-radius: var(--radius); background: none; color: var(--text-secondary); text-align: left; text-decoration: none; cursor: pointer; transition: background .12s ease-out, color .12s ease-out; }
.nv-row:hover { background: var(--plane); color: var(--text-primary); }
.nv-row:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--blue-a); }
.nv-row.on { background: var(--blue-100); color: var(--blue-700); font-weight: 500; box-shadow: inset 2px 0 0 var(--blue-600); }
.nv-row.on:focus-visible { box-shadow: inset 2px 0 0 var(--blue-600), 0 0 0 3px var(--blue-a); }
.nv-row.has-on:not(.open) { color: var(--text-primary); }
.nv-row.nv-has-desc { height: auto; min-height: 34px; padding-top: 6px; padding-bottom: 6px; align-items: flex-start; }
.nv-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.nv-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nv-desc, .nv-sub { font-size: var(--fs-caption); font-weight: 400; color: var(--muted); }
.nv-desc { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin-top: 2px; white-space: normal; }
.nv-sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nv-row.nv-hit { height: auto; min-height: 34px; padding-top: 5px; padding-bottom: 5px; }
.nv-row.nv-msg { height: auto; min-height: 34px; padding-top: 5px; padding-bottom: 5px; }
.nv-row.nv-msg .nv-label { white-space: normal; }
.nv-ltr { display: grid; place-items: center; width: 18px; height: 18px; flex: none; border: 1px solid var(--border); border-radius: var(--radius); font-size: var(--fs-caption); font-weight: 500; line-height: 1; }
.nv-open { position: fixed; top: 10px; left: 10px; z-index: 30; display: grid; place-items: center; width: 44px; height: 44px; padding: 0; border: 1px solid var(--control-border); border-radius: var(--radius); background: var(--raised); color: var(--text-primary); box-shadow: var(--shadow); cursor: pointer; }
.nv-open:hover { background: var(--plane); }
.nv-open:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--blue-a); }
.nv-open[hidden] { display: none; }
.nv-open .ic { width: 20px; height: 20px; }
.nv-n { font-size: var(--fs-caption); font-variant-numeric: tabular-nums; color: var(--muted); }
.nv-chev { display: grid; color: var(--muted); }
.nv-chev .ic { width: 14px; height: 14px; }
.nv-pg { display: flex; align-items: center; gap: 2px; }
.nv-pg .nv-pl { flex: 1; min-width: 0; }
.nv-tgl { flex: none; display: grid; place-items: center; width: 30px; height: 30px; padding: 0; border: 0; border-radius: var(--radius); background: none; color: var(--muted); cursor: pointer; transition: background .12s ease-out, color .12s ease-out; }
.nv-tgl:hover { background: var(--plane); color: var(--text-primary); }
.nv-tgl:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--blue-a); }
.nv-kids { position: relative; margin: 0 0 4px 17px; padding-left: 10px; border-left: 1px solid var(--border); }
.nv-kids .nv-kids { margin-left: 8px; padding-left: 8px; }
.nv-kids .nv-row { height: 30px; padding-left: 10px; font-size: var(--fs-body); }
.nv-kids .nv-row.nv-has-desc { height: auto; min-height: 30px; }
.nv-kids[hidden] { display: none; }
.nv-count, .nv-none { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 8px 0 4px; padding: 0 8px; font-size: var(--fs-caption); color: var(--muted); }
.nv-none { flex-direction: column; align-items: flex-start; padding: 12px 8px; font-size: var(--fs-body); }
.nv-none p { margin: 0; }
.nv-clear { padding: 0; border: 0; background: none; color: var(--blue-600); font-size: var(--fs-caption); text-decoration: underline; cursor: pointer; }
.nv-clear:hover { color: var(--blue-700); }
.nv-foot { display: flex; flex-direction: column; align-items: stretch; margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border); }
.nv-foot > .nv-row { flex: none; margin: 1px 0; }
.nv-foot-row { display: flex; align-items: center; gap: 4px; }
.nv-foot-row .nv-row { flex: 1; margin: 1px 0; }
.nv-foot-row .nv-row.sq { flex: none; width: 34px; padding: 0; justify-content: center; }
.nv-foot-row .nv-row.sq .nv-txt { display: none; }
.nv[data-mode="rail"] .nv-foot-row { flex-direction: column; align-items: stretch; }
.nv[data-mode="rail"] .nv-foot-row .nv-row, .nv[data-mode="rail"] .nv-foot-row .nv-row.sq { flex: none; width: 100%; }
/* rail: rows keep their names for assistive technology and hide them by clipping, never by display */
.nv[data-mode="rail"] .nv-head { padding: 2px 3px 12px; }
.nv[data-mode="rail"] .nv-name, .nv[data-mode="rail"] .nv-n, .nv[data-mode="rail"] .nv-chev, .nv[data-mode="rail"] .nv-kbd, .nv[data-mode="rail"] .nv-search input, .nv[data-mode="rail"] .nv-crumbs { display: none; }
.nv[data-mode="rail"] .nv-row { justify-content: flex-start; padding: 0 12px; }
.nv[data-mode="rail"] .nv-fly .nv-row { padding: 0 8px; }
/* The flyout and the tips are position: fixed, so the scroller cannot clip them. Across, they sit at their static place and the margins carry the
   pane's own geometry: the 60px rail has a 1px border and 8px padding, so its edge is 59px from a row's left. Down, a fixed box with no offset
   takes the place it would have in the rail before the rail scrolled, so where the browser anchors one box to another each is placed from its
   own row instead: the open group's button for the flyout, the tip's own row for a tip. The flyout is held inside the viewport and scrolls
   when it is taller. */
.nv-fly { position: fixed; z-index: 20; margin-top: -43px; margin-left: 59px; min-width: 210px; padding: 6px; background: var(--raised); border: 1px solid var(--control-border); border-radius: var(--radius); box-shadow: var(--shadow); }
.nv-fly .nv-row { height: 30px; font-size: var(--fs-body); }
.nv-fly .nv-d1 { padding-left: 16px; }
.nv-fly .nv-d2 { padding-left: 24px; }
.nv-fly .nv-d3, .nv-fly .nv-d4 { padding-left: 32px; }
.nv-fly-t { margin: 2px 0 4px; padding: 4px 8px 6px; border-bottom: 1px solid var(--border); font-size: var(--fs-eyebrow); font-weight: 500; letter-spacing: .09em; text-transform: uppercase; color: var(--muted); }
.nv-fly-sub { margin: 6px 0 2px; padding: 0 8px; font-size: var(--fs-eyebrow); letter-spacing: .09em; text-transform: uppercase; color: var(--muted); }
.nv-tip { display: none; }
.nv[data-mode="rail"] .nv-tip { display: block; position: fixed; margin-left: 42px; z-index: 25; padding: 5px 10px; border: 1px solid var(--control-border); border-radius: var(--radius); background: var(--raised); color: var(--text-primary); box-shadow: var(--shadow); font-size: var(--fs-body); font-weight: 400; white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity .12s ease-out; }
.nv[data-mode="rail"] :hover > .nv-tip, .nv[data-mode="rail"] :focus-visible > .nv-tip { opacity: 1; }
.nv:has(.nv-fly) .nv-tip { display: none; }
@supports (anchor-name: --nv-a) {
  .nv[data-mode="rail"] .nv-grp > [aria-expanded="true"] { anchor-name: --nv-fly; }
  .nv-fly { position-anchor: --nv-fly; top: anchor(top); margin-top: -8px; max-height: calc(100vh - 16px); overflow-y: auto; position-try-fallbacks: --nv-fly-up; }
  .nv[data-mode="rail"] .nv-row { anchor-name: --nv-row; anchor-scope: --nv-row; }
  .nv[data-mode="rail"] .nv-tip { position-anchor: --nv-row; top: anchor(center); translate: 0 -50%; }
}
@position-try --nv-fly-up { top: auto; bottom: 8px; margin-top: 0; }
@media print { .nv { display: none; } }
/* --- END: SIDEBAR ------------------------------------------------------- */

/* --- ANCHOR: CONTENT ------------------------------------------------------
 * The page body: figure tiles and their grid, failure rows, the coverage
 * panel, banners, and the two card shells the meters, bars and funnels sit
 * in.
 * `docs/ui-template/style-guide.md` §3.1 to §3.4.
 *
 * EVERY RULE HERE OVERRIDES ONE ABOVE RATHER THAN REPLACING IT. The widget
 * palette's own block is left where it is: it holds what the static pages
 * looked like, and rewriting it in place would make the diff between the
 * two designs unreadable. Same specificity, later in the file, so these
 * win. That is also why nothing here needs an `!important`.
 *
 * NO FIGURE IS A CLASS. Every width on this page still rides on `data-width`
 * and is applied through the CSSOM by `AM.applyDynamicStyle`. Quantising a
 * width into a class would be rounding a number to make it fit a stylesheet,
 * which is the one thing this repository does not do.
 *
 * TWO SHAPE CHANGES APPLY TO EVERYTHING BELOW, so they are stated once.
 * Radius drops from 10px to the guide's §1.3 4px (`--radius`), and every
 * box-shadow goes: §1.3 reserves elevation for overlays and does separation
 * with borders and background steps. `--shadow` keeps its definition because
 * the workbench's column-chooser popover is an overlay and wants it. */

/* ---------------------------------------------------------- content type
 *
 * §1.2's ramp, on the two headings a page body carries. `--fs-h1`, `--fs-h2`
 * and `--font-display` are the tokens these rules read. A 26px bold
 * system-font title under a 28px Inter Light design is the most visible
 * single difference the switch to these tokens made. */
h1 {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: var(--fs-h1);
  line-height: 1.15;
  letter-spacing: -0.015em;
  margin: 0 0 var(--sp-2);
}
h2 {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--fs-h2);
  line-height: 1.2;
  letter-spacing: 0;
  margin: var(--sp-7) 0 10px;
}
.am-section:first-of-type h2 { margin-top: 0; }
/* Tighter heading-to-note spacing, only where a `.sub` note
 * follows the heading. A section with no note keeps the 10px above. */
h2:has(+ .sub) { margin-bottom: 4px; }
/* The note's own bottom margin, on every page, tighter than the base
 * `.sub` rule's 24px. `h2 + .sub` beats that rule by
 * specificity and reaches only a note that directly follows a heading.
 * --sp-4 is 12px on the §1.3 scale. A section with an assurance level
 * (`renderSectionHtml` in `web/views.js`) puts `.sec-level` between the
 * `h2` and the `.sub`, so the adjacent-sibling selector alone missed it;
 * `h2 + .sec-level + .sub` reaches that shape too. */
h2 + .sub, h2 + .sec-level + .sub { margin: 0 0 var(--sp-4); }
.sub {
  font-size: var(--fs-nav);
  color: var(--text-secondary);
  line-height: 1.5;
  text-wrap: pretty;
}

/* -------------------------------------------------- §3.1, the figure tile
 *
 * The grid is already the guide's `repeat(auto-fit, minmax(230px, 1fr))` at
 * a 12px gap and is not restated here.
 *
 * A FAILURE ROW INSIDE A KPI SECTION SPANS THE GRID, and this rule is the
 * whole fix for a collision. The register-row stat tiles
 * that render as failure rows sit in the `*-assertable-objective-failures-
 * target-zero` sections, which `report_sections.csv` marks
 * `presentation=kpi_row`, so `renderSectionHtml` wraps them in `.kpis`. A
 * 230px-wide three-column row is unreadable. The obvious-looking fix is to have
 * the renderer suppress the failure-row treatment inside a KPI section.
 * That makes `failureRow` unreachable on every page that has one, because those
 * are the same sections. So the fix is here: a row is a row, and the grid
 * gives it the full width. */
.kpis > .failrow { grid-column: 1 / -1; }

.tile {
  border-radius: var(--radius);
  padding: 14px 16px;
  box-shadow: none;
}
/* The label is sentence-case body text at the guide's `steel-800`, not an
 * uppercase eyebrow. §1.2 reserves ALL CAPS for eyebrow keys and status
 * badges, and a tile's label is neither: it is the question the figure
 * answers, and a question set in caps reads as a category. */
.tile .q {
  font-size: var(--fs-body);
  color: var(--steel-800);
  text-transform: none;
  letter-spacing: 0;
  margin-bottom: 9px;
}
.tile .v {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: 26px;
  line-height: 1;
  letter-spacing: -0.02em;
}
/* THE UNKNOWN FIGURE IS HELD TO NO MINIMUM SIZE. `steel-600` is `#616E78`,
 * which measures 4.63:1 against the hatch's dark band. That is the
 * normal-text floor at any size. (The prototype's `#697782` measures 4.07:1
 * there, and WCAG's 3:1 floor only reaches text at 24px and over.)
 * `web/tests/contrast.test.js` is what holds it: `--muted` clears 4.5:1 on
 * `--plane` and `--surface-1` in both themes, and those are the two hatch
 * bands. */
.tile .v.unknown {
  font-size: 22px;
  font-weight: 300;
  color: var(--muted);
}
/* A badge inside a tile's figure takes the badge's own type, not the
 * figure's display face, tracking and line height, so a gated tile's badge
 * matches the one on the assertion pages. */
.tile .v .badge {
  font-family: var(--font-sans);
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.55;
  letter-spacing: normal;
}
.tile .cap {
  font-size: var(--fs-caption);
  line-height: 1.45;
  color: var(--text-secondary);
}
.tile .target {
  font-size: var(--fs-caption);
  color: var(--muted);
  margin-top: var(--sp-2);
}
.tile .excepted {
  font-size: var(--fs-caption);
  color: var(--muted);
  margin-top: var(--sp-2);
  font-style: italic;
}
.tile .drill { font-size: var(--fs-body); }

/* THE HATCH, §3.1 verbatim, expressed in tokens so one rule serves both
 * themes. `--plane` and `--surface-1` resolve to `#F7F8F9`/`#F0F1F2` in
 * light, which is the guide's own gradient, and to `#14171A`/`#23282C` in
 * dark, which is the dark hatch. Both bands are surfaces
 * `contrast.test.js` already measures muted text against, so the figure on
 * the hatch is covered by a test rather than by this comment. */
.tile.unmeasured, .tile.st-unsummed {
  background: repeating-linear-gradient(135deg,
    var(--plane) 0 6px, var(--surface-1) 6px 12px);
}

/* A figure withheld because a collection-time parameter moved
 * (`value.state === "parameter_moved"`). The face the unmeasured tile uses,
 * without the hatch: the hatch says a figure could not be collected, and
 * this one was collected and is withheld. */
.tile .v.withheld {
  font-size: 22px;
  font-weight: 300;
  color: var(--muted);
}

/* The sum tile (st-) and the severity matrix (sm-), built on the tile rules
 * above. A cell's heat step comes from the --heat-1..5 tokens in the :root
 * blocks, named on data-color-var, and its ink from --heat-ink and
 * --heat-5-ink, which contrast.test.js holds at 4.5:1 on each step. */
/* ---- sum tile ---- */
.st { display: flex; flex-direction: column; }
.st .cap { margin-top: 6px; }
.st-rows { list-style: none; margin: var(--sp-4) 0 0; padding: 10px 0 0; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 5px; }
.st-row { display: grid; grid-template-columns: minmax(64px, 96px) minmax(0, 1fr) auto; align-items: center; gap: var(--sp-3); font-size: var(--fs-caption); }
.st-lbl { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-secondary); }
.st-track { display: block; height: 8px; background: var(--surface-1); border-radius: var(--radius-chip); overflow: hidden; }
.st-unsummed .st-track { background: var(--raised); box-shadow: inset 0 0 0 1px var(--border); }
.st-bar { display: block; height: 100%; background: var(--seq-450); border-radius: 0 var(--radius-chip) var(--radius-chip) 0; }
.st-bar-partial { background: var(--seq-250); }
.st-n { text-align: right; font-weight: 500; font-variant-numeric: tabular-nums; white-space: nowrap; }
.st-n-unknown { color: var(--muted); }
.st-strip { display: flex; height: 6px; margin-top: 10px; overflow: hidden; border-radius: var(--radius-chip); }
.st-seg { display: block; height: 100%; flex: none; box-shadow: inset -2px 0 0 var(--raised); }
.st-seg:last-child { box-shadow: none; }
.st-seg-0 { background: var(--seq-450); }
.st-seg-1 { background: var(--seq-250); }
.st-seg-partial.st-seg-0 { background: var(--steel-400); }
.st-seg-partial.st-seg-1 { background: var(--steel-300); }
.st-seg.st-seg-unknown, .st-sw.st-seg-unknown { background: repeating-linear-gradient(135deg, var(--plane) 0 3px, var(--steel-300) 3px 6px); }
.st-unsummed .st-seg { box-shadow: inset -2px 0 0 var(--surface-1); }
.st-foot { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-3); margin-top: var(--sp-3); }
.st-foot .cap { margin-top: 0; }
.st-open { flex: none; white-space: nowrap; display: inline-flex; align-items: center; gap: var(--sp-2); padding: var(--sp-1) 6px; border: 1px solid var(--control-border); border-radius: var(--radius); background: var(--raised); color: var(--text-primary); font: inherit; font-size: var(--fs-caption); cursor: pointer; transition: background .12s ease-out; }
.st-open:hover { background: var(--surface-1); }
.st-open[aria-expanded="true"] { background: var(--blue-a); }
.st-open:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--blue-a); border-color: var(--blue-600); }
.st-open .ic { width: 12px; height: 12px; }
.st { position: relative; }
.st-pop { position: absolute; z-index: 10; top: 100%; right: 0; left: 0; margin-top: var(--sp-2); padding: 10px var(--sp-4); background: var(--raised); border: 1px solid var(--control-border); border-radius: var(--radius); box-shadow: var(--shadow); }
.st-pop[hidden] { display: none; }
.st-eyebrow { margin: 0 0 6px; font-size: var(--fs-eyebrow); font-weight: 500; letter-spacing: .09em; text-transform: uppercase; color: var(--muted); }
.st-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.st-list li { display: grid; grid-template-columns: 10px minmax(0, 1fr) auto; align-items: center; gap: var(--sp-3); font-size: var(--fs-body); }
.st-sw { width: 10px; height: 10px; border-radius: var(--radius-chip); }
.st-total { display: flex; justify-content: space-between; margin: 6px 0 0; padding-top: 6px; border-top: 1px solid var(--border); font-size: var(--fs-body); color: var(--text-secondary); }
.st-total b { font-weight: 500; color: var(--text-primary); font-variant-numeric: tabular-nums; }
.st-total b.st-n-unknown { color: var(--muted); }

/* ---- severity matrix ---- */
.sm { margin: 0; padding: 14px 16px; background: var(--raised); border: 1px solid var(--border); border-radius: var(--radius); container-type: inline-size; display: flex; flex-direction: column; gap: 10px; }
.sm-title { font-size: var(--fs-h2); font-weight: 500; color: var(--text-primary); }
.sm-t { width: 100%; border-collapse: separate; border-spacing: 2px; table-layout: fixed; font-size: var(--fs-body); font-variant-numeric: tabular-nums; }
.sm-t th { font-size: var(--fs-caption); font-weight: 400; color: var(--text-secondary); padding: var(--sp-2) 6px; text-transform: none; letter-spacing: 0; background: none; }
.sm-t td, .sm-t th { border-bottom: 0; vertical-align: middle; }
.sm-t thead th { text-align: center; }
.sm-t tbody th { text-align: left; color: var(--steel-800); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sm-t thead td { width: 76px; }
.sm-t th.sm-tot, .sm-t td.sm-tot { width: 64px; text-align: right; font-weight: 500; }
.sm-c { height: 40px; text-align: center; font-weight: 500; color: var(--heat-ink); border-radius: var(--radius-chip); background: var(--raised); box-shadow: inset 0 0 0 1px var(--border); }
.sm-c[data-heat]:not([data-heat="0"]) { box-shadow: none; }
.sm-c[data-heat="5"] { color: var(--heat-5-ink); }
/* The hatch's two bands are --plane and --surface-1, the unmeasured tile's
 * own, because --muted on --steel-200 is 4.02:1 in the light theme.
 * contrast.test.js holds --muted on each band, which the ink-over-fill
 * walker cannot read through a gradient, so the ink and the hatch are two
 * rules. */
.sm-unk { font-size: var(--fs-eyebrow); font-weight: 500; color: var(--muted); }
.sm-c.sm-unk { background: repeating-linear-gradient(135deg, var(--plane) 0 4px, var(--surface-1) 4px 8px); box-shadow: inset 0 0 0 1px var(--steel-300); }
.sm-tot { color: var(--text-primary); padding-right: 6px; }
.sm-tot-unk { font-size: var(--fs-eyebrow); color: var(--muted); }
.sm-foot th, .sm-foot td { border-top: 1px solid var(--baseline); padding-top: 6px; }
.sm-foot td.sm-tot { text-align: center; }
.sm-foot td.sm-tot:last-child { text-align: right; }
/* compact and narrow: platforms in rows, numbers at the right */
.sm-compact thead th, .sm-narrow thead th { text-align: right; }
.sm-compact .sm-c, .sm-narrow .sm-c { height: 28px; text-align: right; padding-right: 6px; }
.sm-compact .sm-foot td.sm-tot, .sm-narrow .sm-foot td.sm-tot { text-align: right; }
.sm-t.sm-narrow { font-size: 12px; }
.sm-t.sm-narrow th { font-size: 11px; padding: 3px var(--sp-2); }
.sm-t.sm-narrow thead td { width: 58px; }
.sm-t.sm-narrow th.sm-tot, .sm-t.sm-narrow td.sm-tot { width: 44px; }
.sm-narrow .sm-unk, .sm-narrow .sm-tot-unk { font-size: 12px; }
.sm-notes { list-style: none; margin: 0; padding: 0; font-size: var(--fs-caption); line-height: 1.45; color: var(--text-secondary); }
/* one face shows: the wide face down to 440px of the figure's own width, the narrow face below it; a narrow layout draws only its narrow face */
.sm[data-layout="grid"] .sm-face-narrow, .sm[data-layout="compact"] .sm-face-narrow { display: none; }
@container (max-width: 439px) {
  .sm-face-wide { display: none; }
  .sm[data-layout="grid"] .sm-face-narrow, .sm[data-layout="compact"] .sm-face-narrow { display: block; }
}
@media print { .st-pop { display: none; } }

/* ------------------------------------------------- §3.3, the failure row
 *
 * `80px minmax(0,1fr) auto` and a 3px left border in the verdict colour.
 * The element vocabulary is the tile's, so a failure row reads as the same
 * tile. `failureRow` in `web/palette.js` says what that constrains, including
 * why the severity badge leads the caption line here instead of standing
 * in a status column of its own. */
/* `a.tile { display:block }` is (0,1,1) and `.failrow` alone is (0,1,0), so
 * the linked form needs the tag in the selector or it loses the tie and
 * stacks. Found by looking at the rendering rather than by reading the file:
 * every measured, drillable failure row drew as four stacked blocks while
 * the unlinked ones drew as the grid.
 *
 * The same tag-in-selector fact bit the severity colour below a second way:
 * `a.tile.failrow` is (0,2,1), one class heavier than a bare `.failrow.critical`
 * at (0,2,0), so on a linked tile the base rule's grey baseline always beat
 * the severity rule and every linked failure tile drew grey while an
 * unlinked one kept its colour. Measured in real Chrome on
 * `#/admin-roles-index`: every linked tile's computed `border-left-color`
 * was the baseline grey regardless of severity class. Each severity rule below
 * now also names the linked form, `a.tile.failrow.<sev>`, at (0,3,1), so it
 * wins on both the unlinked and the linked tile. */
.failrow, a.tile.failrow {
  display: grid;
  grid-template-columns: 80px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--sp-2) var(--sp-4);
  padding: 12px 14px;
  border-left: 3px solid var(--baseline);
}
.failrow .v {
  grid-column: 1;
  grid-row: 1 / span 2;
  font-size: 30px;
  color: var(--text-primary);
}
.failrow .fr-claim {
  grid-column: 2;
  grid-row: 1;
}
/* A plain stat tile carrying chips wraps its question the same way,
 * without the failure row's grid placement. */
.tile .fr-claim {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  min-width: 0;
}
.failrow .q {
  font-family: var(--font-sans);
  font-size: var(--fs-nav);
  font-weight: 500;
  color: var(--text-primary);
  text-transform: none;
  letter-spacing: 0;
  margin: 0;
}
.failrow .cap {
  grid-column: 2;
  grid-row: 2;
  margin: 0;
}
.failrow .drill {
  grid-column: 3;
  grid-row: 1 / span 2;
  margin: 0;
  text-align: right;
  white-space: nowrap;
}
/* The register id, §3.3's mono chip. `--radius-chip` is §1.3's 2px. The
 * class is `fr-chip` rather than a bare `chip` because the workbench's
 * filter chips are a different component with a different job, and one
 * name for two would make the first rule written for either the other's
 * problem. */
.tile .fr-chip {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--text-secondary);
  background: var(--surface-1);
  border-radius: var(--radius-chip);
  padding: 2px 5px;
  white-space: nowrap;
}
/* The verdict colour, from the severity the API served. Colour never carries
 * it alone: the badge on the caption line has the glyph and the word, and
 * this border is reinforcement. */
.failrow.good, a.tile.failrow.good { border-left-color: var(--good); }
.failrow.good .v { color: var(--green-700); }
.failrow.warning, a.tile.failrow.warning { border-left-color: var(--warning); }
.failrow.serious, a.tile.failrow.serious { border-left-color: var(--serious); }
.failrow.critical, a.tile.failrow.critical { border-left-color: var(--critical); }
.failrow.critical .v { color: var(--red-700); }
.failrow.unknown, a.tile.failrow.unknown { border-left-color: var(--steel-500); }
.failrow.vacuous, a.tile.failrow.vacuous {
  border-left-color: var(--control-border);
}
/* The unmeasured variant. A wider first column and a smaller face, because
 * the word UNKNOWN does not fit an 80px column at 30px and shrinking the
 * column instead would move every measured row beside it. */
.failrow.unmeasured {
  grid-template-columns: 96px minmax(0, 1fr) auto;
  align-items: start;
}
.failrow.unmeasured .v.unknown {
  font-size: 15px;
  line-height: 1.2;
  padding-top: var(--sp-2);
}
.failrow.unmeasured .fr-chip {
  background: var(--raised);
  border: 1px solid var(--border);
}
.failrow.withheld {
  grid-template-columns: 96px minmax(0, 1fr) auto;
  align-items: start;
}
.failrow.withheld .v.withheld {
  font-size: 15px;
  line-height: 1.2;
  padding-top: var(--sp-2);
}

/* ---------------------------------------------------- the coverage panel
 *
 * Every figure on this page with no number, the state that stopped it, and
 * the collector's own reason. Its own class vocabulary rather than the
 * tile's, because `web/tests/render-page.test.js` proves an explorer page
 * renders no tile, no empty state and no funnel, and a panel reusing those
 * names would make that proof answer a different question. */
.cov {
  margin: var(--sp-6) 0 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--raised);
}
.cov-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
}
/* An icon beside a text label, at §4.1's non-text 3:1 floor. The label is
 * the carrier; this is reinforcement. */
.cov-head .ic { color: var(--warning); }
.cov-title { font-size: var(--fs-nav); font-weight: 500; }
.cov-count {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  color: var(--muted);
}
.cov-note {
  margin: 0;
  padding: 10px 14px;
  font-size: var(--fs-caption);
  line-height: 1.5;
  color: var(--text-secondary);
}
.cov-rows { display: flex; flex-direction: column; }
.cov-row {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  gap: var(--sp-2) var(--sp-3);
  padding: 9px 14px;
  border-top: 1px solid var(--surface-1);
}
/* The mock-up's own `#B35F00`, kept, and measured before keeping it: the
 * `--warning` step is 4.63:1 on the panel in light and 6.95:1 in dark, both
 * over the 4.5:1 floor. §4.1's 4.14:1 finding is this step on the ORANGE
 * WASH, and these rows are on the panel. It is not the badge's case either,
 * where the ink darkens to `#8a6100` because `--surface-1` is a step down
 * and 1.79:1 there. */
.cov-tag {
  grid-column: 1;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--warning);
}
.cov-what { grid-column: 2; font-size: 12px; color: var(--steel-800); }
.cov-why {
  grid-column: 2;
  font-size: var(--fs-caption);
  line-height: 1.45;
  color: var(--muted);
}

/* ------------------------------------------------------- §3.4, the banner
 *
 * The glyph, the wash and the ink. `AM.widgets.banner` decides which glyph a
 * tone gets and gives `good` none; everything else about the treatment is
 * here. The base rule's 4px left border goes: the guide draws a banner as a
 * plain 1px box, and a coloured edge doing the work of the glyph beside it
 * is colour carrying severity alone. */
.banner {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  border-left-width: 1px;
  border-radius: var(--radius);
  padding: 11px 13px;
  margin: 0 0 18px;
  font-size: 12px;
  line-height: 1.5;
}
.banner .ic { margin-top: 2px; }
.bn-body { min-width: 0; }
.banner.warning {
  background: var(--orange-a2);
  border-color: var(--orange-100);
  color: var(--orange-ink);
}
.banner.critical {
  background: var(--red-a);
  border-color: var(--critical);
  color: var(--red-700);
}
/* §3.4: "Body copy is quoted verbatim from the collector, parameter names
 * in mono." `.mono` sets its own colour for a table cell, which on a wash is
 * a second, quieter ink over a tinted ground. Inside a washed banner the
 * body colour is already the readable step, so the mono run takes it. */
.banner.warning .mono, .banner.critical .mono { color: inherit; }

/* --------------------------------- the card shells: meter, hbar, funnel
 *
 * Restyled only. `data-width` is untouched, and so is every geometry rule
 * the meter, the bars and the funnels already carry. */
.meter, .card {
  border-radius: var(--radius);
  padding: 14px 16px;
  box-shadow: none;
}
.meter strong, .card strong { font-family: var(--font-sans); font-weight: 500; }
/* §1.3 allows two radii and no third. A track and a swatch are chips. */
.meter .track { border-radius: var(--radius-chip); }
.meter .seg:first-child { border-radius: var(--radius-chip) 0 0 var(--radius-chip); }
.meter .seg:last-child { border-radius: 0 var(--radius-chip) var(--radius-chip) 0; }
.meter .sw { border-radius: var(--radius-chip); }
.chart-row .bar { border-radius: 0 var(--radius-chip) var(--radius-chip) 0; }
/* the detection page: the figure card, the facts under it, the statement.
 * Colours are the tokens above, so both themes follow. */
.det-entry { display:flex; flex-wrap:wrap; align-items:center; gap:8px 16px;
  margin-bottom:16px; }
.det-entry .det-figure { font-size:28px; font-weight:500;
  font-variant-numeric:tabular-nums; color:var(--text-primary); }
.det-entry .det-state { display:flex; flex-wrap:wrap; align-items:center;
  gap:6px; }
.det-entry .det-reason { flex-basis:100%; margin:0; }
.det-facts { display:flex; flex-wrap:wrap; gap:4px 24px; flex-basis:100%;
  margin:0; }
.det-facts dt { font-size:11.5px; color:var(--text-secondary); }
.det-facts dd { margin:0; overflow-wrap:anywhere; }
.det-query { display:block; overflow-wrap:anywhere; white-space:pre-wrap; }
.det-assertions { margin:0; padding-left:20px; }
.det-assertions li { overflow-wrap:anywhere; }
/* the practice document: a generated Markdown file drawn as an article,
 * with its export button above it. */
.practice-actions { display:flex; flex-wrap:wrap; align-items:center;
  gap:8px 12px; margin-bottom:16px; }
button.practice-export { padding:7px 11px; font:inherit; font-size:12.5px;
  color:var(--text-primary); background:var(--raised);
  border:1px solid var(--border); border-radius:var(--radius-chip);
  cursor:pointer; }
button.practice-export:hover { background:var(--grid); }
.practice-note { font-size:12.5px; color:var(--text-secondary); }
.practice-doc { max-width:72em; overflow-wrap:anywhere; }
.practice-doc h3, .practice-doc h4 { margin:24px 0 8px; }
.practice-doc h5, .practice-doc h6 { margin:16px 0 6px; }
.practice-doc ul { margin:0 0 12px; padding-left:20px; }
.practice-doc code { white-space:pre-wrap; overflow-wrap:anywhere; }
/* The compliance radiator (ac-). Site tokens only, plus --ac-warn and --ac-logo-ground from the :root blocks. --ac-warn is the
 * dark theme's amber in both themes. In light it is 2.09:1 on the track, so a warning segment carries a 1.2px edge in --warning
 * (4.61:1 on --raised, 4.08:1 on the track); in dark --warning is the fill itself. --ac-logo-ground is the chip behind a served
 * system logo: white in every theme, so a logo drawn for a white page (aws-iam's black lettering, crowdstrike's white ground)
 * reads in dark. */
.ac { background: var(--raised); border: 1px solid var(--border); border-radius: var(--radius); container-type: inline-size; }
.ac-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* marks: one rule per state. Ring segments, pattern grounds and legend swatches read the same data-v. */
.ac-track { stroke: var(--surface-1); }
.ac-seg[data-v="conforming"] { stroke: var(--green-600); }
.ac-seg[data-v="unmeasured"] { stroke: var(--ac-warn); }
.ac-seg[data-v="not_countable"] { stroke: var(--steel-600); }
.ac-pg[data-v="nonconforming"] { fill: var(--red-600); }
.ac-pg[data-v="partially_measured"], .ac-pg[data-v="refused"] { fill: var(--ac-warn); }
.ac-pg[data-v="not_assessing"] { fill: var(--steel-600); }
.ac-pk { fill: var(--raised); }
.ac-edge { stroke: var(--warning); }

.ac-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--sp-3) var(--sp-6); padding: var(--sp-6) var(--sp-6) 0; }
.ac-title { margin: 0; font: 300 var(--fs-h1)/1.1 var(--font-display); color: var(--text-primary); }
.ac-sub { margin: 0; font-size: var(--fs-body); color: var(--text-secondary); }

.ac-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--sp-3); padding: var(--sp-5) var(--sp-6) var(--sp-6); border-bottom: 1px solid var(--divider); }
.ac-tile { display: flex; align-items: center; gap: var(--sp-4); min-width: 0; padding: var(--sp-3) var(--sp-4); border: 1px solid var(--border); border-radius: var(--radius); background: var(--raised); color: var(--text-primary); font: inherit; text-align: left; cursor: pointer; transition: background .12s ease-out; }
.ac-tile:hover { background: var(--surface-1); }
.ac-tile:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--blue-a); border-color: var(--blue-600); }
.ac-tile[aria-pressed="true"] { background: var(--blue-100); border-color: var(--blue-600); box-shadow: inset 0 var(--selected-bar) 0 var(--blue-600); }
.ac-tile .ac-ring { flex: none; }
.ac-tile-body { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; flex: 1; }
.ac-tile-name { font-size: var(--fs-nav); font-weight: 500; line-height: 1.25; text-wrap: balance; overflow-wrap: anywhere; }
.ac-tile-line { font-size: var(--fs-caption); line-height: 1.35; color: var(--text-secondary); overflow-wrap: anywhere; }

.ac-detail-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-3) var(--sp-5); padding: var(--sp-5) var(--sp-6); background: var(--plane); border-bottom: 1px solid var(--divider); }
.ac-detail-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-1) var(--sp-4); }
.ac-detail-name { margin: 0; font-size: var(--fs-h2); font-weight: 500; color: var(--text-primary); }
.ac-detail-sub { margin: 0; font-size: var(--fs-body); color: var(--text-secondary); }
.ac-legend { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-5); font-size: var(--fs-caption); color: var(--text-secondary); }
.ac-legend li { display: flex; align-items: center; gap: var(--sp-2); }
.ac-sw { flex: none; }
.ac-empty { margin: 0; padding: var(--sp-6); font-size: var(--fs-body); color: var(--muted); }

.ac-cards { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 1px; background: var(--divider); border-radius: 0 0 var(--radius) var(--radius); overflow: hidden; }
.ac-card { background: var(--raised); padding: var(--sp-6); display: flex; flex-direction: column; gap: var(--sp-4); }
.ac-card-head { display: flex; gap: var(--sp-5); align-items: center; }
.ac-ring-wrap { position: relative; width: 96px; height: 96px; flex: none; }
.ac-ring-c { position: absolute; left: 50%; top: 50%; width: 52px; height: 32px; transform: translate(-50%, -50%); display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; overflow: hidden; }
.ac-ring-n { font: 300 17px/1 var(--font-display); color: var(--text-primary); font-variant-numeric: tabular-nums; }
.ac-ring-w { font-size: 9.5px; line-height: 1.15; letter-spacing: -0.01em; color: var(--text-secondary); }
.ac-card-id { display: flex; flex-direction: column; gap: var(--sp-2); min-width: 0; }
.ac-card-line { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); }
.ac-aid { font: 500 var(--fs-nav)/1 var(--font-mono); white-space: nowrap; }
.ac-statement { margin: 0; font-size: var(--fs-body); line-height: 1.45; color: var(--text-primary); text-wrap: pretty; }

.ac-ins, .ac-stated { list-style: none; margin: 0; padding: 0; }
.ac-row { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); gap: var(--sp-1) var(--sp-3); align-items: center; padding: var(--sp-2) 0; border-top: 1px solid var(--divider); font-size: var(--fs-caption); }
.ac-row-name { min-width: 0; color: var(--text-primary); overflow-wrap: anywhere; }
.ac-sys { justify-self: center; display: inline-flex; align-items: center; gap: var(--sp-2); padding: var(--sp-1) var(--sp-3) var(--sp-1) var(--sp-1); border-radius: var(--radius-pill); font: 500 var(--fs-mono)/1.3 var(--font-sans); white-space: nowrap; background: var(--sys-neutral-tint); color: var(--sys-neutral-ink); }
.ac-sys:not(:has(.ac-logo, .ic)) { padding-left: var(--sp-3); }
.ac-sys .ic { width: 14px; height: 14px; flex: none; margin-left: var(--sp-1); }
.ac-logo { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; flex: none; border-radius: 50%; background: var(--ac-logo-ground); }
.ac-logo img { display: block; width: 12px; height: 12px; object-fit: contain; }
.ac-sys[data-ramp="indigo"] { background: var(--sys-indigo-tint); color: var(--sys-indigo-ink); }
.ac-sys[data-ramp="yellow"] { background: var(--sys-yellow-tint); color: var(--sys-yellow-ink); }
.ac-sys[data-ramp="purple"] { background: var(--sys-purple-tint); color: var(--sys-purple-ink); }
.ac-sys[data-ramp="pink"] { background: var(--sys-pink-tint); color: var(--sys-pink-ink); }
.ac-sys[data-ramp="aqua"] { background: var(--sys-aqua-tint); color: var(--sys-aqua-ink); }
.ac-sys[data-ramp="brown"] { background: var(--sys-brown-tint); color: var(--sys-brown-ink); }
.ac-sys[data-ramp="olive"] { background: var(--sys-olive-tint); color: var(--sys-olive-ink); }
.ac-sys[data-ramp="teal"] { background: var(--sys-teal-tint); color: var(--sys-teal-ink); }
.ac-sys[data-ramp="blue"] { background: var(--sys-blue-tint); color: var(--sys-blue-ink); }
.ac-metric { min-width: 0; text-align: right; font-weight: 500; font-variant-numeric: tabular-nums; color: var(--muted); }
.ac-metric[data-v="nonconforming"] { color: var(--red-600); }
.ac-metric[data-v="conforming"] { color: var(--green-600); }
.ac-metric[data-v="stated"] { font-weight: 400; }
.ac-reason { grid-column: 1 / -1; color: var(--muted); line-height: 1.35; }
.ac-stated li { display: flex; justify-content: space-between; gap: var(--sp-3); padding: var(--sp-2) 0; border-top: 1px dashed var(--divider); font-size: var(--fs-caption); }
.ac-stated .ac-row-name { color: var(--muted); }

@container (max-width: 520px) {
  .ac-head, .ac-tiles, .ac-detail-head { padding-left: var(--sp-5); padding-right: var(--sp-5); }
  .ac-tiles { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
  .ac-tile { gap: var(--sp-3); padding: var(--sp-3); }
  .ac-cards { grid-template-columns: minmax(0, 1fr); }
  .ac-card { padding: var(--sp-5); }
  .ac-row { grid-template-columns: minmax(0, 1fr) auto; }
  .ac-row .ac-sys { grid-row: 2; justify-self: start; }
  .ac-row .ac-metric { grid-row: 1 / span 2; grid-column: 2; align-self: center; }
}
@media print { .ac-tile, .ac-card { break-inside: avoid; } }
/* --- END: CONTENT ------------------------------------------------------- */

/* --- ANCHOR: WORKBENCH -------------------------------------------------- */
/* docs/ui-template/style-guide.md §3.5. The table and
 * the controls over it, as one panel.
 *
 * ONE SET OF CLASSES, NOT TWO. `report_pages.csv` has an explorer
 * column, so the four explorer pages are pages and `web/explorer-views.js`
 * builds its tables through the same `AM.widgets.dataTable` the generic
 * views use. Everything below therefore styles whatever that function
 * emits, wherever it was called from, which is what §3.5 was decided to
 * get: two toolbars that look alike are two toolbars free to drift.
 *
 * `.tools` above stays. It is the EXPLORERS' OWN view-level filter bar.
 * It re-renders a whole explorer from its payload, and it is a
 * different control from the toolbar over one table. Reusing these classes
 * for it would say the two do the same thing. */

/* The panel. `[data-table]` rather than a new class: the attribute is
 * already the contract between `dataTable` and `mountDataTables`, and a
 * class beside it would be a second name for one thing. */
[data-table] {
  background: var(--raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  /* The column popover is absolutely positioned against the toolbar and
   * hangs below it, so the panel cannot clip its own overflow. */
  overflow: visible;
  /* An `.estate` panel (a not-collected or filtered empty state) can
   * be this panel's own content, but the container for it lives on
   * `.estate-host` around that panel, not here: this attribute also
   * selects the workbench's Confirm button (`web/register-editors.js`, the activity
   * route's `button.activity-confirm`), which is never a panel parent. */
}
/* The scroller inside the panel has lost its reason to draw an edge of its
 * own: the panel draws one. Its radius is gone too, because two radii on
 * the same corner is the "nothing else" §1.3 refuses. */
[data-table] .tbl-scroll {
  border: 0;
  border-radius: 0;
  background: transparent;
}
[data-table] table { font-size: var(--fs-body); }
/* The sunken step §3.5 asks for, and Medium 11.5px rather than the legacy
 * uppercase treatment. Sticky against the panel, not the page. */
[data-table] th {
  background: var(--surface-1);
  font-size: var(--fs-caption);
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-secondary);
}
[data-table] tbody tr:nth-child(even) { background: var(--plane); }
[data-table] tbody tr:hover { background: var(--surface-1); }
/* The sticky column's own opaque background (in this file, the `th`
 * anchor) would otherwise paint every row the same shade in here and lose
 * the zebra stripe the rest of the row keeps; the hover shade above
 * already matches `.col-sticky`'s own hover rule and needs no second
 * copy. */
[data-table] tbody tr:nth-child(even) .col-sticky { background: var(--plane); }

/* NUMERIC COLUMNS RIGHT-ALIGNED AND MONO, on the header as well as the
 * cell: a right-aligned column under a left-aligned label reads as two
 * columns. `data-type` is `dataTable`'s own per-column declaration and is
 * what the sort already reads, so nothing new says which column is which. */
[data-table] th[data-type="num"] { text-align: right; }
[data-table] td.num {
  text-align: right;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
}

/* The active sort column, in `blue-700` with its arrow beside it. `data-dir`
 * is set by the mount, so an unsorted table carries none of this. */
[data-table] th[data-dir] { color: var(--blue-700); }
[data-table] th[data-dir] .arrow { opacity: 1; font-size: 10px; }

/* §3.5's three cell treatments, and each one is a claim about the value.
 *
 * An absent value is an em dash in the muted step, never a blank and never
 * the string `False`, which is `CLAUDE.md`'s rule in a table cell. A zero steps
 * down the ramp because a measured zero is a result and should not shout.
 * `--muted` is `steel-600` after §4.1's correction, which clears 4.5:1 on
 * the panel, on the canvas and on the banded row; the guide's `steel-400`
 * for the em dash measures 2.2:1 and is what §4.1 rejected. */
[data-table] .wb-absent { color: var(--muted); }
[data-table] .wb-zero { color: var(--muted); }
[data-table] .wb-no { color: var(--muted); font-family: var(--font-mono); }

.wb-right {
  position: relative;
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

/* Columns · n and the density toggle. 30px at radius 4. */
.wb-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 10px;
  font: inherit;
  font-size: var(--fs-body);
  white-space: nowrap;
  cursor: pointer;
  color: var(--steel-800);
  background: var(--raised);
  border: 1px solid var(--control-border);
  border-radius: var(--radius);
}
.wb-btn:hover { background: var(--surface-1); }
.wb-btn:focus-visible {
  outline: none;
  border-color: var(--blue-600);
  box-shadow: 0 0 0 3px var(--blue-a);
}
.wb-btn .ic { color: var(--text-secondary); }

/* The split export button: one 30px box, one border, a divider between the
 * halves in `blue-700`, per §1.1's own name for that token. */
.wb-split {
  display: flex;
  height: 30px;
  border: 1px solid var(--blue-600);
  border-radius: var(--radius);
  overflow: hidden;
}
.wb-split button {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 100%;
  padding: 0 11px;
  font: inherit;
  font-size: var(--fs-body);
  white-space: nowrap;
  cursor: pointer;
  color: var(--raised);
  background: var(--blue-600);
  border: 0;
}
.wb-split button:hover { background: var(--blue-700); }
.wb-split button:focus-visible { outline: 2px solid var(--blue-700); outline-offset: -2px; }
.wb-split .ic { color: var(--raised); }
/* The divider between the halves, drawn only where there are two: a CSV
 * button standing alone (a table with no widget id behind it, so no XLSX
 * route to call) is one button, not half of one. */
.wb-split button + button { border-left: 1px solid var(--blue-700); padding: 0 10px; }

/* ------------------------------------------------- density, and the footer */
/* §3.5: 8px comfortable, 4px compact. The class rides on the panel so one
 * toggle moves every cell in it, and the button beside it names the state a
 * click would MOVE TO, never the one already showing. */
.wb-compact th, .wb-compact td { padding-top: 4px; padding-bottom: 4px; }

.wb-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4);
  margin: 0;
  padding: 9px 12px;
  background: var(--plane);
  border-top: 1px solid var(--border);
  border-radius: 0 0 var(--radius) var(--radius);
}
.wb-foot:empty, .wb-foot[hidden] { display: none; }
/* What a chip hid, and what an export wrote. Both are muted because both
 * are reports about the view rather than evidence in it. Both are
 * PRESENT rather than absent, because a chip that hides rows without saying
 * how many is the defect `CLAUDE.md` names as a noise class being dropped
 * instead of named. */
.wb-hidden { font-size: 12px; color: var(--text-secondary); }
/* What the toolbar's arithmetic is OVER, on a table that is a page of a
 * larger population. Rendered with the table rather than written by the
 * mount, because it is a fact about the payload and not about what the
 * reader did. */
.wb-scope { font-size: 12px; color: var(--muted); }
.wb-note { margin-left: auto; font-size: 12px; color: var(--muted); }

/* Server mode. A withheld
 * workbook keeps its place disabled, so it is drawn as unavailable and the
 * `.wb-cap` sentence beside it says why. The pager is two `.wb-btn`s. A
 * view the service refused is said above the table in the house's critical
 * colour, and the CSV note under the full export is a muted sentence. */
.wb-split button:disabled, .wb-btn:disabled {
  cursor: default; color: var(--muted); background: var(--surface-1);
}
.wb-split button:disabled .ic { color: var(--muted); }
.wb-cap { font-size: 12px; color: var(--text-secondary); }
.wb-pager { display: inline-flex; flex-wrap: wrap; gap: var(--sp-3); }
.export-note { margin: 6px 0 0; font-size: 12px; color: var(--muted); }
.server-error { color: var(--critical); }

/* The filtered-to-nothing state, rendered by the mount because the filter
 * that empties the table is applied there. It reuses `.estate`, so it looks
 * like every other declared empty state on the page. */
.wb-empty { padding: 10px 12px; }
.wb-empty[hidden] { display: none; }

/* ------------------------------------------------------- typed columns */
/* A cell drawn from its column record by `columnCellHtml` in `web/table.js`.
 * The cell's text is always the value or the empty glyph, and every marker
 * is an empty span drawn here, so a copied or exported cell carries no
 * marker text. Markers are muted, and the squares take the severity
 * tokens the badges use. */
[data-table] .ct-empty { color: var(--muted); }
[data-table] .ct-unknown {
  color: var(--text-secondary); border: 1px dashed var(--muted);
  border-radius: var(--radius-chip); padding: 0 var(--sp-2);
  font-size: var(--fs-caption);
}
/* The mismatch marker: a small muted outlined diamond before a value that
 * does not parse as its type or that the record does not declare, and
 * before the empty glyph in a column declared never empty. */
[data-table] .ct-mark {
  display: inline-block; width: 6px; height: 6px; margin-right: var(--sp-2);
  border: 1px solid var(--muted); transform: rotate(45deg);
  vertical-align: middle;
}
/* An `item_names` list whose length differs from the items: a bar after
 * the squares, where the per-item names would have been. */
[data-table] .ct-mark.ct-names {
  width: 2px; height: 10px; margin: 0 0 0 var(--sp-2); transform: none;
  border-color: var(--muted); background: var(--muted);
}
[data-table] .ct-mismatch { color: var(--text-primary); }

/* A GUID shows its head, carries the whole value in its title, and copies
 * the whole value. The copy icon is two offset outlines. */
[data-table] .ct-guid { display: inline-flex; align-items: center; gap: var(--sp-2); max-width: 100%; }
[data-table] .ct-guid-v {
  display: inline-block; max-width: 12ch; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; font-family: var(--font-mono);
  font-size: var(--fs-mono);
}
[data-table] .ct-copy {
  position: relative; flex: none; width: 20px; height: 20px; padding: 0;
  border: 1px solid transparent; border-radius: var(--radius-chip);
  background: transparent; cursor: pointer;
}
[data-table] .ct-copy::before,
[data-table] .ct-copy::after {
  content: ""; position: absolute; width: 8px; height: 9px;
  border: 1px solid var(--text-secondary); border-radius: 1px;
  background: var(--raised);
}
[data-table] .ct-copy::before { left: 4px; top: 3px; }
[data-table] .ct-copy::after { left: 7px; top: 6px; }
[data-table] .ct-copy:hover, [data-table] .ct-copy:focus-visible {
  border-color: var(--control-border);
}
[data-table] .ct-copy.is-done::before,
[data-table] .ct-copy.is-done::after { border-color: var(--good); }
[data-table] .ct-copy.is-fail::before,
[data-table] .ct-copy.is-fail::after { border-color: var(--critical); }

[data-table] .ct-time { font-variant-numeric: tabular-nums; }

/* A multi-valued enum: one tag per item. */
[data-table] .ct-tags { display: inline-flex; flex-wrap: wrap; gap: var(--sp-1); }
[data-table] .ct-tag {
  border: 1px solid var(--border); border-radius: var(--radius-chip);
  padding: 0 var(--sp-2); font-size: var(--fs-caption);
}
[data-table] .ct-tag-undeclared { border-style: dashed; border-color: var(--muted); }

/* A multi-valued status: one square per item, coloured by its severity,
 * then "k of n". `unknown` is dashed, as `.badge.unknown` is; an item the
 * record does not declare is a muted outline. */
[data-table] .ct-items { display: inline-flex; align-items: center; gap: var(--sp-2); }
[data-table] .ct-sqs { display: inline-flex; flex-wrap: wrap; gap: var(--sp-1); }
[data-table] .ct-sq {
  display: inline-block; width: 9px; height: 9px;
  border: 1px solid transparent; border-radius: 1px;
}
[data-table] .ct-sev-critical { background: var(--critical); }
[data-table] .ct-sev-serious { background: var(--serious); }
[data-table] .ct-sev-warning { background: var(--warning); }
[data-table] .ct-sev-good { background: var(--good); }
[data-table] .ct-sev-info { background: var(--steel-500); }
[data-table] .ct-sev-unknown { background: transparent; border: 1px dashed var(--text-secondary); }
[data-table] .ct-sq-undeclared { background: transparent; border-color: var(--muted); }
[data-table] .ct-kn { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Print: the evidence stays, and the footer note with it, because what an
 * export wrote is a fact about the page a printed copy should still carry. */
@media print {
  [data-table] { border-color: var(--baseline); }
}
/* CARL table widget chrome. Class-based, no inline styles, tokens only.
 * Cells are drawn by the site's AM.columnTypes.cellHtml and styled by the site's ct-* classes, not here. */
.tw { position: relative; background: var(--raised); border: 1px solid var(--border); border-radius: var(--radius); font-family: var(--font-sans); font-size: var(--fs-body); color: var(--text-primary); }
.tw[aria-busy="true"] .tw-scroll { opacity: .6; }
.tw button, .tw input, .tw select { font: inherit; }
.tw .ic { width: 1em; height: 1em; flex: none; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; font-size: var(--fs-nav); }
.tw-pchips { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 12px; border-bottom: 1px solid var(--border); }

/* toolbar */
.tw-bar { position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; padding: 10px 12px; background: var(--plane); border-bottom: 1px solid var(--border); border-radius: var(--radius) var(--radius) 0 0; }
.tw-search { position: relative; display: flex; align-items: center; }
.tw-search .ic { position: absolute; left: 10px; color: var(--muted); pointer-events: none; }
.tw-search input { width: 240px; height: 30px; box-sizing: border-box; padding: 0 10px 0 30px; border: 1px solid var(--control-border); border-radius: var(--radius); background: var(--raised); color: var(--text-primary); font-size: var(--fs-body); }
.tw-search input:focus, .tw-gearbtn:focus-visible, .tw-hbtn:focus-visible, .tw-fchip:focus-visible, .tw-split button:focus-visible, .tw-xlsx:focus-visible, .tw-textbtn:focus-visible, .tw-resize:focus-visible { outline: none; border-color: var(--blue-600); box-shadow: 0 0 0 3px var(--blue-a); }
.tw-fchip { display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 9px; border: 1px solid var(--blue-600); border-radius: var(--radius-pill); background: var(--blue-a); color: var(--blue-700); font-size: var(--fs-caption); cursor: pointer; transition: background .12s ease-out; }
.tw-fchip:hover { background: var(--blue-100); }
.tw-textbtn { padding: 0 6px; height: 28px; border: 0; background: none; color: var(--blue-600); font-size: var(--fs-body); cursor: pointer; border-radius: var(--radius); }
.tw-textbtn:hover { color: var(--blue-700); text-decoration: underline; }
.tw-textbtn:disabled { color: var(--muted); cursor: default; text-decoration: none; }
.tw-right { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.tw-count { font-size: var(--fs-caption); color: var(--text-secondary); font-variant-numeric: tabular-nums; margin-right: 2px; }
.tw-gearbtn { min-width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 0 7px; border: 1px solid var(--control-border); border-radius: var(--radius); background: var(--raised); color: var(--text-secondary); cursor: pointer; transition: background .12s ease-out, color .12s ease-out; }
.tw-gearbtn:hover { background: var(--surface-1); color: var(--steel-900); }
.tw-gearbtn[aria-expanded="true"] { background: var(--blue-100); }
.tw-gear-n { font-size: var(--fs-caption); white-space: nowrap; }
.tw-split { display: flex; height: 30px; border: 1px solid var(--control-border); border-radius: var(--radius); overflow: hidden; }
.tw-split button, .tw-xlsx { display: inline-flex; align-items: center; gap: 6px; padding: 0 11px; border: 0; background: var(--raised); color: var(--blue-700); font-size: var(--fs-body); text-decoration: none; cursor: pointer; }
.tw-split button:hover, .tw-xlsx:hover { background: var(--blue-a); }
.tw-split > * + * { border-left: 1px solid var(--control-border); }

/* popovers: the one overlay shadow. The gear is placed by CSS; the column menu is anchored to its open header. */
.tw-pop { z-index: 20; padding: 8px 0; background: var(--raised); border: 1px solid var(--control-border); border-radius: var(--radius); box-shadow: var(--shadow); }
.tw-pop-gear { position: absolute; top: 46px; right: 12px; width: 240px; }
.tw-pop-key { position: absolute; top: 46px; right: 12px; width: 360px; max-width: calc(100% - 24px); padding: 10px 12px; font-size: var(--fs-caption); line-height: 1.6; color: var(--text-primary); }
.tw-gearbtn[aria-expanded="true"].tw-keybtn { background: var(--blue-100); }
.tw-menu { position: absolute; z-index: 30; width: 236px; padding: 6px 0; top: 92px; left: 12px; }
.tw-hbtn[aria-expanded="true"] { anchor-name: --tw-open; }
@supports (anchor-name: --a) {
  .tw-menu { position-anchor: --tw-open; top: calc(anchor(bottom) + 4px); left: anchor(left); position-try-fallbacks: flip-inline; }
}
.tw-eyebrow { margin: 0; padding: 4px 12px 6px; font-size: var(--fs-eyebrow); font-weight: 500; letter-spacing: .09em; text-transform: uppercase; color: var(--text-secondary); }
.tw-rule { border: 0; border-top: 1px solid var(--border); margin: 8px 0; }
.tw-row-opt { display: flex; align-items: center; gap: 10px; padding: 5px 12px; color: var(--steel-800); cursor: pointer; }
.tw-row-opt:hover { background: var(--plane); }
.tw-row-opt input { accent-color: var(--blue-600); margin: 0; }
.tw-locked { color: var(--muted); cursor: default; }
.tw-grow { flex: 1; }
.tw-facet-n { color: var(--blue-700); font-size: var(--fs-caption); font-variant-numeric: tabular-nums; }
.tw-menu-note { margin: 6px 12px 2px; font-size: var(--fs-caption); line-height: 1.4; color: var(--text-secondary); }
.tw-seg-ctl { display: flex; margin: 0 12px; border: 1px solid var(--control-border); border-radius: var(--radius); overflow: hidden; }
.tw-seg-ctl button { flex: 1; height: 28px; border: 0; border-right: 1px solid var(--border); background: var(--raised); color: var(--text-primary); cursor: pointer; }
.tw-seg-ctl button:last-child { border-right: 0; }
.tw-seg-ctl button[aria-pressed="true"] { background: var(--blue-100); color: var(--blue-700); }
.tw-pop-gear .tw-textbtn { margin: 0 6px; }
.tw-menu-item { display: flex; width: 100%; box-sizing: border-box; align-items: center; gap: 8px; padding: 6px 12px; border: 0; background: var(--raised); color: var(--text-primary); text-align: left; cursor: pointer; }
.tw-menu-item .ic { opacity: 0; }
.tw-menu-item:hover { background: var(--plane); }
.tw-menu-item.tw-on { background: var(--blue-a); color: var(--blue-700); }
.tw-menu-item.tw-on .ic { opacity: 1; }
.tw-menu-head { display: flex; justify-content: space-between; align-items: center; padding-right: 6px; }
.tw-menu-head .tw-eyebrow { padding-bottom: 4px; }
.tw-menu-list, .tw-col-list { max-height: 260px; overflow: auto; }

/* the table */
.tw-scroll { overflow: auto; max-height: 620px; }
.tw-table { border-collapse: separate; border-spacing: 0; table-layout: fixed; width: max-content; min-width: 100%; font-size: var(--fs-nav); }
.tw-th { position: sticky; top: 0; z-index: 2; padding: 8px 12px; text-align: left; background: var(--surface-1); border-bottom: 1px solid var(--baseline); font-size: var(--fs-caption); font-weight: 500; color: var(--steel-800); white-space: nowrap; overflow: hidden; }
.tw-th.tw-sorted { color: var(--blue-700); }
.tw-table th.tw-th.col-sticky { z-index: 3; }
.tw-r { text-align: right; }
.tw-hbtn { display: inline-flex; align-items: center; gap: 5px; max-width: calc(100% - 10px); min-width: 0; padding: 0; border: 1px solid transparent; border-radius: var(--radius-chip); background: none; color: inherit; font: inherit; cursor: pointer; transition: color .12s ease-out; }
.tw-hbtn:hover { color: var(--blue-700); }
.tw-hbtn .ic { font-size: var(--fs-caption); }
.tw-hbtn > .tw-arrow, .tw-hbtn > .tw-fcount, .tw-hbtn > .ic { flex: none; }
.tw-hlabel { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.tw-arrow { display: inline-flex; color: var(--blue-600); }
.tw-arrow.tw-off { visibility: hidden; }
.tw-hbtn > .ic:last-child { color: var(--text-secondary); }
.tw-fcount { padding: 1px 6px; border-radius: var(--radius-pill); background: var(--blue-100); color: var(--blue-700); font-size: var(--fs-caption); font-variant-numeric: tabular-nums; }
.tw-table td { padding: 9px 12px; border-bottom: 1px solid var(--grid); vertical-align: top; background: inherit; overflow: hidden; text-overflow: ellipsis; }
.tw-compact .tw-table td { padding-top: 4px; padding-bottom: 4px; }
.tw-nowrap .tw-table td, .tw-nowrap .tw-table td * { white-space: nowrap; }
.tw-table .col-sticky { position: sticky; left: 0; z-index: 1; }
.tw-table tbody tr { background: var(--raised); transition: background .12s ease-out; }
.tw-table tr.am-rowlink { cursor: pointer; }
.tw-table tbody tr:nth-child(even) { background: var(--plane); }
.tw-table tbody tr:hover { background: var(--plane); }
.tw-table tbody tr:nth-child(even):hover { background: var(--surface-1); }
.tw-table tbody tr:last-child td { border-bottom: 0; }
.tw-resize { position: absolute; top: 0; right: 0; bottom: 0; width: 9px; cursor: col-resize; touch-action: none; }
.tw-resize::after { content: ""; position: absolute; top: 6px; bottom: 6px; right: 3px; width: 1px; background: var(--baseline); }
.tw-resize:hover::after, .tw-resize:focus-visible::after, .tw-resizing::after { top: 0; bottom: 0; width: 2px; right: 3px; background: var(--blue-600); }
.tw-empty { padding: 28px 16px; text-align: center; font-size: var(--fs-nav); color: var(--text-secondary); }
.tw-foot { padding: 9px 12px; border-top: 1px solid var(--border); font-size: var(--fs-caption); color: var(--text-secondary); }
.tw-foot { display: flex; flex-wrap: wrap; gap: 4px 16px; align-items: center; }
.tw-foot > span { max-width: 100%; }

@media (max-width: 600px) {
  .tw-search { flex: 1 1 100%; }
  .tw-search input { width: 100%; }
  .tw-right { margin-left: 0; width: 100%; justify-content: space-between; flex-wrap: wrap; }
  }

.tw-chip { display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 9px; border: 1px solid var(--control-border); border-radius: var(--radius-pill); background: var(--raised); color: var(--text-secondary); font-size: var(--fs-caption); cursor: pointer; transition: background .12s ease-out, color .12s ease-out; }
.tw-chip:hover { background: var(--surface-1); color: var(--text-primary); }
.tw-chip:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--blue-a); }
.tw-chip-on { border-color: var(--blue-600); background: var(--blue-a); color: var(--blue-700); }
.tw-facet-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 6px 12px; border-bottom: 1px solid var(--border); }
.tw-facet-row .tw-eyebrow { padding: 0; margin-right: 4px; }

/* --- END: WORKBENCH ----------------------------------------------------- */

/* --- ANCHOR: PROVENANCE ------------------------------------------------- */
/* docs/ui-template/style-guide.md §3.6. One line always, a panel on demand.
 *
 * The outer element keeps the class `.prov` the page renderer used before
 * the strip existed, and the older `.prov`/`.prov dl` rules further up this
 * file are left where they are: `web/explorer-views.js`'s `.pol` property
 * list is written to match that look, so those rules are still somebody's.
 * What they no longer are is this strip's, and every rule below is scoped by
 * the region id so it wins without touching them.
 *
 * THE SIZING RULE. The region is one line in the page's own flow (the
 * SHELL anchor's rule 2). The full panel is a slide-over with its own
 * scroller, so an expanded view of a 14-field envelope never squeezes the
 * content region. */

#spa-provenance .prov {
  margin: 0;
  padding: 0;
  border-top: 0;
  color: var(--text-secondary);
  font-size: var(--fs-body);
}

/* The collapsed line: the last element of the page's own flow. It
 * wraps rather than truncates on a narrow viewport. */
.prov-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-4);
  padding: 10px var(--sp-6);
}
.prov-line .ic { color: var(--muted); }
.prov-title {
  font: 500 var(--fs-body) var(--font-sans);
  color: var(--text-secondary);
}
.prov-stamp { color: var(--muted); }
.prov-mono {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  color: var(--text-secondary);
}
.prov-toggle {
  margin-left: auto;
  border: 0;
  background: transparent;
  padding: 0;
  font: inherit;
  font-size: var(--fs-body);
  color: var(--blue-600);
  cursor: pointer;
}
.prov-toggle:hover { color: var(--blue-700); }
.prov-toggle:focus-visible {
  outline: 2px solid var(--blue-600);
  outline-offset: 2px;
}

/* The slide-over. A modal dialog from the right, over a
 * backdrop; `hidden` on both is what closes it, and the global `[hidden]`
 * rule makes that win over the `display` below. While closed it covers
 * nothing. */
#spa-provenance .prov-backdrop {
  position: fixed;
  inset: 0;
  z-index: 49;
  background: rgba(15, 23, 42, .4);
}
#spa-provenance .prov-panel {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  z-index: 50;
  /* A strip of backdrop stays visible at every width, so a click outside
   * the panel is always there to close it. */
  width: min(640px, calc(100vw - 40px));
  overflow-y: auto;
  padding: 0 var(--sp-6) var(--sp-6);
  background: var(--raised);
  border-left: 1px solid var(--border);
  box-shadow: var(--shadow);
}
.prov-panel-head {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: 14px 0;
  margin-bottom: var(--sp-4);
  background: var(--raised);
  border-bottom: 1px solid var(--border);
}
.prov-panel-head h2 {
  margin: 0;
  font: 500 var(--fs-h2) var(--font-sans);
  color: var(--text-primary);
}
.prov-close { display: inline-flex; align-items: center; gap: 6px; }
.prov-note {
  margin: 0 0 var(--sp-4);
  font-size: var(--fs-body);
  line-height: 1.5;
  color: var(--muted);
}

/* Disagreement is a note, never an alarm. */
.prov-disagree {
  display: flex;
  gap: var(--sp-3);
  margin: 0 0 var(--sp-4);
  padding: 10px 12px;
  color: var(--text-secondary);
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.prov-disagree .ic { flex: none; margin-top: 2px; color: var(--muted); }
.prov-disagree-head { margin: 0 0 4px; font-weight: 500; }
.prov-disagree ul { margin: 0; padding-left: 18px; }
.prov-disagree li { margin: 2px 0; line-height: 1.45; }

/* The four groups. */
.prov-group h3 {
  margin: var(--sp-5) 0 var(--sp-2);
  font: 500 var(--fs-caption) var(--font-sans);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The definition list. A label column of at least 12ch, and one column
 * where the panel is narrow. UNKNOWN, version strings and dates never break
 * inside a token; an id may break anywhere. */
/* `#spa-provenance` scoped, so the older `.prov dl` grid further up this
 * file, which `web/explorer-views.js`'s `.pol` list still matches, does not
 * lay these fields out in two columns of its own. */
#spa-provenance .prov-grid { display: block; margin: 0; }
.prov-field {
  display: grid;
  grid-template-columns: minmax(12ch, 18ch) minmax(0, 1fr);
  gap: var(--sp-3);
  padding: 7px 0;
  border-bottom: 1px solid var(--border);
}
.prov-field dt { color: var(--muted); }
.prov-field dd {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  min-width: 0;
  margin: 0;
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
.prov-value { white-space: nowrap; }
.prov-value.is-id {
  max-width: 100%;
  white-space: normal;
  overflow-wrap: anywhere;
}
.prov-value.is-unknown { color: var(--muted); }
@media (max-width: 520px) {
  .prov-field { grid-template-columns: minmax(0, 1fr); gap: 2px; }
}

/* The age beside the collected time, and its band. */
.prov-age {
  font-family: var(--font-sans);
  font-size: var(--fs-caption);
  color: var(--muted);
  white-space: nowrap;
}
.prov-age.is-ageing,
.prov-age.is-stale {
  padding: 0 6px;
  border-radius: var(--radius-chip);
}
.prov-age.is-ageing { color: var(--orange-ink); background: var(--orange-a2); }
.prov-age.is-stale { color: var(--red-700); background: var(--red-a); }

/* Sources, then runs. */
.prov-sources { margin: 0; padding: 0; list-style: none; }
.prov-sources li {
  display: flex;
  flex-wrap: wrap;
  gap: 2px var(--sp-3);
  padding: 5px 0;
  border-bottom: 1px solid var(--border);
}
.prov-source {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  color: var(--text-primary);
}
.prov-source-ds,
.prov-sources-none { color: var(--muted); font-size: var(--fs-caption); }
.prov-sources-none { margin: 0; }
.prov-runset-summary {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-caption);
  color: var(--muted);
}
.prov-runset {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 10px 0 0;
  padding: 0;
  list-style: none;
}
.prov-run {
  padding: 8px 10px;
  background: var(--plane);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.prov-run-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 10px;
}
.prov-run-time {
  font-weight: 500;
  color: var(--text-primary);
  white-space: nowrap;
}
.prov-run-head .prov-mono { overflow-wrap: anywhere; }
.prov-run-source { font-size: var(--fs-caption); color: var(--muted); }
.prov-run-none { color: var(--orange-500); font-family: var(--font-mono); }
.prov-run-note { font-size: var(--fs-caption); color: var(--muted); }
.prov-run-ds { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.prov-chip-id {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  height: 18px;
  padding: 0 5px;
  border-radius: var(--radius-chip);
  background: var(--surface-1);
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  font-weight: 500;
}

/* Buttons. A disclosure carries a chevron that turns while open, and its
 * label says Hide. */
.prov-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: var(--sp-5);
}
.prov-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 28px;
  padding: 0 10px;
  font: inherit;
  font-size: var(--fs-body);
  color: var(--text-secondary);
  background: var(--raised);
  border: 1px solid var(--control-border);
  border-radius: var(--radius);
  cursor: pointer;
}
.prov-btn:hover { background: var(--surface-1); }
.prov-btn:active { background: var(--steel-200); }
.prov-btn:focus-visible {
  outline: none;
  border-color: var(--blue-600);
  box-shadow: 0 0 0 3px var(--blue-a);
}
.prov-disclosure .ic { transition: transform .15s ease; }
.prov-disclosure[aria-expanded="true"] .ic { transform: rotate(90deg); }

/* A copy button keeps its width through "Copied". Its three labels
 * share one grid cell and only one is visible. */
.prov-copybtn { display: inline-grid; place-items: center; }
.prov-copybtn > span { grid-area: 1 / 1; }
.prov-copybtn .cb-done,
.prov-copybtn .cb-fail,
.prov-copybtn.is-done .cb-label,
.prov-copybtn.is-fail .cb-label { visibility: hidden; }
.prov-copybtn.is-done .cb-done,
.prov-copybtn.is-fail .cb-fail { visibility: visible; }
.prov-copy-value {
  height: 22px;
  font-family: var(--font-sans);
  font-size: var(--fs-caption);
}

/* Print. The PRINT anchor forces `#spa-provenance [hidden]` visible, so the
 * panel prints in the flow, the run list prints open, and the backdrop and
 * the controls do not print at all. */
@media print {
  #spa-provenance .prov-panel {
    position: static;
    width: auto;
    overflow: visible;
    padding: 0 0 var(--sp-4);
    box-shadow: none;
    border: 0;
  }
  #spa-provenance .prov-runset[hidden] { display: flex !important; }
  #spa-provenance .prov-backdrop,
  #spa-provenance .prov-backdrop[hidden] { display: none !important; }
  #spa-provenance .prov-toggle,
  #spa-provenance .prov-btn { display: none; }
  #spa-provenance .prov-panel-head { position: static; }
}

/* The per-figure flag: a figure whose source collected only partly. */
.fig-flag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  align-self: flex-start;
  margin: 0 0 6px;
  padding: 1px 7px;
  font-size: var(--fs-caption);
  font-weight: 500;
  color: var(--orange-ink);
  background: var(--orange-a2);
  border: 1px solid var(--orange-100);
  border-radius: var(--radius-chip);
}

/* A heading that is in the outline and not on screen. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
/* The footer while a route waits on its own response
 * (`clearProvenance`): hidden, so no empty strip draws. */
#spa-provenance[hidden] { display: none; }
/* --- END: PROVENANCE ---------------------------------------------------- */

/* On paper nothing scrolls, so a header carried down by the last
 * screen's scroll position prints in its own place. `!important` because
 * the translation is written through the CSSOM. */
@media print {
  [data-table] thead th { transform: none !important; }
}

/* The Run now control, above the schedule clock's producer table.
 * `.cycle-control[hidden]` covers the load before the first `GET
 * /cycle/state` answers, and the state a fetch failure leaves it in. */
.cycle-control { margin:0 0 16px; padding:14px 16px; border:1px solid var(--border);
  border-radius:10px; background:var(--raised); }
.cycle-control[hidden] { display:none; }
.cycle-control .sub { margin:8px 0 0; }
button.cycle-run-now { padding:7px 13px; font:inherit; font-size:12.5px;
  font-weight:500; cursor:pointer; border:1px solid var(--seq-450);
  border-radius:7px; background:var(--surface-1); color:var(--seq-450); }
button.cycle-run-now:hover { background:var(--grid); }
button.cycle-run-now:disabled { color:var(--muted); border-color:var(--border);
  cursor:default; }

/* The Collection schedule clock's dial. Every colour and geometry
 * value that is a figure rides on an SVG presentation attribute (`d`,
 * `fill`, `stroke`, ...) written into the markup, never `style="..."`:
 * CSP's `default-src 'self'` blocks the `style` attribute, not an SVG
 * attribute whose value happens to be `var(...)`. Only the constant
 * presentation below is here.
 *
 * Two renderings of one layout. The wide one carries each arc's pointer
 * and label beside the dial and shows once the widget is wide enough for
 * 12px labels at scale; below that the narrow one shows the dial alone
 * with its labels as the key list under it. */
.am-clock-widget { container-type:inline-size; }
.am-clock-heading {
  margin:0 0 4px; font-size:13px; font-weight:600;
  color:var(--text-secondary);
}
.am-clock-frame { margin:0 0 16px; }
.am-clock { display:block; width:100%; height:auto; }
.am-clock-wide { display:none; max-width:1040px; }
.am-clock-narrow { max-width:420px; margin:0 auto; }
.am-clock-hourlabel {
  fill:var(--muted); font-size:10px; font-family:var(--font-mono);
}
.am-clock-readout { fill:var(--text-secondary); font-size:13px; }
.am-clock-readout-now {
  fill:var(--text-primary); font-size:17px; font-weight:600;
  font-family:var(--font-mono);
}
.am-clock-readout-day, .am-clock-readout-job {
  font-size:12px; font-family:var(--font-mono); fill:var(--muted);
}
.am-clock-label { font-size:12px; fill:var(--text-primary); }
.am-clock-label-name { font-weight:600; }
.am-clock-label-time { font-family:var(--font-mono); font-size:11px; }
.am-clock-label-systems { fill:var(--text-secondary); font-size:11px; }
.am-clock-run { cursor:pointer; transition:opacity .12s; }
.am-clock-run a { text-decoration:none; }
.am-clock-key {
  list-style:none; margin:8px 0 0; padding:0;
  display:grid; grid-template-columns:repeat(auto-fill, minmax(280px, 1fr));
  gap:4px 16px; font-size:13px;
}
.am-clock-key li { display:flex; align-items:center; gap:6px; }
.am-clock-key-systems { color:var(--text-secondary); font-size:12px; }
.am-clock-swatch { width:14px; height:14px; flex:none; }
/* After the key's own rules, which it overrides at the same specificity. */
@container (min-width: 860px) {
  .am-clock-wide { display:block; }
  .am-clock-narrow, .am-clock-key { display:none; }
}
.am-link-off { display:none; }
.am-clock-hour.am-link-off { display:inline; fill:var(--raised); }
.am-link-hot .am-clock-run:not(.is-hot),
.am-link-hot .am-clock-key li:not(.is-hot) { opacity:.25; }
.am-link-hot tr.is-hot > td { background:var(--surface-1); }
.am-clock-lede { margin:0 0 8px; }

/* ------------------------------------------------------ assertion detail
 *
 * The claim callout, the details card, chip rows and icon links on
 * `#/assertion/:id`. The shared `.estate` and `h2:has(+ .sub)` rules are
 * above and reach every page. The sidebar is kept everywhere, so `#app`
 * carries no rule of its own for this page. */

/* §3.1: the info icon and the text sit in a shared flex row, the same
 * pairing `.prov-disagree` already gives `role="note"` and `icon("info")`
 * a few hundred lines above. `--seq-700` ink: darker than
 * `--text-primary` in light, not a quieting tint, so it does not run
 * against §1.1's "never tint body text to make it quieter". */
.claim-callout {
  display:flex; gap:10px; align-items:flex-start;
  background:var(--blue-a); border:1px solid var(--blue-100);
  border-radius:var(--radius); padding:var(--sp-4) var(--sp-5);
  color:var(--seq-700); font-size:var(--fs-nav); line-height:1.5;
  margin:0 0 var(--sp-6);
}
.claim-callout > .ic {
  flex:none; width:16px; height:16px; margin-top:2px; color:var(--blue-600);
}
.claim-callout .cc-body {
  display:flex; flex-direction:column; gap:10px; min-width:0;
}
.claim-callout .cc-lead { margin:0; }
/* 4px from the eyebrow to its own content, not the body's 10px
 * flex gap every other pair of children gets. The gap still applies (flex
 * gap is not a per-pair value), so the negative margin below only takes
 * back the 6px of it this one pair does not want. */
.claim-callout .cc-label {
  margin:0 0 -6px; font-size:var(--fs-eyebrow); font-weight:500;
  letter-spacing:.09em; text-transform:uppercase; color:var(--blue-700);
}
.claim-callout .cc-measured {
  margin:0; padding-left:18px; display:flex; flex-direction:column; gap:2px;
}
.claim-callout .cc-none { margin:0; }

/* §3.2: `overflow:hidden` clips the card, so `.dc-head` needs no top radius
 * of its own. The grid carries no gap of its own: each `dt`/`dd` is padded to
 * `--sp-3 --sp-5` (8px 16px) and separated by its own `--divider` bottom
 * border, so a row's label and value share one visual block and the border
 * falls between one row and the next, at every width. */
.detail-card {
  background:var(--raised); border:1px solid var(--border);
  border-radius:var(--radius); overflow:hidden;
}
.detail-card .dc-head {
  font-size:var(--fs-nav); font-weight:500; color:var(--text-primary);
  background:var(--surface-1); padding:10px 16px;
  border-bottom:1px solid var(--border);
}
.detail-card dl {
  margin:0; display:grid; grid-template-columns:168px minmax(0,1fr);
}
.detail-card dt {
  margin:0; color:var(--text-secondary); font-weight:400;
  font-size:var(--fs-nav); line-height:1.45;
  padding:var(--sp-3) var(--sp-5);
  border-bottom:1px solid var(--divider);
}
.detail-card dd {
  margin:0; display:flex; flex-wrap:wrap; gap:6px; align-items:center;
  font-size:var(--fs-nav); line-height:1.45;
  padding:var(--sp-3) var(--sp-5);
  border-bottom:1px solid var(--divider);
}
.detail-card dt:last-of-type, .detail-card dd:last-of-type {
  border-bottom:none;
}
/* The floor caveat and the no-instance reason are `.sub` spans inside a
 * `dd`; as flex items they would otherwise keep `.sub`'s own
 * `margin:0 0 24px`. */
.detail-card dd > .sub { margin:0; }
/* One column: the divider moves to the value, so it still falls between
 * one row's value and the next row's label, not above the label. */
@media (max-width:480px) {
  .detail-card dl { grid-template-columns:1fr; }
  .detail-card dt {
    font-weight:600; border-bottom:none; padding:var(--sp-3) var(--sp-5) 0;
  }
  .detail-card dd {
    border-bottom:1px solid var(--divider); padding:0 var(--sp-5) var(--sp-3);
  }
  .detail-card dd:last-of-type { border-bottom:none; }
}

/* Chips take the 20px pill radius style
 * guide §1.3 now gives every chip, badge and status pill, and lose their
 * border. `.chip.neutral` is a row status. `.chip.sys` is a system,
 * mono. `.chip.ctl` is a control reference and a link to the controls
 * page, blue on `--blue-a`, `--blue-100` on hover.
 * `.chip.clause` is a clause reference: never a link, so it stays grey,
 * on `--surface-1`, the same fill `.chip.neutral` and `.chip.sys` already
 * use. A literal `--steel-100` fill would read correctly in light but
 * vanish in dark, where `--raised` is also `--steel-100`. The `--divider`
 * token met the same collision. */
.chip {
  display:inline-flex; align-items:center; font-size:11.5px;
  padding:2px 8px; border-radius:var(--radius-pill); white-space:nowrap;
}
a.chip { text-decoration:none; }
.chip.neutral {
  font-weight:500; color:var(--text-secondary); background:var(--surface-1);
}
.chip.sys {
  font-family:var(--font-mono); font-size:var(--fs-mono); line-height:1.5;
  color:var(--text-primary); background:var(--surface-1);
}
.chip.ctl, .chip.asr {
  font-family:var(--font-mono); color:var(--seq-700); background:var(--blue-a);
}
a.chip.ctl:hover, a.chip.asr:hover { background:var(--blue-100); }
/* An assertion lozenge (`.chip.asr`, styled with `.chip.ctl` above): a link
 * to `#/assertion/<id>` on a register table screen's "Assertions that read
 * this table" row. */
.re-readers {
  display:flex; flex-wrap:wrap; align-items:center; gap:6px;
}
.re-readers-label { font-size:12px; color:var(--text-secondary); }
.re-readers-note { font-size:12px; color:var(--text-primary); }
.chip.clause {
  font-family:var(--font-mono); color:var(--seq-700); background:var(--surface-1);
}

/* A badge with a tooltip (§3.3). The cursor invites the pointer. The badge
 * takes no focus and gets no focus ring, because it is not a control. */
.badge.has-tip { cursor:help; }

/* Icon links (§3.5): the icon beside its word, never
 * alone, per style guide §6. 28px so the link is a comfortable target on
 * touch, 16px icon, `--text-secondary` at rest so it reads quieter than
 * the row it sits in, `--seq-450` and a `--surface-1` wash on hover. */
.row-links { display:inline-flex; gap:12px; }
.icon-link {
  display:inline-flex; align-items:center; gap:2px; height:28px;
  padding:0 8px; border-radius:var(--radius); color:var(--text-secondary);
  text-decoration:none;
}
.icon-link:hover { background:var(--surface-1); color:var(--seq-450); }
.icon-link .ic { width:16px; height:16px; }

/* The load indicator. A thin indeterminate bar across the top of
 * `#spa-scroll`, shown only once a load passes 200 ms. It
 * is Steel: the moving segment is `--steel-600` on a `--steel-200` track, and
 * both tokens are redefined in the dark blocks. Sticky with a negative margin
 * as tall as itself, so it overlays the content and never moves it. It has no
 * width to set: the segment's sweep is this animation, and nothing on it is a
 * figure. The stage line it accompanies is a `.sub` paragraph and needs no
 * rule of its own. */
.load-bar {
  position: sticky;
  top: 0;
  z-index: 5;
  height: 3px;
  margin-bottom: -3px;
  overflow: hidden;
  background: var(--steel-200);
}
.load-bar[hidden] { display: none; }
.load-bar::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 30%;
  background: var(--steel-600);
  animation: load-bar-sweep 1.2s ease-in-out infinite;
}
@keyframes load-bar-sweep {
  from { transform: translateX(-100%); }
  to { transform: translateX(334%); }
}
/* No motion under reduced motion. A still segment would read as a
 * part-filled bar, which is a figure nothing measured, so the track alone
 * stays and the stage line carries the words. */
@media (prefers-reduced-motion: reduce) {
  .load-bar::before { animation: none; display: none; }
}
@media print {
  .load-bar { display: none; }
}

/* The rebuild banner, drawn above the content from a register import's
 * answer until the first read after it returns. Sticky so it stays in view
 * while the load stage line below it counts seconds. */
.rebuild-banner {
  position: sticky;
  top: 0;
  z-index: 4;
  padding: 10px 14px;
  background: var(--blue-a);
  border-bottom: 1px solid var(--blue-100);
  font-size: 13px;
  line-height: 1.4;
  color: var(--blue-700);
}
@media print {
  .rebuild-banner { display: none; }
}

/* --- ANCHOR: REGISTER EDITOR ----------------------------------------
 * The shared register editor, first configured for `#/role-tiers`.
 * Every colour is a Steel token. Column
 * widths are one modifier class per table, so no figure goes inline. */
.re-editor { display:flex; flex-direction:column; gap:var(--sp-5); }
.re-editor.re-panel-open { padding-right:calc(var(--panel-w) + var(--sp-2)); }
.re-head { display:flex; flex-direction:column; gap:6px; }
.re-head h1 { margin:0; }
.re-desc { margin:0; max-width:820px; font-size:13px; line-height:1.5;
  color:var(--text-secondary); }

.re-banner { display:flex; align-items:center; gap:var(--sp-4); flex-wrap:wrap;
  padding:10px 14px; background:var(--blue-a); border:1px solid var(--blue-100);
  border-radius:var(--radius); font-size:13px; line-height:1.4;
  color:var(--blue-700); }
.re-banner > .ic { width:16px; height:16px; flex:none; color:var(--blue-600); }
.re-banner-text { flex:1; min-width:200px; }
.re-banner-unmeasured { background:var(--plane); border-color:var(--border);
  color:var(--text-secondary); }
.re-banner-unmeasured > .ic { color:var(--warning); }

.re-btn { height:var(--control-h); padding:0 14px; display:inline-flex;
  align-items:center; gap:6px; border:1px solid var(--control-border);
  border-radius:var(--radius); background:var(--raised);
  color:var(--text-primary); font:500 13px/1 var(--font-sans); cursor:pointer;
  white-space:nowrap; }
.re-btn:hover { background:var(--surface-1); }
.re-btn:disabled { opacity:.5; cursor:not-allowed; }
.re-btn:disabled:hover { background:var(--raised); }
.re-btn .ic { width:15px; height:15px; }
.re-btn-primary { padding:0 14px 0 10px; background:var(--blue-600);
  border-color:var(--blue-600); color:var(--raised); }
.re-btn-primary:hover { background:var(--blue-700); border-color:var(--blue-700); }
.re-btn-primary:disabled:hover { background:var(--blue-600); }
.re-panel-foot .re-btn-primary { padding:0 14px; }
.re-btn-quiet { height:28px; padding:0 10px; border-color:var(--blue-600);
  color:var(--blue-600); font-size:12.5px; }
.re-btn-quiet:hover { background:var(--blue-100); }
.re-icon-btn { width:32px; height:32px; display:inline-flex; align-items:center;
  justify-content:center; border:0; border-radius:var(--radius);
  background:transparent; color:var(--text-secondary); cursor:pointer; }
.re-icon-btn:hover { background:var(--surface-1); color:var(--blue-600); }
.re-icon-btn:disabled { color:var(--steel-300); cursor:not-allowed; }
.re-icon-btn:disabled:hover { background:transparent; color:var(--steel-300); }
.re-icon-btn .ic { width:15px; height:15px; }

.re-card { background:var(--raised); border:1px solid var(--border);
  border-radius:var(--radius); display:flex; flex-direction:column; }
.re-toolbar { display:flex; align-items:center; gap:var(--sp-4); flex-wrap:wrap;
  padding:var(--sp-4) var(--sp-5); border-bottom:1px solid var(--border); }
/* The import panel's header line is one unbroken token; it wraps at
   any character so a narrow panel holds it. */
.re-import-detail code { overflow-wrap:anywhere; }
/* The no-store panel's lone Import, outside any card. */
.re-toolbar-bare { border-bottom:0; padding:var(--sp-4) 0 0; }
/* The import's file picker: the native input stays focusable but unseen,
   and the chosen file's name is drawn from the draft. */
.re-file-pick { position:relative; display:flex; align-items:center; gap:10px; }
.re-file-input { position:absolute; width:1px; height:1px; opacity:0;
  overflow:hidden; }
.re-file-pick:focus-within .re-btn { border-color:var(--blue-600); }
.re-file-name { color:var(--text-secondary); font-size:13px;
  overflow-wrap:anywhere; }
.re-search { position:relative; display:flex; align-items:center; }
.re-search > .ic { position:absolute; left:10px; width:14px; height:14px;
  color:var(--steel-500); pointer-events:none; }
.re-input { height:var(--control-h); padding:0 10px; border:1px solid var(--control-border);
  border-radius:var(--radius); background:var(--raised); color:var(--text-primary);
  font:400 13px/1 var(--font-sans); }
.re-input:focus { border-color:var(--blue-600); outline:none; }
.re-search .re-input { width:260px; padding-left:30px; }
textarea.re-input { height:auto; min-height:104px; padding:8px 10px;
  line-height:1.5; resize:vertical; }
select.re-input { padding-right:6px; cursor:pointer; }
.re-group-filter { display:flex; min-width:0; width:320px; max-width:100%; }
.re-group-filter .re-input { width:100%; min-width:0; }
.re-input.re-readonly { background:var(--surface-1); color:var(--text-secondary);
  cursor:default; }
.re-input.re-invalid { border-color:var(--red-600); }
.re-seg { display:flex; border:1px solid var(--control-border); border-radius:var(--radius);
  overflow:hidden; }
.re-seg button { height:30px; padding:0 12px; display:flex; align-items:center;
  flex:none; white-space:nowrap;
  gap:6px; border:0; border-right:1px solid var(--control-border); background:var(--raised);
  color:var(--text-primary); font:500 12.5px/1 var(--font-sans); cursor:pointer; }
.re-seg button:last-child { border-right:0; }
.re-seg button:hover { background:var(--surface-1); }
.re-seg button[aria-pressed="true"] { background:var(--blue-100); color:var(--blue-700); }
.re-seg-count { font:400 var(--fs-mono)/1 var(--font-mono);
  color:var(--text-secondary); }
.re-toggle { height:var(--control-h); padding:0 12px; display:flex;
  align-items:center; gap:6px; border:1px solid var(--control-border);
  border-radius:var(--radius); background:var(--raised); color:var(--text-primary);
  font:500 12.5px/1 var(--font-sans); cursor:pointer; }
.re-toggle:hover { background:var(--surface-1); }
.re-toggle[aria-pressed="true"] { background:var(--blue-100); border-color:var(--blue-600); }
.re-toggle .ic { width:14px; height:14px; }
.re-showing { font-size:12px; color:var(--text-secondary); }
.re-add { margin-left:auto; position:relative; }

.re-picker { position:absolute; right:0; top:38px; z-index:30; width:400px;
  max-width:calc(100vw - 32px); display:flex; flex-direction:column;
  background:var(--raised); border:1px solid var(--border);
  border-radius:var(--radius); box-shadow:var(--shadow); }
.re-banner .re-picker { top:34px; }
.re-picker-head { padding:12px 12px 8px; display:flex; flex-direction:column; gap:8px; }
.re-picker-head .re-input { border-color:var(--blue-600); }
.re-picker-list { max-height:320px; overflow-y:auto; padding:4px 0;
  border-top:1px solid var(--surface-1); }
.re-option { width:100%; min-height:32px; padding:6px 12px; display:flex;
  align-items:center; justify-content:space-between; gap:12px; border:0;
  background:var(--raised); text-align:left; color:var(--text-primary);
  font:400 13px/1.35 var(--font-sans); cursor:pointer; }
.re-option-hint { font-size:11.5px; font-weight:500; color:var(--blue-600);
  visibility:hidden; }
.re-option-active, .re-option:hover { background:var(--surface-1); }
.re-option-active .re-option-hint, .re-option:hover .re-option-hint { visibility:visible; }
.re-picker-empty { padding:12px; font-size:12.5px; line-height:1.5;
  color:var(--text-secondary); }
.re-picker-foot { padding:8px 12px; border-top:1px solid var(--surface-1);
  font-size:11.5px; line-height:1.4; color:var(--text-secondary); }

.re-eyebrow { font:500 var(--fs-eyebrow)/1 var(--font-sans); letter-spacing:.09em;
  text-transform:uppercase; color:var(--text-secondary); }

.re-scroll { overflow-x:auto; container:re-grid / inline-size; }
.re-grid { min-width:820px; }
.re-grid-role-tiers .re-row, .re-grid-role-tiers .re-grid-head {
  grid-template-columns:28px 190px 172px minmax(180px,1fr) 140px 128px 44px; }
/* Vocabularies are member, match, rationale and status; permission
 * tiers keep role tiers' seven columns, the role cell holding the value. */
.re-grid-vocabularies .re-row, .re-grid-vocabularies .re-grid-head {
  grid-template-columns:28px 230px 128px minmax(220px,1fr) 128px 44px; }
.re-grid-permission-tiers .re-row, .re-grid-permission-tiers .re-grid-head {
  grid-template-columns:28px 230px 172px minmax(180px,1fr) 140px 128px 44px; }
/* The cloud tier tables keep role tiers' seven columns. A policy key may
 * be a document's digest, which breaks anywhere to fit its column. */
.re-grid-aws-policy-tiers .re-row, .re-grid-aws-policy-tiers .re-grid-head {
  grid-template-columns:28px 300px 172px minmax(180px,1fr) 140px 128px 44px; }
.re-grid-aws-policy-tiers .re-cell-key { overflow-wrap:anywhere; }
.re-grid-gcp-role-tiers .re-row, .re-grid-gcp-role-tiers .re-grid-head {
  grid-template-columns:28px 260px 172px minmax(180px,1fr) 140px 128px 44px; }
/* The permission set tiers rows below the AWS table, and the Tiers
 * landing's badges and links. */
.re-below { margin-top:32px; }
.re-below-title { display:flex; align-items:center; gap:12px; margin:0 0 4px;
  font-size:16px; }
.re-below-rows { display:inline-flex; align-items:center; gap:4px;
  font-size:12px; font-weight:400; }
.tiers-definition, .tiers-links { list-style:none; margin:0 0 24px; padding:0; }
.tiers-definition { display:flex; flex-wrap:wrap; gap:8px; }
.tiers-links li { margin:0 0 8px; }
/* Job functions are key, kind, rationale and status. The chevron
 * column stays empty, so the key lines up with the other tables'. The key
 * column is wider for an entitlement's system, route and id. */
.re-grid-job-functions .re-row, .re-grid-job-functions .re-grid-head {
  grid-template-columns:28px 280px 128px minmax(220px,1fr) 128px 44px; }
/* The three proposal tables share one grid: the row's name, a short
 * cell, the long text, a second text or the checks, and the status. */
.re-grid-proposals .re-row, .re-grid-proposals .re-grid-head {
  grid-template-columns:28px 220px 140px minmax(180px,1fr) 160px 150px 44px; }
/* Scopes are the version's address, the instances that use it, the
 * predicate and the status. The predicate takes the widest column, since
 * one runs to a few hundred characters. */
.re-grid-scopes .re-row, .re-grid-scopes .re-grid-head {
  grid-template-columns:28px 200px minmax(160px,1fr) minmax(240px,1.5fr) 150px 44px; }
/* Activity is the time, the table, the kind, who and which rows, and
 * Confirm. The grid has no panel, so it needs no docked layout. */
.re-grid-activity .re-row, .re-grid-activity .re-grid-head {
  grid-template-columns:28px 76px 110px minmax(0,1fr) minmax(0,1.5fr) 88px 44px; }
.re-grid.re-grid-activity { min-width:640px; }
.re-grid-activity .re-cell-text { min-width:0; overflow-wrap:anywhere; }
.act-role { display:block; color:var(--text-secondary); font-size:12px; margin:4px 0; }
.act-touched { display:flex; flex-wrap:wrap; gap:4px 8px; margin-top:4px; }
.act-rows { display:grid; grid-template-columns:repeat(2, minmax(0,1fr));
  gap:12px; }
.act-rows > div { display:flex; flex-direction:column; gap:4px; min-width:0; }
.act-row-json { margin:0; max-height:240px; overflow:auto;
  font:400 12px/1.5 var(--font-mono); color:var(--steel-800); }
@media (max-width: 700px) { .act-rows { grid-template-columns:minmax(0,1fr); } }
.re-cell-predicate { min-width:0; }
.re-cell-predicate code { font:400 12px/1.5 var(--font-mono);
  color:var(--steel-800); overflow-wrap:anywhere; }
.re-scope-use { display:block; }
/* The scopes panel's two readings: the current funnel, and the
 * candidate with its Preview button. */
.re-scope-block { display:flex; flex-direction:column; gap:8px; min-width:0; }
.re-scope-block-head { display:flex; align-items:center;
  justify-content:space-between; gap:12px; }
.re-scope-reading { min-width:0; overflow-x:auto; }
.re-grid-head { display:grid; align-items:center; background:var(--surface-1);
  border-bottom:1px solid var(--border); font:500 12px/1 var(--font-sans);
  color:var(--steel-800); }
.re-grid-head > span { padding:10px 12px; }
.re-grid-head > span:nth-child(2) { padding-left:0; }
.re-group { display:flex; align-items:center; gap:8px; padding:8px 16px;
  background:var(--plane); border-bottom:1px solid var(--surface-1);
  font:500 11px/1 var(--font-sans); letter-spacing:.06em; text-transform:uppercase;
  color:var(--text-secondary); }
.re-group-count { font:400 var(--fs-mono)/1 var(--font-mono); letter-spacing:0; }
.re-group-key { font:400 var(--fs-mono)/1 var(--font-mono); letter-spacing:0;
  text-transform:none; color:var(--text-secondary); }
.re-group-note { font-weight:400; letter-spacing:0; text-transform:none;
  color:var(--text-secondary); }
.re-subgroup { padding-left:40px; background:var(--raised); }
.re-match { display:inline-flex; padding:1px 8px; border:1px solid var(--steel-300);
  border-radius:20px; font-size:11.5px; font-weight:500; line-height:1.5;
  color:var(--steel-800); white-space:nowrap; }
.re-key-note { display:block; margin-top:2px; font-size:12px; font-weight:400;
  color:var(--text-secondary); }
.re-dot { width:8px; height:8px; border-radius:50%; flex:none;
  background:var(--steel-300); }
.re-dot-critical { background:var(--critical); }
.re-dot-serious { background:var(--serious); }
.re-dot-warning { background:var(--warning); }
.re-row { display:grid; align-items:start; border-bottom:1px solid var(--surface-1);
  background:var(--raised); }
.re-row-selected { background:var(--blue-a);
  box-shadow:inset var(--selected-bar) 0 0 var(--blue-600); }
.re-cell { padding:10px 12px; font-size:13px; line-height:1.45; }
.re-cell-chevron { padding:10px 0 0 8px; }
.re-cell-key { padding:12px 12px 12px 0; font-weight:500; line-height:1.4;
  color:var(--text-primary); overflow-wrap:anywhere; }
.re-cell-pill { padding:9px 12px; }
.re-cell-text { color:var(--steel-800); }
.re-cell-flags { padding:9px 12px; display:flex; flex-direction:column; gap:6px;
  align-items:flex-start; }
.re-cell-status { padding:12px; font-size:12.5px; line-height:1.4;
  color:var(--text-secondary); display:flex; flex-direction:column; gap:4px;
  align-items:flex-start; }
.re-cell-action { padding:6px 8px 0 0; display:flex; justify-content:flex-end; }
.re-editor .badge { font-weight:500; line-height:1.5; }
.re-editor .badge.info { border-color:var(--steel-300); }
.re-editor .badge .ic { width:12px; height:12px; }
button.badge { background:var(--raised); color:var(--steel-800); font:inherit;
  font-size:11.5px; cursor:pointer; }
button.badge:hover { border-color:var(--blue-600); color:var(--blue-600); }
.re-pill-saved { display:inline-flex; align-items:center; padding:1px 9px;
  border-radius:20px; background:var(--green-a2); color:var(--green-700);
  font-size:11.5px; font-weight:500; line-height:1.5; }
/* A proposal's pill in Status. A proposed add or change is blue, the
 * selection's own hue; a removal is red; a superseded proposal is neutral,
 * since it can no longer be confirmed. */
.re-pill-proposal { display:inline-flex; align-items:center; padding:1px 9px;
  border-radius:20px; background:var(--blue-100); color:var(--blue-700);
  font-size:11.5px; font-weight:500; line-height:1.5; }
.re-pill-proposal.re-pill-remove { background:var(--red-a); color:var(--red-700); }
.re-pill-proposal.re-pill-superseded { background:var(--surface-1);
  color:var(--text-secondary); }
.re-row-proposed { background:var(--blue-a); }
.re-import { margin-left:auto; }
.re-import + .re-add { margin-left:0; }
/* The review panel's facts and its before-and-after table. */
.re-facts { margin:0; display:grid; grid-template-columns:auto minmax(0,1fr);
  gap:6px 16px; font-size:13px; line-height:1.4; }
.re-facts dt { color:var(--text-secondary); }
.re-facts dd { margin:0; color:var(--text-primary); overflow-wrap:anywhere; }
.re-diff { width:100%; border-collapse:collapse; font-size:12.5px; line-height:1.4;
  table-layout:fixed; }
.re-diff th, .re-diff td { padding:6px 8px; border-bottom:1px solid var(--surface-1);
  text-align:left; vertical-align:top; }
.re-diff th { white-space:normal; overflow-wrap:anywhere; }
.re-diff thead th:first-child { width:34%; }
/* A value wraps at a word where it can; `anywhere` broke words mid-way at
   375. The fixed layout holds the table to the panel, so a word wider than
   its column still breaks. */
.re-diff td { overflow-wrap:break-word; }
.re-diff thead th { font-weight:500; color:var(--text-secondary); }
.re-diff tbody th { font-weight:500; color:var(--steel-800); }
.re-diff-changed td:last-child { background:var(--blue-a); color:var(--blue-700); }
.re-chevron { width:22px; height:22px; display:inline-flex; align-items:center;
  justify-content:center; border:0; border-radius:var(--radius);
  background:transparent; color:var(--text-secondary); cursor:pointer; }
.re-chevron:hover { background:var(--surface-1); }
.re-chevron .ic { width:14px; height:14px; transition:transform 120ms ease-out; }
.re-chevron[aria-expanded="true"] .ic { transform:rotate(90deg); }
.re-expander { grid-column:2 / -1; margin:0 12px 12px 0; padding:10px 14px;
  display:flex; flex-direction:column; gap:4px; background:var(--plane);
  border:1px solid var(--surface-1); border-radius:var(--radius); }
.re-expander-text { font-size:13px; line-height:1.5; color:var(--steel-800); }
.re-empty { padding:24px 16px; font-size:13px; line-height:1.5;
  color:var(--text-secondary); }

.re-panel { position:fixed; z-index:35; top:0; right:0; bottom:0;
  width:var(--panel-w); display:flex; flex-direction:column;
  background:var(--raised); border-left:1px solid var(--border); }
.re-panel-head { flex:none; display:flex; align-items:flex-start;
  justify-content:space-between; gap:12px; padding:16px 20px;
  border-bottom:1px solid var(--border); }
.re-panel-title { flex:1; display:flex; flex-direction:column; gap:6px; min-width:0; }
.re-panel-name { margin:0; font:300 22px/1.2 var(--font-display);
  letter-spacing:-.01em; color:var(--text-primary); overflow-wrap:anywhere; }
.re-current { display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  font-size:12px; line-height:1.4; color:var(--text-secondary); }
.re-current-value { display:inline-flex; align-items:center; gap:8px;
  white-space:nowrap; }
.re-panel-body { flex:1; overflow-y:auto; padding:20px; display:flex;
  flex-direction:column; gap:20px; }
.re-fieldset { display:flex; flex-direction:column; gap:6px; min-width:0;
  margin:0; padding:0; border:0; }
.re-fieldset > legend { padding:0; margin-bottom:6px; }
.re-fieldset-flags { gap:10px; }
.re-label, .re-field { font:500 12px/1.3 var(--font-sans); color:var(--steel-800); }
.re-field { display:flex; flex-direction:column; gap:4px; }
.re-help { font-weight:400; color:var(--text-secondary); }
.re-radio-card { display:grid; grid-template-columns:16px minmax(0,1fr) auto;
  align-items:center; gap:10px; min-height:40px; padding:8px 12px;
  border:1px solid var(--border); border-radius:var(--radius);
  background:var(--raised); text-align:left; cursor:pointer; }
.re-radio-card:hover { border-color:var(--blue-600); }
.re-radio-card.re-invalid { border-color:var(--red-600); }
.re-radio-card[aria-checked="true"] { border-color:var(--blue-600); background:var(--blue-a); }
.re-ring { width:16px; height:16px; border-radius:50%;
  border:1.5px solid var(--steel-400); background:var(--raised); }
.re-radio-card[aria-checked="true"] .re-ring { border:5px solid var(--blue-600); }
.re-radio-label { display:flex; align-items:center; gap:8px; min-width:0;
  font:500 13px/1.3 var(--font-sans); color:var(--text-primary); }
.re-radio-label .ic { width:14px; height:14px; flex:none; }
.re-ink-critical { color:var(--critical); }
.re-ink-serious { color:var(--serious); }
.re-ink-warning { color:var(--warning); }
.re-ink-info { color:var(--text-secondary); }
.re-radio-count { font:400 var(--fs-mono)/1 var(--font-mono); color:var(--text-secondary); }
.re-check { display:grid; grid-template-columns:16px minmax(0,1fr); gap:10px;
  align-items:start; cursor:pointer; }
.re-check input { width:16px; height:16px; margin:1px 0 0; accent-color:var(--blue-600); }
.re-check-text { display:flex; flex-direction:column; gap:2px; }
.re-check-label { font:500 13px/1.3 var(--font-sans); color:var(--text-primary); }
.re-check .re-help { font-size:12px; line-height:1.45; }
.re-field-error { font-weight:400; color:var(--red-700); }
.re-field-error:empty { display:none; }
.re-panel-foot { flex:none; display:flex; align-items:center; gap:12px;
  flex-wrap:wrap; padding:12px 20px; border-top:1px solid var(--border);
  background:var(--plane); }
.re-summary { flex:1 1 0; min-width:0; font-size:12px; line-height:1.4;
  color:var(--text-secondary); }
.re-summary-error { color:var(--red-700); }
.re-server-errors { flex-basis:100%; margin:0; padding-left:16px; font-size:12px;
  line-height:1.4; color:var(--red-700); }
.re-save-failure { flex-basis:100%; }
.re-save-failure:empty { display:none; }
.re-foot-buttons { display:flex; gap:8px; }

.re-toast { position:fixed; z-index:40; left:50%; bottom:24px;
  transform:translateX(-50%); display:flex; align-items:center; gap:10px;
  max-width:calc(100vw - 32px); padding:10px 16px; background:var(--text-primary);
  color:var(--raised); border-radius:var(--radius); box-shadow:var(--shadow);
  font-size:13px; line-height:1.4; }
.re-toast .ic { width:16px; height:16px; flex:none; }

/* With the panel open, a grid narrower than its columns reflows each row
 * onto three lines: role, tier and edit; the rationale; flags and status. */
@container re-grid (max-width: 880px) {
  .re-panel-open .re-grid { min-width:0; }
  .re-panel-open .re-grid-role-tiers .re-row,
  .re-panel-open .re-grid-role-tiers .re-grid-head {
    grid-template-columns:28px minmax(0,1fr) auto 44px; }
  .re-panel-open .re-grid-role-tiers .re-grid-head > span:nth-child(n+4):nth-child(-n+6) {
    display:none; }
  .re-panel-open .re-grid-role-tiers .re-cell-chevron { grid-column:1; grid-row:1; }
  .re-panel-open .re-grid-role-tiers .re-cell-key { grid-column:2; grid-row:1; }
  .re-panel-open .re-grid-role-tiers .re-cell-pill { grid-column:3; grid-row:1; }
  .re-panel-open .re-grid-role-tiers .re-cell-action { grid-column:4; grid-row:1; }
  .re-panel-open .re-grid-role-tiers .re-cell-text { grid-column:2 / -1; grid-row:2;
    padding:0 12px 8px 0; }
  .re-panel-open .re-grid-role-tiers .re-cell-flags { grid-column:2; grid-row:3;
    flex-direction:row; flex-wrap:wrap; padding:0 12px 10px 0; }
  .re-panel-open .re-grid-role-tiers .re-cell-status { grid-column:3 / -1; grid-row:3;
    padding:0 12px 10px; }
  .re-panel-open .re-grid-role-tiers .re-expander { grid-row:4; }
  /* Vocabularies: member, match and edit; the rationale; the status. */
  .re-panel-open .re-grid-vocabularies .re-row,
  .re-panel-open .re-grid-vocabularies .re-grid-head {
    grid-template-columns:28px minmax(0,1fr) auto 44px; }
  .re-panel-open .re-grid-vocabularies .re-grid-head > span:nth-child(n+4):nth-child(-n+5) {
    display:none; }
  .re-panel-open .re-grid-vocabularies .re-cell-chevron { grid-column:1; grid-row:1; }
  .re-panel-open .re-grid-vocabularies .re-cell-key { grid-column:2; grid-row:1; }
  .re-panel-open .re-grid-vocabularies .re-cell-pill { grid-column:3; grid-row:1; }
  .re-panel-open .re-grid-vocabularies .re-cell-action { grid-column:4; grid-row:1; }
  .re-panel-open .re-grid-vocabularies .re-cell-text { grid-column:2 / -1; grid-row:2;
    padding:0 12px 8px 0; }
  .re-panel-open .re-grid-vocabularies .re-cell-status { grid-column:2 / -1; grid-row:3;
    padding:0 12px 10px 0; }
  .re-panel-open .re-grid-vocabularies .re-expander { grid-row:4; }
  /* Job functions reflow as vocabularies do. */
  .re-panel-open .re-grid-job-functions .re-row,
  .re-panel-open .re-grid-job-functions .re-grid-head {
    grid-template-columns:28px minmax(0,1fr) auto 44px; }
  .re-panel-open .re-grid-job-functions .re-grid-head > span:nth-child(n+4):nth-child(-n+5) {
    display:none; }
  .re-panel-open .re-grid-job-functions .re-cell-chevron { grid-column:1; grid-row:1; }
  .re-panel-open .re-grid-job-functions .re-cell-key { grid-column:2; grid-row:1; }
  .re-panel-open .re-grid-job-functions .re-cell-pill { grid-column:3; grid-row:1; }
  .re-panel-open .re-grid-job-functions .re-cell-action { grid-column:4; grid-row:1; }
  .re-panel-open .re-grid-job-functions .re-cell-text { grid-column:2 / -1; grid-row:2;
    padding:0 12px 8px 0; }
  .re-panel-open .re-grid-job-functions .re-cell-status { grid-column:2 / -1; grid-row:3;
    padding:0 12px 10px 0; }
  /* Permission tiers and the cloud tier tables reflow as role tiers do. */
  .re-panel-open .re-grid-permission-tiers .re-row,
  .re-panel-open .re-grid-aws-policy-tiers .re-row,
  .re-panel-open .re-grid-gcp-role-tiers .re-row,
  .re-panel-open .re-grid-permission-tiers .re-grid-head,
  .re-panel-open .re-grid-aws-policy-tiers .re-grid-head,
  .re-panel-open .re-grid-gcp-role-tiers .re-grid-head {
    grid-template-columns:28px minmax(0,1fr) auto 44px; }
  .re-panel-open .re-grid-permission-tiers .re-grid-head > span:nth-child(n+4):nth-child(-n+6),
  .re-panel-open .re-grid-aws-policy-tiers .re-grid-head > span:nth-child(n+4):nth-child(-n+6),
  .re-panel-open .re-grid-gcp-role-tiers .re-grid-head > span:nth-child(n+4):nth-child(-n+6) {
    display:none; }
  .re-panel-open .re-grid-permission-tiers .re-cell-chevron,
  .re-panel-open .re-grid-aws-policy-tiers .re-cell-chevron,
  .re-panel-open .re-grid-gcp-role-tiers .re-cell-chevron { grid-column:1; grid-row:1; }
  .re-panel-open .re-grid-permission-tiers .re-cell-key,
  .re-panel-open .re-grid-aws-policy-tiers .re-cell-key,
  .re-panel-open .re-grid-gcp-role-tiers .re-cell-key { grid-column:2; grid-row:1; }
  .re-panel-open .re-grid-permission-tiers .re-cell-pill,
  .re-panel-open .re-grid-aws-policy-tiers .re-cell-pill,
  .re-panel-open .re-grid-gcp-role-tiers .re-cell-pill { grid-column:3; grid-row:1; }
  .re-panel-open .re-grid-permission-tiers .re-cell-action,
  .re-panel-open .re-grid-aws-policy-tiers .re-cell-action,
  .re-panel-open .re-grid-gcp-role-tiers .re-cell-action { grid-column:4; grid-row:1; }
  .re-panel-open .re-grid-permission-tiers .re-cell-text,
  .re-panel-open .re-grid-aws-policy-tiers .re-cell-text,
  .re-panel-open .re-grid-gcp-role-tiers .re-cell-text { grid-column:2 / -1; grid-row:2;
    padding:0 12px 8px 0; }
  .re-panel-open .re-grid-permission-tiers .re-cell-flags,
  .re-panel-open .re-grid-aws-policy-tiers .re-cell-flags,
  .re-panel-open .re-grid-gcp-role-tiers .re-cell-flags { grid-column:2; grid-row:3;
    flex-direction:row; flex-wrap:wrap; padding:0 12px 10px 0; }
  .re-panel-open .re-grid-permission-tiers .re-cell-status,
  .re-panel-open .re-grid-aws-policy-tiers .re-cell-status,
  .re-panel-open .re-grid-gcp-role-tiers .re-cell-status { grid-column:3 / -1; grid-row:3;
    padding:0 12px 10px; }
  .re-panel-open .re-grid-permission-tiers .re-expander,
  .re-panel-open .re-grid-aws-policy-tiers .re-expander,
  .re-panel-open .re-grid-gcp-role-tiers .re-expander { grid-row:4; }
  /* The proposal tables reflow as role tiers do. */
  .re-panel-open .re-grid-proposals .re-row,
  .re-panel-open .re-grid-proposals .re-grid-head {
    grid-template-columns:28px minmax(0,1fr) auto 44px; }
  .re-panel-open .re-grid-proposals .re-grid-head > span:nth-child(n+4):nth-child(-n+6) {
    display:none; }
  .re-panel-open .re-grid-proposals .re-cell-chevron { grid-column:1; grid-row:1; }
  .re-panel-open .re-grid-proposals .re-cell-key { grid-column:2; grid-row:1; }
  .re-panel-open .re-grid-proposals .re-cell-pill { grid-column:3; grid-row:1; }
  .re-panel-open .re-grid-proposals .re-cell-action { grid-column:4; grid-row:1; }
  .re-panel-open .re-grid-proposals .re-cell-text { grid-column:2 / -1;
    padding:0 12px 8px 0; }
  .re-panel-open .re-grid-proposals .re-cell-flags { grid-column:2;
    flex-direction:row; flex-wrap:wrap; padding:0 12px 10px 0; }
  .re-panel-open .re-grid-proposals .re-cell-status { grid-column:2 / -1;
    padding:0 12px 10px 0; }
  /* Scopes: the address and edit; the instances; the predicate; the
   * status; the history. */
  .re-panel-open .re-grid-scopes .re-row,
  .re-panel-open .re-grid-scopes .re-grid-head {
    grid-template-columns:28px minmax(0,1fr) 44px; }
  .re-panel-open .re-grid-scopes .re-grid-head > span:nth-child(n+3):nth-child(-n+5) {
    display:none; }
  .re-panel-open .re-grid-scopes .re-cell-chevron { grid-column:1; grid-row:1; }
  .re-panel-open .re-grid-scopes .re-cell-key { grid-column:2; grid-row:1; }
  .re-panel-open .re-grid-scopes .re-cell-action { grid-column:3; grid-row:1; }
  .re-panel-open .re-grid-scopes .re-cell-text { grid-column:2 / -1; grid-row:2;
    padding:0 12px 6px 0; }
  .re-panel-open .re-grid-scopes .re-cell-predicate { grid-column:2 / -1; grid-row:3;
    padding:0 12px 6px 0; }
  .re-panel-open .re-grid-scopes .re-cell-status { grid-column:2 / -1; grid-row:4;
    padding:0 12px 10px 0; }
  .re-panel-open .re-grid-scopes .re-expander { grid-row:5; }
}

/* The SHELL anchor's one breakpoint: the sidebar becomes an overlay, so
 * the panel covers the content at full width and the content keeps its
 * own padding. */
@media (max-width: 1000px) {
  .re-editor.re-panel-open { padding-right:0; }
  .re-panel { width:100%; border-left:0; }
  .re-search, .re-search .re-input { width:100%; }
  .re-group-filter { width:100%; }
  .re-add, .re-import { margin-left:0; }
  /* The picker spans its toolbar or banner, so it stays on screen. */
  .re-toolbar, .re-banner { position:relative; }
  .re-add { position:static; }
  .re-picker, .re-banner .re-picker { left:var(--sp-4); right:var(--sp-4);
    top:auto; width:auto; max-width:none; margin-top:6px; }
  /* The segments wrap, each its own bordered button. */
  .re-seg { flex-wrap:wrap; gap:6px; border:0; border-radius:0; overflow:visible; }
  .re-seg button, .re-seg button:last-child {
    border:1px solid var(--control-border); border-radius:var(--radius); }
}
/* Principal purposes: one grid row per purpose, its entitlements folded
 * under a disclosure, then the proposals and the import cards. */
.pp-screen { display:flex; flex-direction:column; gap:var(--sp-5); }
.pp-card { padding-bottom:var(--sp-4); }
.pp-section { padding:var(--sp-5); gap:var(--sp-4); }
.pp-section h2 { margin:0; font-size:14px; }
.pp-head, .pp-row { display:grid; align-items:start; gap:var(--sp-4);
  grid-template-columns:minmax(160px,1.2fr) minmax(200px,2fr) minmax(140px,1fr)
    minmax(90px,.6fr) minmax(130px,1fr);
  padding:10px 16px; }
.pp-head { border-bottom:1px solid var(--border); background:var(--plane);
  font:500 12px/1 var(--font-sans); color:var(--steel-800); }
.pp-row { border-bottom:1px solid var(--surface-1); font-size:13px;
  line-height:1.4; }
.pp-row[hidden], .pp-group[hidden] { display:none; }
.pp-main, .pp-purpose, .pp-owner, .pp-status { display:flex;
  flex-direction:column; align-items:flex-start; gap:2px; min-width:0;
  overflow-wrap:anywhere; }
.pp-ents summary { cursor:pointer; color:var(--blue-600); }
.pp-ent-list { margin:var(--sp-3) 0 0; padding-left:var(--sp-5);
  font-size:12.5px; }
.pp-orphans { padding:10px 16px 10px 32px; }
.pp-proposals { list-style:none; margin:0; padding:0; display:flex;
  flex-direction:column; gap:var(--sp-3); }
.pp-proposal { display:flex; align-items:flex-start; justify-content:space-between;
  gap:var(--sp-4); padding:var(--sp-4); border:1px solid var(--border);
  border-radius:var(--radius); background:var(--plane); }
.pp-proposal-text { display:flex; flex-direction:column; gap:2px; min-width:0;
  font-size:13px; overflow-wrap:anywhere; }
.pp-compose-grid { display:grid; gap:var(--sp-4);
  grid-template-columns:repeat(auto-fit,minmax(200px,1fr)); }
.pp-compose-foot, .pp-import-foot { display:flex; align-items:center;
  gap:var(--sp-4); }
.pp-import { display:flex; flex-direction:column; gap:var(--sp-4); }
.pp-import textarea.re-input { min-height:140px; height:auto; resize:vertical;
  font-family:var(--font-mono); }
.pp-refusal { padding:var(--sp-4) var(--sp-5); border:1px solid var(--red-700);
  border-radius:var(--radius); background:var(--raised); color:var(--red-700);
  font-size:13px; }
.pp-refusal ul { margin:var(--sp-3) 0 0; padding-left:var(--sp-5); }
@media (max-width: 760px) {
  .pp-head { display:none; }
  .pp-row { grid-template-columns:1fr; }
}
/* --- END: REGISTER EDITOR --------------------------------------------- */

/* --- ANCHOR: PRINT ------------------------------------------------------
 * Last on purpose: print rules override, so they read better at the end.
 * Print forces the light tokens whatever the toggle says. */
/* The shell prints the CURRENT PAGE. The navigation pane is chrome for
 * navigating and there is nothing to navigate on paper; the content region
 * stops being a scroller so it prints in full rather than one viewport's
 * worth; the provenance panel is forced open so the evidence stamp lands in
 * the PDF, which is the whole reason this tool's output gets printed.
 *
 * PRINT FORCES THE LIGHT TOKENS whatever the toggle says. A dark-theme page
 * sent to a printer is either unreadable or a full-bleed ink dump, and the
 * palette is redefined here rather than trusting `color-scheme` because the
 * dark values are set by an attribute selector that beats a media query. */
@media print {
  :root, :root[data-theme="dark"] {
    color-scheme: light;
    --steel-a:#F7F8F9; --steel-100:#F0F1F2; --steel-200:#DEE2E4;
    --steel-300:#C7CDD1; --steel-400:#A7AFB6; --steel-500:#85919A;
    --steel-600:#616E78; --steel-700:#5A6166; --steel-800:#434C53;
    --steel-900:#30363B;
    --blue-a:#EBF3FB; --blue-100:#D1E4F6; --blue-600:#135FB3;
    --blue-700:#0C4995;
    --red-a:#FFEFEF; --red-600:#C30000; --red-700:#9B0000;
    --orange-a2:#FFF1DC; --orange-100:#FFDCA7; --orange-500:#B35F00;
    --ac-warn:#E09A4B; --ac-logo-ground:#FFFFFF;
    --orange-ink:#612200;
    --green-a2:#E6F6E8; --green-600:#0C6F2E; --green-700:#04572B;
    /* System palette: the ramps only. Light: tint 100, ink 700, marker 600.
     * Red, orange and green are left out, since they are the outcome rings. */
    --sys-indigo-tint:#DEE0F4; --sys-indigo-ink:#353DAB; --sys-indigo-marker:#4854C4;
    --sys-yellow-tint:#FEEB7A; --sys-yellow-ink:#614700; --sys-yellow-marker:#7D5A00;
    --sys-purple-tint:#EEDAF5; --sys-purple-ink:#742591; --sys-purple-marker:#9132B5;
    --sys-pink-tint:#FDD7E9; --sys-pink-ink:#8A1D59; --sys-pink-marker:#AE286C;
    --sys-aqua-tint:#ACECF2; --sys-aqua-ink:#004E7D; --sys-aqua-marker:#00678F;
    --sys-brown-tint:#EBE0CF; --sys-brown-ink:#69430E; --sys-brown-marker:#825722;
    --sys-olive-tint:#E8EDC8; --sys-olive-ink:#4A5410; --sys-olive-marker:#667316;
    --sys-teal-tint:#CCF2EC; --sys-teal-ink:#0F4F4A; --sys-teal-marker:#11706A;
    --sys-blue-tint:#D6E6FA; --sys-blue-ink:#0F3F80; --sys-blue-marker:#1F5FBF;
    /* System palette default: no ramp row, or more than one
     * system on a producer, reads Steel. */
    --sys-neutral-tint:var(--steel-100); --sys-neutral-ink:var(--steel-700);
    --sys-neutral-marker:#697782;
    --control-border:#7D8890;
    --raised:#FFFFFF; --surface-1:var(--steel-100);
    --heat-1:#FBEFC4; --heat-2:#FFDCA7; --heat-3:#F7BE8C; --heat-4:#EE9B93; --heat-5:#B9382F; --heat-ink:var(--steel-900); --heat-5-ink:#FFFFFF;
    --divider:var(--steel-100);
    --seq-100:#D1E4F6; --seq-250:#9CC4EA; --seq-400:#5C97D8;
    --seq-450:var(--blue-600); --seq-550:#12539B; --seq-600:var(--blue-700);
    --seq-700:#08305F;
    --serious:#8F2E00;
  }

  #spa-sidebar, .nv-open { display: none; }

  #app {
    display: block;
    height: auto;
    min-height: 0;
    background: #fff;
  }
  #spa-main { display: block; }

  /* Rule 1 inverted: on paper there is no viewport to scroll, and a region
   * that kept `overflow-y:auto` would print its first screen and clip the
   * rest without saying so. */
  #spa-scroll {
    overflow: visible;
    height: auto;
  }
  #spa-content {
    overflow: visible;
    height: auto;
    padding: 0;
  }

  /* The strip prints in full, with the hidden panel forced open. The
   * `[hidden]` override is deliberate: the provenance panel closes by
   * being hidden, and the stamp has to print whether or not the reader
   * opened it before reaching for the print dialog. */
  #spa-provenance {
    max-height: none;
    overflow: visible;
    break-inside: avoid;
  }
  #spa-provenance [hidden] { display: block !important; }
}
/* --- END: PRINT --------------------------------------------------------- */

/* The exception table's live Jira column: one lozenge per ticket. The dot's
 * colour is a token named on `data-color-var`; the status word beside it is
 * what carries the meaning. */
.jira-lozenge { display:inline-flex; align-items:center; gap:6px;
  font-size:11.5px; padding:1px 8px; border:1px solid var(--border);
  border-radius:10px; white-space:nowrap; }
.jira-dot { display:inline-block; width:8px; height:8px; border-radius:50%;
  flex:none; }
.jira-absent { color:var(--text-secondary); font-size:12px; }

/* An unknown figure's reason wraps inside its cell, so a narrow screen grows
 * the row and never the page. */
.excepted-unknown { font-style:normal; overflow-wrap:anywhere; }
.activity-overdue-unread, .activity-overdue-reasons { font-size:12.5px;
  color:var(--text-secondary); margin:8px 0 0; overflow-wrap:anywhere; }
.activity-overdue-reasons { padding-left:20px; }

/* The grouped table. A roll-up row spans the table on the subtle ground,
 * its toggle is a chevron drawn here on an empty button, and its parts
 * wrap on a narrow screen. A row under a collapsed group carries the
 * `hidden` attribute, which the table's own `display` never overrides. */
tr.dt-group > td { background:var(--surface-1); }
.dt-group td > * { margin-right:10px; }
.dt-group-toggle { width:22px; height:22px; padding:0; vertical-align:middle;
  border:1px solid var(--border); border-radius:4px; background:var(--raised);
  cursor:pointer; }
.dt-group-toggle::before { content:""; display:inline-block; width:6px;
  height:6px; border-right:2px solid var(--text-primary);
  border-bottom:2px solid var(--text-primary); transform:rotate(-45deg); }
.dt-group-toggle[aria-expanded="true"]::before { transform:rotate(45deg); }
.dt-group-key { font-weight:600; }
.dt-group-count, .dt-group-renewal, .dt-group-excepting {
  color:var(--text-secondary); font-size:12.5px; white-space:nowrap; }
.dt-group-note > * { margin-right:12px; }
.dt-group-note[hidden], tbody tr[hidden] { display:none; }
.dt-group-pick, .dt-group-pick-head { text-align:center; width:1%; }
/* The bar over the grouped exception table: its two actions, the reasons
   each is off, and the form the one picked opens. */
.exc-act-panel { margin:0 0 12px; }
.exc-act-bar { display:flex; flex-wrap:wrap; align-items:center; gap:8px; }
.exc-act, .exc-act-reload { padding:6px 12px; font:inherit; font-size:13px;
  border:1px solid var(--border); border-radius:6px; background:var(--surface-1);
  color:var(--text-primary); cursor:pointer; }
.exc-act:disabled { color:var(--muted); cursor:not-allowed; }
.exc-act-activity { font-size:12.5px; }
.exc-act-why { margin:8px 0 0; padding-left:18px; font-size:12.5px; color:var(--muted); }
.exc-act-form { display:flex; flex-direction:column; gap:8px; margin:8px 0 0;
  max-width:480px; }
.exc-act-form label { display:flex; flex-direction:column; gap:4px; font-size:12.5px; }
.exc-act-form input, .exc-act-form textarea { font:inherit; font-size:13px;
  padding:6px 10px; }
.exc-act-form button[type=submit] { align-self:flex-start; padding:6px 12px; }
.exc-pending { margin:4px 0 0; }
