/* =============================================================================
   AgentWorkAtlas — design plan, token system, signature spec
   Written BEFORE the build (frontend-design doctrine), critiqued against the
   impeccable absolute bans, then implemented exactly.
   =============================================================================

   SCENE (forces the theme, per impeccable "write one sentence of physical scene")
   -------------------------------------------------------------------------
   fire17, late evening, macOS, one lamp on, deciding which earning path to wire
   into MoneyLoop next — scanning 342 rows fast, then reading one dossier deeply
   and asking "how much do I trust this?". That scene is dark-first. But the same
   atlas gets read at noon in a bright room, so light mode is a real design: a
   daylight chart room — pure white paper, ink text, same brass instrument color.
   Light is not an inversion of dark; it is the same instrument in the sun.

   PRODUCT.md's mood, binding: "surveyor's field instrument at night — brass-amber
   signal lamps over ink-dark chart paper; clinical, precise, warm only where data
   speaks."

   COLOR — strategy: COMMITTED (PRODUCT.md, binding)
   -------------------------------------------------------------------------
   Brand seed (impeccable palette.mjs): oklch(0.660 0.201 40.0) — brass amber.
   "Committed" is honoured as: the brand hue OWNS the identity, not a timid 5%
   accent. Every neutral in both themes is tinted toward hue 40 (chroma .004-.02),
   so the surfaces themselves are the instrument's brass-warmed ink — there is no
   neutral gray anywhere in this app. Amber carries every moment where data speaks:
   the constellation lamps, scores, rank, focus ring, current selection, links,
   active nav. The Insights thesis is one drenched amber surface (the single place
   Committed goes full — product register permits exactly one surface to earn it).
   Everything NOT data stays quiet ink. "Warm only where data speaks" is literal.

   Two counter-hues, and they are not decoration — they are the two other things
   this dataset says. Both are drawn from the subject's own materials (a surveyor's
   theodolite: brass body, verdigris patina, red chart-ink):
     verdigris  oklch(.78 .08 190) — verification / live evidence / primary source
     clay       oklch(.70 .15 25)  — dead link / quarantined / no-go
   Three roles, each earned by the data. This is deliberately NOT "near-black + one
   bright accent" (a banned AI lane): the ground has real chroma and is not black,
   and the palette has three semantic roles, not one.

   Verified, not eyeballed — .deify/contrast.py computes OKLCH→sRGB→WCAG for all
   20 fg/bg pairs. Every one passes (body ≥4.5:1, marks ≥3:1). Worst case is
   light-mode amber-as-mark at 3.38:1 (non-text only). Body ratios: dark 15.5:1,
   light 16.5:1. Re-run the script if any token below changes.

   TYPE — three roles on a real contrast axis, zero external requests
   -------------------------------------------------------------------------
   All system stacks; no CDN, no webfont, no icon font.
     display  ui-serif / Iowan Old Style / Palatino  — view titles + opportunity
              NAMES. Cartographic: survey maps and field ledgers are set in serif.
              A serif name column in a dense data table is the thing no dashboard
              does, and it is what makes this read as a research instrument. Serif
              is kept OUT of labels, buttons, and chrome (product register bans
              display faces in UI furniture) — it touches titles and names only,
              which are content, not chrome.
     ui       system-ui / -apple-system  — every label, button, control, chip.
     data     ui-monospace / SF Mono / Menlo  — every number, cid, score, count.
              tabular-nums everywhere so columns align on the decimal.
   Fixed rem scale at ratio ~1.2 (product register: no fluid clamp headings).

   LAYOUT
   -------------------------------------------------------------------------
   Top bar (wordmark · view nav · search · theme) is the only persistent chrome.
   Atlas   : filter rail (left, sticky, collapses to a disclosure on mobile)
             + the ranked table. Density is the point; 342 rows, all client-side.
   Detail  : a full hash-routed page, never a modal (product register: "modal as
             first thought" is a ban) — a dossier deserves the whole surface.
   Insights: thesis surface, then the merit quadrant, then distribution.
   No-go   : first-class nav (ORACLE §6 UI law), never buried.
   Compare : 2-4 aligned columns.
   Flex for 1D, grid for 2D. Semantic z-scale, no 9999s.

   SIGNATURE — the convergence constellation
   -------------------------------------------------------------------------
   The one bold thing; everything else disciplined (Chanel rule).
   8 independent research efforts → 8 FIXED angular positions on a small rose,
   clockwise from north: mine, A2, A3, A4, A5, A6, A7, UPro1. A record's signature
   is which lamps are lit. Position encodes WHICH source, so the shape itself is
   identity — a plain "5/8" dot row throws that away. Learn the rose once and you
   read provenance at a glance across 342 rows: a full rose = all 8 found it, a
   lone north lamp = only fire17's fleet did.
   Same object at three sizes: 18px inline in the table, 22px in compare, 168px
   with labels on detail. Unlit lamps stay drawn as hairline rings — absence is
   data too, and showing it is why the shape is readable.
   The merit quadrant on Insights is the same idea at fleet altitude: convergence
   × consensus, so "high score, nobody else found it" is visibly not the same
   claim as "everyone found it".
   Gapfill records have no rose (they were never in the 8-way merge) — they get an
   honest "gapfill-verified · unscored" plate instead. Never a 0. Never a fake dot.

   SELF-CRITIQUE vs the bans (run before writing a line of implementation)
   -------------------------------------------------------------------------
   ✓ cream/terracotta default        — banned lane; ground is ink, not paper-warm.
   ✓ near-black + one acid accent    — banned lane; ground has chroma, palette has
                                       three earned semantic roles, and the display
                                       face is a serif, not the terminal reflex.
   ✓ newsprint broadsheet            — banned lane; no hairline-rule column grid.
   ✓ side-stripe borders             — none. Chips and full 1px borders only.
   ✓ gradient text                   — none. background-clip:text appears nowhere.
   ✓ glassmorphism                   — none. No backdrop-filter.
   ✓ hero-metric template            — the Insights hero is the thesis SENTENCE,
                                       not big-number/small-label/gradient.
   ✓ identical card grids            — the atlas is a table; insights sections all
                                       differ structurally.
   ✓ eyebrow kickers on every section— none.
   ✓ numbered section scaffolding    — none. Numbers appear only where they are
                                       real data (rank, score, convergence).
   ✓ display font in UI labels       — serif restricted to titles + names.
   ✓ decorative motion               — motion conveys state only; 150-250ms.
   ✓ text overflow                   — names cap at 75 chars (PATTERNS); tested at
                                       every breakpoint.
   Second-order reflex check: "research atlas that isn't newsprint → dark terminal
   dashboard, mono, one accent" is the trap one tier down. Escaped by the serif
   name column, the three-role material palette, a light mode designed as its own
   room, and the positional rose — none of which a terminal dashboard produces.
   ============================================================================= */

/* ---------- tokens ---------------------------------------------------------- */
:root {
  color-scheme: dark;

  --bg:        oklch(0.190 0.012 40);
  --surface:   oklch(0.228 0.014 40);
  --rail:      oklch(0.212 0.016 40);
  --line:      oklch(0.320 0.018 40);
  --line-soft: oklch(0.270 0.016 40);
  --ink:       oklch(0.940 0.008 40);
  --muted:     oklch(0.760 0.014 40);
  --amber:     oklch(0.660 0.201 40);   /* brand seed, exact — marks/fills */
  --amber-txt: oklch(0.800 0.150 45);   /* text-safe amber, 8.99:1 on --bg */
  --amber-dim: oklch(0.660 0.201 40 / 0.16);
  --verdigris: oklch(0.780 0.080 190);
  --verd-dim:  oklch(0.780 0.080 190 / 0.16);
  --clay:      oklch(0.700 0.150 25);
  --clay-dim:  oklch(0.700 0.150 25 / 0.16);
  --sel:       oklch(0.660 0.201 40 / 0.12);
  --hover:     oklch(0.660 0.201 40 / 0.07);

  --font-display: ui-serif, "Iowan Old Style", "Palatino Linotype", Palatino, Charter, Georgia, serif;
  --font-ui: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-data: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;

  /* fixed rem scale, ratio ~1.2 — product register: no fluid headings */
  --t-xs: 0.6875rem; --t-sm: 0.8125rem; --t-md: 0.9375rem;
  --t-lg: 1.125rem;  --t-xl: 1.375rem;  --t-2xl: 1.75rem; --t-3xl: 2.25rem;

  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px; --s7: 48px;
  --r1: 3px; --r2: 5px; --r3: 8px;

  /* The top bar is pinned to an exact height so the rail and the table head can
     anchor to it. Measured, not guessed: everything below sticks at this value. */
  --topbar-h: 64px;

  /* semantic z-scale — never arbitrary */
  --z-sticky: 10; --z-rail: 20; --z-topbar: 30; --z-toast: 40;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);  /* ease-out-quart */
  --dur: 180ms;
}

/* light = daylight chart room. Same instrument colour, different room.
   An inline <head> script stamps data-theme from localStorage ?? prefers-color-scheme
   before first paint, so this one block covers both the system preference and the
   manual toggle with no duplicated token table and no flash. No JS → dark-first. */
:root[data-theme="light"] {
  color-scheme: light;
  --bg:        oklch(1.000 0.000 0);      /* pure white paper */
  --surface:   oklch(0.985 0.004 40);
  --rail:      oklch(0.972 0.006 40);
  --line:      oklch(0.900 0.010 40);
  --line-soft: oklch(0.940 0.008 40);
  --ink:       oklch(0.240 0.020 40);
  --muted:     oklch(0.480 0.020 40);
  --amber:     oklch(0.660 0.201 40);     /* same instrument colour */
  --amber-txt: oklch(0.520 0.170 40);     /* text-safe on white, 5.95:1 */
  --amber-dim: oklch(0.660 0.201 40 / 0.14);
  --verdigris: oklch(0.480 0.090 190);
  --verd-dim:  oklch(0.480 0.090 190 / 0.12);
  --clay:      oklch(0.480 0.170 25);
  --clay-dim:  oklch(0.480 0.170 25 / 0.12);
  --sel:       oklch(0.660 0.201 40 / 0.10);
  --hover:     oklch(0.660 0.201 40 / 0.06);
}

/* ---------- reset ----------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--bg);
  color: var(--ink);
  font: var(--t-md)/1.55 var(--font-ui);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
h1, h2, h3, h4, p, figure { margin: 0; }
button, input, select { font: inherit; color: inherit; }
a { color: var(--amber-txt); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { text-decoration-thickness: 2px; }
:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
  border-radius: var(--r1);
}
::placeholder { color: var(--muted); opacity: 1; }   /* 4.5:1, not the gray default */

.num { font-family: var(--font-data); font-variant-numeric: tabular-nums; }
.sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
/* a skip link is only honest if it becomes VISIBLE when it takes focus */
a.sr:focus-visible {
  width: auto; height: auto; clip-path: none; z-index: var(--z-toast);
  background: var(--surface); color: var(--amber-txt); border: 1px solid var(--amber);
  border-radius: var(--r1); padding: var(--s2) var(--s3);
}

/* ---------- top bar --------------------------------------------------------- */
.topbar {
  position: sticky; top: 0; z-index: var(--z-topbar);
  display: flex; align-items: center; gap: var(--s4);
  height: var(--topbar-h);
  padding: 0 var(--s5);
  background: var(--rail);
  border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: baseline; gap: var(--s2); margin-right: var(--s2); }
.brand h1 {
  font: 600 var(--t-lg)/1 var(--font-display);
  letter-spacing: -0.01em;
}
.brand .cnt { font-family: var(--font-data); font-size: var(--t-xs); color: var(--muted); }

.nav { display: flex; gap: var(--s1); }
.nav button {
  display: flex; align-items: center; gap: var(--s2);
  /* A sixth view (Frontier) tightened the row enough that "No-go" and "Compare" broke
     mid-label and stood the whole bar up three lines tall at 1440. The labels are two
     words at most — they are never the thing that should wrap. */
  white-space: nowrap;
  padding: var(--s2) var(--s3);
  background: none; border: 1px solid transparent; border-radius: var(--r2);
  color: var(--muted); font-size: var(--t-sm); cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.nav button:hover { background: var(--hover); color: var(--ink); }
.nav button[aria-current="page"] {
  background: var(--sel); color: var(--amber-txt); border-color: var(--amber-dim);
}
.nav kbd {
  font: var(--t-xs)/1 var(--font-data);
  color: var(--muted); border: 1px solid var(--line); border-radius: 2px;
  padding: 1px 3px;
}
.nav button[aria-current="page"] kbd { border-color: var(--amber-dim); }

.search { margin-left: auto; position: relative; display: flex; align-items: center; }
.search input {
  width: 280px; padding: var(--s2) var(--s3); padding-right: 30px;
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--r2);
  font-size: var(--t-sm);
  transition: border-color var(--dur) var(--ease);
}
.search input:hover { border-color: var(--muted); }
.search input:focus { border-color: var(--amber); }
.search kbd {
  position: absolute; right: var(--s2); pointer-events: none;
  font: var(--t-xs)/1 var(--font-data); color: var(--muted);
  border: 1px solid var(--line); border-radius: 2px; padding: 1px 4px;
}
.search input:focus + kbd { opacity: 0; }

.theme-btn {
  padding: var(--s2) var(--s3); background: none;
  border: 1px solid var(--line); border-radius: var(--r2);
  color: var(--muted); font-size: var(--t-sm); cursor: pointer;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.theme-btn:hover { color: var(--ink); border-color: var(--muted); }

.mode-chip {
  font: var(--t-xs)/1 var(--font-data);
  padding: 3px var(--s2); border-radius: var(--r1);
  border: 1px solid var(--line-soft); color: var(--muted);
}
.mode-chip[data-mode="api"] { color: var(--verdigris); border-color: var(--verd-dim); background: var(--verd-dim); }

/* ---------- layout ---------------------------------------------------------- */
main { display: block; }
.view { display: none; }
.view.on { display: block; }        /* visibility is never gated on a transition */

.atlas-wrap { display: grid; grid-template-columns: 232px 1fr; align-items: start; }

.rail {
  position: sticky; top: var(--topbar-h); z-index: var(--z-rail);
  max-height: calc(100vh - var(--topbar-h)); overflow-y: auto;
  padding: var(--s4);
  background: var(--rail);
  border-right: 1px solid var(--line);
}
.rail h2 {
  font: 600 var(--t-xs)/1 var(--font-ui);
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted); margin-bottom: var(--s3);
}
.field { margin-bottom: var(--s4); }
.field label {
  display: block; font-size: var(--t-xs); color: var(--muted); margin-bottom: var(--s1);
}
.field select, .field input[type="range"] { width: 100%; }
.field select {
  padding: var(--s2); background: var(--bg);
  border: 1px solid var(--line); border-radius: var(--r2); font-size: var(--t-sm);
}
.field select:hover { border-color: var(--muted); }
.field input[type="range"] { accent-color: var(--amber); }
.field .val {
  float: right; font-family: var(--font-data); font-size: var(--t-xs); color: var(--amber-txt);
}
.reset {
  width: 100%; padding: var(--s2); background: none; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--r2);
  color: var(--muted); font-size: var(--t-sm);
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.reset:hover { color: var(--ink); border-color: var(--muted); }

.panel { padding: var(--s5); min-width: 0; }
.panel-head {
  display: flex; align-items: baseline; gap: var(--s3);
  margin-bottom: var(--s4); flex-wrap: wrap;
}
.panel-head h2 { font: 600 var(--t-2xl)/1.1 var(--font-display); letter-spacing: -0.015em; }
.panel-head .sub { font-family: var(--font-data); font-size: var(--t-sm); color: var(--muted); }

/* ---------- table ----------------------------------------------------------- */
/* NOTE: .tbl-scroll must NOT be a scroll container at desktop width. `overflow-x`
   makes it the sticky <th>'s nearest scrollport, which anchors the head to the
   TABLE's top rather than the viewport's — the head then renders exactly
   --topbar-h below the thead, between row 1 and row 2. Measured, not theorised.
   Horizontal scroll is only actually needed once the columns stop fitting, so it
   is switched on at the narrow breakpoint only, where a viewport-sticky head is
   not wanted anyway. */
table.atlas { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
table.atlas th {
  position: sticky; top: var(--topbar-h); z-index: var(--z-sticky);
  text-align: left; padding: var(--s2) var(--s3);
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  font-size: var(--t-xs); font-weight: 600; color: var(--muted);
  white-space: nowrap;
}
table.atlas th.sortable { cursor: pointer; user-select: none; }
table.atlas th.sortable:hover { color: var(--ink); }
table.atlas th[aria-sort] { color: var(--amber-txt); }
table.atlas th .arrow { font-family: var(--font-data); margin-left: 2px; }
table.atlas td {
  padding: var(--s2) var(--s3);
  border-bottom: 1px solid var(--line-soft);
  vertical-align: middle;
}
table.atlas tbody tr { cursor: pointer; transition: background var(--dur) var(--ease); }
table.atlas tbody tr:hover { background: var(--hover); }
table.atlas tbody tr:focus-visible { background: var(--sel); outline-offset: -2px; }
td.rank, td.score, td.autonomy { font-family: var(--font-data); font-variant-numeric: tabular-nums; }
td.rank { color: var(--muted); font-size: var(--t-xs); }
td.name {
  font-family: var(--font-display); font-size: var(--t-md);
  max-width: 44ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
td.score { color: var(--amber-txt); font-weight: 600; }
td.score.unscored { color: var(--muted); font-weight: 400; font-size: var(--t-xs); }
td.cmp { width: 28px; }
td.cmp input { accent-color: var(--amber); cursor: pointer; }

/* ---------- chips ----------------------------------------------------------- */
.chip {
  display: inline-flex; align-items: center; gap: var(--s1);
  padding: 2px var(--s2); border-radius: var(--r1);
  font-size: var(--t-xs); white-space: nowrap;
  border: 1px solid var(--line-soft); color: var(--muted);
}
.chip.cat { font-family: var(--font-ui); }
.chip.gate-executable-now   { color: var(--verdigris); border-color: var(--verd-dim); background: var(--verd-dim); }
.chip.gate-quarantined      { color: var(--clay); border-color: var(--clay-dim); background: var(--clay-dim); }
.chip.gate-human-gated,
.chip.gate-credential-gated,
.chip.gate-audience-gated   { color: var(--amber-txt); border-color: var(--amber-dim); background: var(--amber-dim); }
.chip.modeled {
  color: var(--muted); border-style: dashed; font-family: var(--font-data);
}
.chip.live-ok      { color: var(--verdigris); border-color: var(--verd-dim); }
.chip.live-dead    { color: var(--clay); border-color: var(--clay-dim); background: var(--clay-dim); }
.chip.live-blocked { color: var(--amber-txt); border-color: var(--amber-dim); }
.chip.q-primary   { color: var(--verdigris); border-color: var(--verd-dim); }
.chip.q-secondary { color: var(--muted); }
.chip.q-anecdotal { color: var(--clay); border-color: var(--clay-dim); }

/* ---------- the signature: convergence constellation ------------------------ */
.rose { display: block; overflow: visible; flex: none; }
/* The ring is drawn only on the big rose. At table size it collides with the
   lamps and the whole mark turns to mush — the lamps alone carry the shape. */
.rose .ring { fill: none; stroke: var(--line); stroke-width: 1; }
.rose .lamp-off { fill: var(--line-soft); stroke: none; }
.rose .lamp-on  { fill: var(--amber); stroke: none; }
.rose .hub { fill: var(--line-soft); }
.rose-big .lamp-on { filter: drop-shadow(0 0 4px oklch(0.660 0.201 40 / 0.55)); }
.rose-big .lbl {
  font: var(--t-xs)/1 var(--font-data); fill: var(--muted);
  text-anchor: middle; dominant-baseline: middle;
}
.rose-big .lbl.on { fill: var(--amber-txt); }
.rose-cell { display: flex; align-items: center; gap: var(--s2); }
.rose-cell .n { font-family: var(--font-data); font-size: var(--t-xs); color: var(--muted); }
.gapfill-plate {
  font: var(--t-xs)/1 var(--font-data); color: var(--muted);
  border: 1px dashed var(--line); border-radius: var(--r1); padding: 2px var(--s2);
}

/* ---------- detail ---------------------------------------------------------- */
.detail { padding: var(--s5); max-width: 1100px; margin: 0 auto; }
.back {
  display: inline-flex; align-items: center; gap: var(--s2);
  background: none; border: none; cursor: pointer;
  color: var(--muted); font-size: var(--t-sm); padding: var(--s2) 0;
}
.back:hover { color: var(--amber-txt); }
.detail-head { margin: var(--s3) 0 var(--s5); }
.detail-head h2 {
  font: 600 var(--t-3xl)/1.15 var(--font-display);
  letter-spacing: -0.02em; text-wrap: balance; max-width: 24ch;
}
.detail-head .meta {
  display: flex; align-items: center; gap: var(--s2);
  margin-top: var(--s3); flex-wrap: wrap;
}
.cid { font-family: var(--font-data); font-size: var(--t-xs); color: var(--muted); }

.dossier { display: grid; grid-template-columns: 1fr 300px; gap: var(--s6); align-items: start; }
.dossier > section { min-width: 0; }

.block { margin-bottom: var(--s6); }
.block > h3 {
  font: 600 var(--t-xs)/1 var(--font-ui);
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted);
  padding-bottom: var(--s2); margin-bottom: var(--s3);
  border-bottom: 1px solid var(--line);
}
.block > h3 .n { font-family: var(--font-data); color: var(--muted); }

.readout {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: var(--s4); padding: var(--s4);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r3);
}
.readout .cell .k { font-size: var(--t-xs); color: var(--muted); display: block; }
.readout .cell .v {
  font-family: var(--font-data); font-size: var(--t-xl); font-variant-numeric: tabular-nums;
  color: var(--amber-txt); display: block; margin-top: 2px;
}
.readout .cell .v.q { color: var(--muted); font-size: var(--t-md); }
/* the "/ 8" denominator inside a value is context, not the value itself */
.readout .cell .v .q { color: var(--muted); font-size: var(--t-md); font-weight: 400; }

.member {
  padding: var(--s3) 0; border-bottom: 1px solid var(--line-soft);
}
.member:last-child { border-bottom: none; }
.member-head { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.src-tag {
  font: 600 var(--t-xs)/1 var(--font-data);
  padding: 3px var(--s2); border-radius: var(--r1);
  background: var(--amber-dim); color: var(--amber-txt);
}
.member-head .mname { font-family: var(--font-display); font-size: var(--t-md); }
.member dl { margin: var(--s2) 0 0; display: grid; grid-template-columns: 56px 1fr; gap: var(--s1) var(--s3); }
.member dt { font-size: var(--t-xs); color: var(--muted); }
.member dd { margin: 0; font-size: var(--t-sm); max-width: 72ch; text-wrap: pretty; }

.cite { padding: var(--s3) 0; border-bottom: 1px solid var(--line-soft); }
.cite:last-child { border-bottom: none; }
.cite .u { font-family: var(--font-data); font-size: var(--t-xs); word-break: break-all; }
.cite.dead .u { text-decoration: line-through; color: var(--muted); }
.cite .claim { font-size: var(--t-sm); color: var(--muted); margin-top: var(--s1); max-width: 72ch; }
.cite .tags { display: flex; gap: var(--s2); margin-top: var(--s2); flex-wrap: wrap; }

.fact {
  padding: var(--s3); margin-bottom: var(--s3);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r2);
}
.fact .p { font-weight: 600; font-size: var(--t-sm); }
.fact .f { font-size: var(--t-sm); color: var(--muted); margin-top: 2px; }
.fact .v { font-family: var(--font-data); font-size: var(--t-sm); margin-top: var(--s2); }
.fact .quote {
  margin-top: var(--s2); padding-left: var(--s3);
  border-left: 1px solid var(--line);      /* 1px rule, not a side-stripe accent */
  font-size: var(--t-xs); color: var(--muted); font-style: italic;
}
.qbanner {
  margin-top: var(--s4); padding: var(--s3) var(--s4);
  border: 1px solid var(--clay); border-radius: var(--r2);
  background: var(--clay-dim); color: var(--clay);
  font-size: var(--t-sm); line-height: 1.45;
}
.fact .corrects {
  margin-top: var(--s2); font-size: var(--t-xs); color: var(--clay);
}

.alias-row { display: flex; gap: var(--s2); flex-wrap: wrap; }

.aside-card {
  padding: var(--s4); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r3); margin-bottom: var(--s4);
}
.aside-card h3 {
  font: 600 var(--t-xs)/1 var(--font-ui);
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted);
  margin-bottom: var(--s3);
}

/* ---------- insights -------------------------------------------------------- */
/* Insights is prose + one chart, not a fleet table: it gets a measure, so the
   drenched surface never stretches into a field of empty amber on a wide display. */
#view-insights .panel { max-width: 1280px; margin: 0 auto; }

.thesis {
  /* the ONE drenched surface — Committed goes full exactly here.
     Two columns: the claim, and the eight lamps that back it. The rose is the
     argument, not an ornament — the thesis IS "all eight agreed". */
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--s7);
  align-items: center;
  padding: var(--s6); border-radius: var(--r3); margin-bottom: var(--s6);
  background: var(--amber);
  color: oklch(0.16 0.03 40);
}
.thesis .lede {
  font: 500 var(--t-3xl)/1.3 var(--font-display);
  letter-spacing: -0.02em; text-wrap: balance; max-width: 38ch;
}
.thesis .lede em { font-style: normal; text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 3px; }
.thesis .by {
  margin-top: var(--s4); font: var(--t-sm)/1.6 var(--font-ui);
  color: oklch(0.16 0.03 40 / 0.88);   /* 0.78 measured 4.35:1 — below floor; 0.88 = 5.06:1 */
  max-width: 62ch; text-wrap: pretty;
}
.thesis-mark { text-align: center; }
.thesis-mark .rose .lamp-on { fill: oklch(0.16 0.03 40); }
.thesis-mark .rose .lamp-off { fill: oklch(0.16 0.03 40 / 0.25); }
.thesis-mark .rose .ring { stroke: oklch(0.16 0.03 40 / 0.35); }
.thesis-mark .rose .hub { fill: oklch(0.16 0.03 40 / 0.35); }
.thesis-mark .rose-big .lamp-on { filter: none; }
.thesis-mark .rose-big .lbl { fill: oklch(0.16 0.03 40 / 0.75); }
.thesis-mark .rose-big .lbl.on { fill: oklch(0.16 0.03 40); }
.thesis-mark figcaption {
  margin: var(--s3) auto 0; font: var(--t-xs)/1.5 var(--font-ui);
  color: oklch(0.16 0.03 40 / 0.88); max-width: 24ch;
}
@media (max-width: 900px) {
  .thesis { grid-template-columns: 1fr; gap: var(--s5); }
  .thesis-mark { justify-self: start; }
}

.quad-wrap { display: grid; grid-template-columns: 1fr 260px; gap: var(--s5); align-items: start; }
.quad {
  position: relative; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r3); padding: var(--s4);
  /* The plot is drawn 1:1 in px (type must never be scaled). On very narrow screens
     it can exceed the column, so any residual width scrolls INSIDE the plot box —
     never the page body (BUDGETS: body must never scroll sideways). */
  overflow-x: auto;
}
.quad svg { display: block; margin: 0 auto; }   /* sized in JS at 1:1 — never scaled */
.quad .grid-l { stroke: var(--line-soft); stroke-width: 1; }
.quad .axis-l { stroke: var(--line); stroke-width: 1; }
.quad .tick { font: var(--t-xs)/1 var(--font-data); fill: var(--muted); }
.quad .axis-t { font: var(--t-xs)/1 var(--font-ui); fill: var(--muted); }
.quad .qname { font: var(--t-xs)/1 var(--font-ui); fill: var(--muted); opacity: 0.75; }
.quad .pt {
  fill: var(--amber); fill-opacity: 0.5; stroke: var(--amber); stroke-width: 1; cursor: pointer;
  transition: fill-opacity var(--dur) var(--ease), r var(--dur) var(--ease);
}
.quad .pt:hover, .quad .pt:focus-visible { fill-opacity: 1; }
.quad .pt.validated { fill-opacity: 0.9; }
.quad .trust-l { stroke: var(--verdigris); stroke-width: 1; stroke-dasharray: 3 3; }
.quad .trust-t { font: var(--t-xs)/1 var(--font-data); fill: var(--verdigris); }

/* ---------- frontier: cost x income ------------------------------------------
   The plot's marks are coloured by payout friction from a 4-stop OKLCH ramp resolved
   in JS (app.js RAMP, gated by .deify/contrast_frontier.py) — so fill/stroke arrive as
   attributes here, and this file styles only geometry, type and state. */
.fx-lede {
  font-size: var(--t-sm); color: var(--muted); max-width: 78ch; margin-bottom: var(--s4);
}
.fx-wrap { display: grid; grid-template-columns: 1fr 260px; gap: var(--s5); align-items: start; }
/* A grid item's automatic minimum size is its CONTENT, so the plot column refuses to go
   below the svg's 420px floor and pushes the body into a sideways scroll on a phone —
   .fx's own overflow-x never gets the chance to fire. min-width:0 lets the column shrink
   so the residual width scrolls inside the plot box instead, which is the whole contract.
   (.quad escapes this only because it IS the grid item; here the stack wrapper is.) */
.fx-wrap > * { min-width: 0; }
.fx-bar {
  display: flex; align-items: center; gap: var(--s4); margin-bottom: var(--s3); flex-wrap: wrap;
}
.fx-zoom { display: flex; align-items: center; gap: var(--s2); margin-left: auto; }
.fx-zoom .btn {
  min-width: 30px; padding: var(--s1) var(--s2);
  font-family: var(--font-data); line-height: 1.2;
}
.fx-zoom .btn[hidden] { display: none; }
.fx-hint { font-size: var(--t-xs); color: var(--muted); }
.fx-toggle { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-sm); cursor: pointer; }
.fx-toggle input { accent-color: var(--amber); cursor: pointer; }
.fx-toggle input:disabled { cursor: not-allowed; }
.fx-toggle input:disabled + span { color: var(--muted); }

.fx {
  position: relative; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r3); padding: var(--s4);
  /* same rule as the merit quadrant: the plot is drawn 1:1 in px, so any residual
     width scrolls INSIDE this box — the body must never scroll sideways. */
  overflow-x: auto;
}
.fx svg {
  display: block; margin: 0 auto;
  /* the plot owns its gestures: pinch and drag are the view's, not the scroller's.
     Standard for a zoomable plot, and it is why .fx's own touch scrolling is not missed. */
  touch-action: none;
  cursor: grab;
}
.fx svg:active { cursor: grabbing; }
.fx-grid { stroke: var(--line-soft); stroke-width: 1; }
.fx-axis { stroke: var(--line); stroke-width: 1; }
.fx-break { stroke: var(--line); stroke-width: 1; fill: none; }   /* the "$0 is not on the log axis" mark */
.fx-tick { font: var(--t-xs)/1 var(--font-data); fill: var(--muted); }
.fx-tick.zero { fill: var(--muted); opacity: 0.85; }
.fx-axis-t { font: var(--t-xs)/1 var(--font-ui); fill: var(--muted); }
.fx-corner { font: var(--t-xs)/1 var(--font-ui); fill: var(--muted); opacity: 0.75; }
.fx-iso { stroke: var(--line); stroke-width: 1; stroke-dasharray: 4 4; }
.fx-iso.unit { stroke: var(--verdigris); stroke-dasharray: 2 3; }   /* 1x = ignition: it pays for itself */
.fx-iso-t {
  font: var(--t-xs)/1 var(--font-data); fill: var(--muted); opacity: 0.9;
  /* the label rides ON its line, so it needs the ground painted behind it or the dashes
     strike straight through the type */
  paint-order: stroke; stroke: var(--surface); stroke-width: 3.5; stroke-linejoin: round;
}
.fx-iso-t.unit { fill: var(--verdigris); opacity: 1; }   /* ignition is the one that matters */

/* fire17's encoding (feeltest round 1): FILL = payout friction, OUTLINE = has an agent run
   it, DASH = low confidence. Three orthogonal channels, replacing the old hollow/solid. */
.fx-pt {
  fill-opacity: 0.9;
  /* never-run is the common case (no agent has been to the field yet) — a hairline outline
     keeps the dot's edge crisp against the ground without claiming anything */
  stroke: var(--line); stroke-width: 1;
  cursor: pointer;
  transition: opacity var(--dur) var(--ease);
}
/* verdigris is this palette's "verified / live evidence" role — an agent really ran it */
.fx-pt.run { stroke: var(--verdigris); stroke-width: 2.2; }
.fx-pt.act { fill-opacity: 1; }
/* low confidence: the colour is already desaturated in JS (never faded — opacity would drop
   the mark under the 3.0 contrast floor, measured). The dash is the second channel, because
   41 of the 102 low-confidence points sit where the ramp is near-neutral and desaturating
   them changes nothing a reader can see. */
.fx-pt.low { stroke-dasharray: 2.5 2; }
.fx-pt:hover, .fx-pt:focus-visible { opacity: 1; stroke-width: 2.4; }
.fx-link { stroke-width: 1.4; stroke-dasharray: 2 2; opacity: 0.9; }

.fx-foot {
  font-size: var(--t-xs); color: var(--muted); margin-top: var(--s3);
  padding-top: var(--s3); border-top: 1px solid var(--line-soft);
}
.fx-foot .num { color: var(--ink); }

.fx-ramp {
  height: 10px; border-radius: var(--r1); border: 1px solid var(--line);
  /* mirrors app.js RAMP; both themes are gated by .deify/contrast_frontier.py */
  background: linear-gradient(to right,
    oklch(0.820 0.085 190), oklch(0.780 0.030 90) 25%, oklch(0.700 0.190 45) 50%, oklch(0.560 0.160 25));
}
:root[data-theme="light"] .fx-ramp {
  background: linear-gradient(to right,
    oklch(0.600 0.085 190), oklch(0.590 0.035 90) 25%, oklch(0.580 0.190 45) 50%, oklch(0.450 0.165 25));
}
/* one hairline per opportunity, at its true position on the bar's own absolute scale */
.fx-rug { position: relative; height: 7px; }
.fx-rug i {
  position: absolute; top: 0; width: 1px; height: 100%;
  background: var(--muted); opacity: 0.5;
}
/* (FB2-4 retired .fx-ramp-lbl — end meanings now live in the side columns, the midpoint in
   .fx-mid under the track.) */

/* ---------- frontier: the dimension rack (fire17, feeltest items 7-8) --------
   Eight scales, one instrument. Every bar is the same width and the same track, so a
   record's ticks line up vertically across all of them and the rack can be read down a
   column: that registration is the whole point, and it is why the bars are a stack and
   not a row of cards. */
/* FB2-3: the hovered-record name. A long name wrapped to a second line and pushed the whole
   rack down ~10px — the other half of fire17's hover jitter. Fixed one-line height with an
   ellipsis: the name is a glance-label (its full text is in the readout tip), so truncating is
   fine and staying one line keeps every track below it still. */
.fx-now {
  height: 1.4em; margin-bottom: var(--s2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font: 600 var(--t-sm)/1.4 var(--font-display); color: var(--amber-txt);
}
.fx-now [hidden] { display: none; }
.fx-rack {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r3);
  padding: var(--s4); margin-bottom: var(--s4);
}
.fx-rack-h {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s3); flex-wrap: wrap; margin-bottom: var(--s2);
}
.fx-rack-h h3 { margin: 0; font-size: var(--t-sm); }
.fx-rack-h .sub { font: var(--t-xs)/1 var(--font-data); color: var(--muted); margin-left: var(--s2); }
.fx-rack-lede { font-size: var(--t-xs); color: var(--muted); margin: 0 0 var(--s3); max-width: 90ch; }
.fx-chips { display: flex; flex-wrap: wrap; gap: var(--s1); }
.fx-chip {
  font: var(--t-xs)/1 var(--font-ui); color: var(--muted);
  background: var(--rail); border: 1px solid var(--line); border-radius: var(--r1);
  padding: 5px var(--s2); cursor: pointer;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.fx-chip:hover { color: var(--ink); border-color: var(--amber-dim); }
.fx-chip.on { color: var(--ink); border-color: var(--amber); background: var(--sel); }
.fx-chip:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
/* a dimension this feed cannot speak for: shown, struck, and unclickable — the reader must
   see that it EXISTS and is missing here, which hiding the chip entirely would not say */
.fx-chip[disabled] {
  cursor: not-allowed; opacity: 0.55; text-decoration: line-through;
  text-decoration-color: var(--clay);
}
.fx-chip[disabled]:hover { color: var(--muted); border-color: var(--line); }
.fx-rack-gone { font-size: var(--t-xs); color: var(--muted); margin: 0 0 var(--s3); max-width: 90ch; }
.fx-rack-gone .num { color: var(--clay); }

.fx-sel-chips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); min-height: 0; }
.fx-sel-chips:not(:empty) { margin-bottom: var(--s3); }
.fx-sel-lbl { font: var(--t-xs)/1 var(--font-ui); color: var(--muted); }
.fx-sel-chip, .fx-sel-clear {
  display: inline-flex; align-items: center; gap: var(--s2);
  font: var(--t-xs)/1 var(--font-ui); color: var(--ink);
  background: var(--rail); border: 1px solid var(--line); border-radius: var(--r1);
  padding: 5px var(--s2); cursor: pointer; max-width: 30ch;
}
.fx-sel-chip i {
  flex: none; width: 9px; height: 9px; border-radius: 50%;
  background: var(--g); box-shadow: 0 0 0 2px color-mix(in oklab, var(--g) 30%, transparent);
}
.fx-sel-chip span.x { color: var(--muted); }
.fx-sel-chip:hover span.x { color: var(--clay); }
.fx-sel-chip:focus-visible, .fx-sel-clear:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
.fx-sel-clear { color: var(--muted); }
.fx-sel-clear:hover { color: var(--ink); }
/* the palette is a hard cap of five; the shake says "this click did nothing, and why" at
   the place the click happened, which a toast three hundred pixels away would not */
.fx-sel-chips.full { animation: fx-shake 320ms var(--ease); }
@keyframes fx-shake {
  0%, 100% { transform: translateX(0); } 25% { transform: translateX(-3px); } 75% { transform: translateX(3px); }
}

/* FB2-5/6 — painted ranges. The band is the instrument's amber, a colour no dot ever wears:
   fill stays friction, glow stays selection, and the band reads as a marking ON the
   instrument rather than a fourth data channel. */
.fx-rngband { position: absolute; inset: 0; pointer-events: none; }
.fx-rngband b {
  position: absolute; top: -3px; bottom: -3px; border-radius: 3px;
  background: color-mix(in oklab, var(--amber) 16%, transparent);
  border-inline: 2px solid var(--amber);
}
/* dimmed is de-emphasis, not data: the legend chip says exactly how many are dimmed and why */
.fx-pt.dim, .fx-link.dim, .fx-qx.dim { opacity: 0.14; }
.fx-pt.hid, .fx-link.hid, .fx-qx.hid { display: none; }
/* quarantine (design-critique P0): clay dot + slash. The slash is ink so it cuts the dot on
   both themes; it takes no pointer events — the dot beneath stays the hover/keep target. */
.fx-qx { stroke: var(--ink); stroke-width: 1.4; opacity: 0.9; pointer-events: none; }
.tip .n2.qn { color: var(--clay); }
.fx-rngpip { fill: none; stroke: var(--ink); stroke-width: 1; opacity: 0.55; pointer-events: none; }
.fx-rng-chips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); min-height: 0; }
.fx-rng-chips:not(:empty) { margin-bottom: var(--s3); }
.fx-sel-chip.rng i { border-radius: 2px; background: var(--amber); box-shadow: none; }
.fx-rng-mode { display: inline-flex; border: 1px solid var(--line); border-radius: var(--r1); overflow: hidden; }
.fx-rng-mode button {
  font: var(--t-xs)/1 var(--font-ui); color: var(--muted); background: none;
  border: 0; padding: 5px var(--s2); cursor: pointer;
}
.fx-rng-mode button[aria-pressed="true"] { background: var(--sel); color: var(--amber-txt); }
.fx-rng-mode button:focus-visible { outline: 2px solid var(--amber); outline-offset: -2px; }
.fx-rng-hide {
  font: var(--t-xs)/1 var(--font-ui); color: var(--ink); background: var(--rail);
  border: 1px solid var(--line); border-radius: var(--r1); padding: 5px var(--s2); cursor: pointer;
}
.fx-rng-hide[aria-pressed="true"] { background: var(--amber-dim); color: var(--amber-txt); border-color: var(--amber-dim); }
.fx-rng-hide:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
.fx-rng-n { font-size: var(--t-xs); color: var(--muted); }

/* FB2-4 (fire17, round 2): side meta columns — title + low-end meaning LEFT, live readout +
   high-end meaning RIGHT — so the tracks pack tighter and still register vertically (every
   track the same width, ticks aligned down the rack; the fixed side columns preserve that). */
.fx-dims { display: grid; gap: var(--s2); }
.fx-dim {
  display: grid; grid-template-columns: 168px minmax(0, 1fr) 168px;
  column-gap: var(--s3); align-items: center;
}
.fx-dim-side { display: grid; gap: 2px; min-width: 0; }
.fx-dim-side.l { text-align: right; justify-items: end; }
.fx-dim-side.r { text-align: left; justify-items: start; }
.fx-end { font: var(--t-xs)/1.2 var(--font-data); color: var(--muted); }
/* the midpoint is the one label whose x actually means something — it stays under the track */
.fx-mid {
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  font: var(--t-xs)/1.1 var(--font-data); color: var(--muted); margin-top: 2px;
  pointer-events: none;
}
/* one explainer for eight bars, following the one being read. Height is reserved for two
   lines so the rack never jumps as the sentence changes under the pointer. */
/* FB2-3 de-jitter (fire17 round 2, verbatim: "the message at the bottom's height changes").
   min-height let the note grow from ~30px (idle) to 43px (a 3-line note) on hover — measured
   13px of pure hover jitter. Now a FIXED reserve: the note swaps its text inside a constant
   box, so nothing below it (and the box itself) ever moves. Sized to the tallest note actually
   rendered at each width — 43px @1440, 60px @390 — with a couple px of slack; the fx-jitter
   gate asserts both zero shift AND no clip, so a note that outgrows this fails loudly. */
.fx-dim-note {
  font-size: var(--t-xs); color: var(--muted); max-width: 96ch;
  margin: var(--s3) 0 0; height: 46px; overflow: hidden;
  padding-top: var(--s2); border-top: 1px solid var(--line-soft);
}
.fx-dim-note.idle { opacity: 0.6; font-style: italic; }
/* FB2-3: hovering ANY tick fills EVERY bar's readout (fxRackHover writes all of them), and an
   empty→filled .fx-dim-r nudged each baseline-aligned header row by ~1px — 8 rows deep that
   compounded into the ~10px the whole rack slid on hover. Lock the row's height so a readout
   appearing can never reflow it; the baseline look is kept, only the box is pinned. */
.fx-dim-n { font: var(--t-xs)/1.2 var(--font-ui); color: var(--ink); }
/* the cross-report: hover any tick anywhere and every bar prints THIS record's number in
   ITS units. Reserved height, so eight bars never jump when the row fills in (FB2-3). */
.fx-dim-r {
  font: var(--t-xs)/1.2 var(--font-data); color: var(--amber-txt);
  height: 1.2em;
}
.fx-dim-track {
  position: relative; height: 17px; background: var(--rail);
  border: 1px solid var(--line); border-radius: var(--r1);
  cursor: crosshair; touch-action: pan-y;
}
.fx-dim-track:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
/* the friction bar keeps the shape fire17 liked: the gradient ramp, with the rug of ticks
   beneath it. It is the one bar whose colour IS the scale. */
.fx-dim-track.ramped .fx-rug { position: absolute; inset: auto 0 0 0; height: 6px; }
.fx-dim-track.ramped .fx-ramp { position: absolute; inset: 0 0 6px 0; height: auto; border: 0; border-radius: 0; }
/* every other bar has no colour scale of its own, so its ticks carry the SAME fill language
   as the scatter's dots — friction — straight across the rack */
.fx-dim-track:not(.ramped) .fx-rug { position: absolute; inset: 0; height: auto; }
.fx-dim-track:not(.ramped) .fx-rug i { opacity: 0.75; width: 1.5px; }
/* A LEVEL scale (setup 0–25, convergence 1–8, autonomy 0–5) cannot draw one tick per record:
   276 of them land on 6–8 positions, and the hairlines hid up to 98% of the corpus while the
   lede claimed every tick was one opportunity. Those bars draw a column per level instead,
   grown from the baseline, its height the count — so the pile IS the finding rather than the
   thing that erases it. Bottom-anchored and wider than a hairline, because it is a quantity
   now, not a position. */
.fx-dim-track .fx-rug i.col {
  top: auto; bottom: 0; width: 9px; margin-left: -4.5px;
  opacity: 0.95; border-radius: 1px 1px 0 0;
}
/* A density bar has to be tall enough to BE one. At the 17px every other track uses, a
   six-level histogram renders as six dashes and says nothing — truthful and illegible, which
   is only half the job. These three bars buy ~17px each; the show/hide chips are the release
   valve if that height is not wanted. */
.fx-dim-track.dense { height: 34px; }
.fx-dim-track.dense .fx-rug i.col { min-height: 2px; }

.fx-mks { position: absolute; inset: -3px 0; pointer-events: none; }
.fx-mk, .fx-hov {
  position: absolute; top: 0; bottom: 0; width: 3px; margin-left: -1.5px;
  border-radius: 2px; pointer-events: none;
}
.fx-mk { background: var(--g); box-shadow: 0 0 0 1.5px color-mix(in oklab, var(--g) 35%, transparent); }
/* hover rides on the compositor: it is translated, never laid out, because it moves with
   every pointermove across a 276-tick bar */
.fx-hov {
  left: 0; top: -3px; bottom: -3px; width: 2px; margin-left: -1px;
  background: var(--ink); opacity: 0.9; will-change: transform;
}
.fx-hov[hidden] { display: none; }

/* the fourth channel in the plot: a RING around the dot, so fill (friction), stroke (an
   agent ran it) and dash (low confidence) all still say what they said */
.fx-ring {
  fill: none; stroke-width: 2; pointer-events: none;
  filter: drop-shadow(0 0 3px currentColor);
}
.fx-ring.hov { stroke-width: 1.5; stroke-dasharray: 3 3; opacity: 0.85; }
.fx-k-glow { fill: none; stroke: oklch(0.78 0.16 250); stroke-width: 2; }
:root[data-theme="light"] .fx-k-glow { stroke: oklch(0.58 0.19 250); }

/* Keep: the tip is the record's control surface, because the dot's own click already
   opens its dossier and fire17 asked for that in the same feeltest. */
.fx-keep {
  display: inline-flex; align-items: center; gap: var(--s2); margin-top: var(--s2);
  font: var(--t-xs)/1 var(--font-ui); color: var(--ink);
  background: var(--rail); border: 1px solid var(--line); border-radius: var(--r1);
  padding: 5px var(--s2); cursor: pointer; width: 100%; justify-content: center;
}
.fx-keep i {
  width: 9px; height: 9px; border-radius: 50%; border: 2px solid var(--g); background: transparent;
}
.fx-keep[aria-pressed="true"] i { background: var(--g); }
.fx-keep:hover { border-color: var(--amber-dim); }

@media (prefers-reduced-motion: reduce) {
  .fx-sel-chips.full { animation: none; outline: 1px solid var(--clay); }
  .fx-hov { will-change: auto; }
}
@media (max-width: 720px) {
  .fx-dim-r { display: none; }   /* the bar itself is the readout at this width */
  /* FB2-4's side columns have no room at phone width — the sides become rows: title + low
     end above the track, high end below, everything left-aligned. Still static per bar, so
     hover can never reflow anything. */
  .fx-dim { grid-template-columns: minmax(0, 1fr); row-gap: 3px; }
  .fx-dim-side.l { display: flex; justify-content: space-between; align-items: baseline;
    gap: var(--s2); text-align: left; }
  .fx-dim-side.r { justify-items: end; text-align: right; }
  /* notes wrap further as the column narrows — measured 60px at 390, so reserve more here.
     Still a FIXED height (not min-), so hovering never reflows the bottom of the rack. */
  .fx-dim-note { height: 64px; }
}
.fx-note { font-size: var(--t-xs); color: var(--muted); margin-top: var(--s2); }
.fx-key { list-style: none; margin: 0; padding: 0; font-size: var(--t-xs); color: var(--muted); }
.fx-key li { display: flex; align-items: center; gap: var(--s2); padding: var(--s1) 0; }
.fx-key svg { flex: none; overflow: visible; }
.fx-k-fill { fill: var(--amber); fill-opacity: 0.9; stroke: var(--line); stroke-width: 1; }
.fx-k-run { fill: var(--amber); fill-opacity: 0.9; stroke: var(--verdigris); stroke-width: 2.2; }
.fx-k-link { stroke: var(--muted); stroke-width: 1.4; stroke-dasharray: 2 2; }
.fx-k-low {
  fill: oklch(0.700 0.066 45); fill-opacity: 0.9;   /* the amber swatch at the 0.35 chroma cut */
  stroke: var(--line); stroke-width: 1; stroke-dasharray: 2.5 2;
}
:root[data-theme="light"] .fx-k-low { fill: oklch(0.580 0.066 45); }

/* detail: the Frontier panel ------------------------------------------------- */
.fx-tbl { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.fx-tbl th, .fx-tbl td { text-align: left; padding: var(--s2) var(--s3) var(--s2) 0; }
.fx-tbl thead th {
  font: 500 var(--t-xs)/1 var(--font-ui); color: var(--muted);
  border-bottom: 1px solid var(--line); padding-bottom: var(--s2);
}
.fx-tbl td.n { font-family: var(--font-data); font-variant-numeric: tabular-nums; }
.fx-tbl tbody tr + tr td { border-top: 1px solid var(--line-soft); }
.fx-tbl td.lbl { color: var(--muted); font-size: var(--t-xs); }
.fx-swatch {
  display: inline-block; width: 10px; height: 10px; border-radius: 50%;
  vertical-align: -1px; margin-right: var(--s1);
}
.fx-form { display: grid; gap: var(--s3); }
.fx-form .row { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: var(--s3); }
.fx-form label { display: block; font-size: var(--t-xs); color: var(--muted); margin-bottom: var(--s1); }
.fx-form input, .fx-form textarea {
  width: 100%; padding: var(--s2) var(--s3);
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--r2);
  font-size: var(--t-sm);
}
.fx-form input:focus, .fx-form textarea:focus { border-color: var(--amber); }
.fx-form textarea { min-height: 60px; resize: vertical; font-family: inherit; }

.tip {
  position: fixed; z-index: var(--z-toast); pointer-events: none;
  padding: var(--s2) var(--s3); max-width: 260px;
  background: var(--rail); border: 1px solid var(--line); border-radius: var(--r2);
  font-size: var(--t-xs); box-shadow: 0 6px 20px oklch(0 0 0 / 0.4);
}
.tip[hidden] { display: none; }
/* the #frontier readout: fires on hover AND on keyboard focus (see wireFxTip).
   INTERACTIVE, unlike .tip's default — fire17 asked to click through the title and the
   family list, so it must take the pointer and a hover bridge keeps it alive on the way. */
#fx-tip { display: grid; gap: 2px; min-width: 230px; pointer-events: auto; }
/* NOT redundant with .tip[hidden] above — it is the whole reason the box can hide at all.
   `#fx-tip` is (1,0,0) and `.tip[hidden]` is (0,2,0), so the ID rule WINS and the box was
   rendered `display:grid` with the hidden attribute set: fire17's "if the mouse is not
   hovering over an item then hide the information box", still broken after the JS hide path
   was fixed, because the CSS never let it take. Measured, not reasoned: hidden attribute
   present, computed display `grid`. It survived a gate too — fx-tip.mjs asserts el.hidden,
   the DOM property, which was true the whole time. Assert what the reader SEES. */
#fx-tip[hidden] { display: none; }
#fx-tip .ttl {
  font: 600 var(--t-sm)/1.3 var(--font-display);
  color: var(--ink); text-decoration: none;
  margin-bottom: var(--s1); text-wrap: balance;
}
#fx-tip .ttl:hover { color: var(--amber-txt); text-decoration: underline; }
#fx-tip .fam {
  display: grid; gap: 2px; margin-top: var(--s1);
  padding-top: var(--s1); border-top: 1px solid var(--line-soft);
}
#fx-tip .fam a {
  display: flex; justify-content: space-between; gap: var(--s2);
  color: var(--muted); text-decoration: none; padding: 1px 0;
}
#fx-tip .fam a:hover { color: var(--amber-txt); text-decoration: underline; }
#fx-tip .r { display: flex; justify-content: space-between; gap: var(--s3); }
#fx-tip .k { color: var(--muted); }
#fx-tip .rep { color: var(--verdigris); }   /* verdigris = observed, per the token roles */
/* the caveat lines: shown only on the records they are actually true of */
#fx-tip .n2 {
  color: var(--muted); margin-top: var(--s1); padding-top: var(--s1);
  border-top: 1px solid var(--line-soft); text-wrap: pretty;
}
.tip .tn { font-family: var(--font-display); font-size: var(--t-sm); }
.tip .td { font-family: var(--font-data); color: var(--muted); margin-top: 2px; }

.pick { padding: var(--s3) 0; border-bottom: 1px solid var(--line-soft); cursor: pointer; }
.pick:last-child { border-bottom: none; }
.pick:hover .pn { color: var(--amber-txt); }
.pick .pn { font-family: var(--font-display); font-size: var(--t-md); }
.pick .pd { display: flex; align-items: center; gap: var(--s2); margin-top: var(--s1); }

.dist { display: grid; gap: var(--s2); }
.dist-row {
  display: grid; grid-template-columns: 190px 1fr 40px; align-items: center; gap: var(--s3);
  cursor: pointer; padding: 2px 0;
}
.dist-row:hover .dl { color: var(--amber-txt); }
.dist-row .dl { font-size: var(--t-sm); }
.dist-row .db { height: 8px; background: var(--line-soft); border-radius: 2px; overflow: hidden; }
.dist-row .db i { display: block; height: 100%; background: var(--amber); }
.dist-row .dn { font-family: var(--font-data); font-size: var(--t-xs); color: var(--muted); text-align: right; }

/* ---------- compare --------------------------------------------------------- */
.cmp-grid { display: grid; gap: var(--s4); align-items: start; }
.cmp-col {
  padding: var(--s4); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r3);
}
.cmp-col h3 { font: 600 var(--t-lg)/1.25 var(--font-display); text-wrap: balance; }
.cmp-col dl { margin: var(--s3) 0 0; }
.cmp-col dt {
  font-size: var(--t-xs); color: var(--muted);
  padding-top: var(--s3); margin-top: var(--s3); border-top: 1px solid var(--line-soft);
}
.cmp-col dd { margin: var(--s1) 0 0; font-size: var(--t-sm); }
.cmp-drop {
  background: none; border: none; cursor: pointer; color: var(--muted);
  font-size: var(--t-xs); padding: var(--s1) 0; margin-top: var(--s3);
}
.cmp-drop:hover { color: var(--clay); }

/* ---------- edit / add ------------------------------------------------------ */
.form-grid { display: grid; gap: var(--s4); max-width: 640px; }
.form-grid label { font-size: var(--t-xs); color: var(--muted); display: block; margin-bottom: var(--s1); }
.form-grid input, .form-grid select, .form-grid textarea {
  width: 100%; padding: var(--s2) var(--s3);
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--r2);
  font-size: var(--t-sm); font-family: var(--font-ui);
}
.form-grid textarea { min-height: 90px; resize: vertical; }
.form-grid input:focus, .form-grid select:focus, .form-grid textarea:focus { border-color: var(--amber); }
.btn {
  padding: var(--s2) var(--s4); border-radius: var(--r2); cursor: pointer;
  border: 1px solid var(--amber); background: var(--amber);
  color: oklch(0.16 0.03 40); font-size: var(--t-sm); font-weight: 600;
  transition: filter var(--dur) var(--ease);
}
.btn:hover { filter: brightness(1.1); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; filter: none; }
.btn.ghost { background: none; border-color: var(--line); color: var(--muted); }
.btn.ghost:hover { color: var(--ink); border-color: var(--muted); }
.btn.danger { background: none; border-color: var(--clay-dim); color: var(--clay); }
.btn.danger:hover { background: var(--clay-dim); border-color: var(--clay); filter: none; }

.locked {
  padding: var(--s5); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r3); max-width: 620px;
}
.locked h3 { font: 600 var(--t-xl)/1.2 var(--font-display); margin-bottom: var(--s3); }
.locked p { font-size: var(--t-sm); color: var(--muted); max-width: 62ch; margin-bottom: var(--s4); text-wrap: pretty; }
.cmdline {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3); background: var(--bg);
  border: 1px solid var(--line); border-radius: var(--r2);
}
.cmdline code { font-family: var(--font-data); font-size: var(--t-sm); flex: 1; word-break: break-all; }

.hist { padding: var(--s2) 0; border-bottom: 1px solid var(--line-soft); font-size: var(--t-sm); }
.hist:last-child { border-bottom: none; }
.hist .ht { font-family: var(--font-data); font-size: var(--t-xs); color: var(--muted); }
.hist .hn { margin-top: 2px; }

/* ---------- states ---------------------------------------------------------- */
.empty {
  padding: var(--s7) var(--s5); text-align: center;
  border: 1px dashed var(--line); border-radius: var(--r3);
}
.empty h3 { font: 600 var(--t-lg)/1.3 var(--font-display); margin-bottom: var(--s2); }
.empty p { font-size: var(--t-sm); color: var(--muted); max-width: 48ch; margin: 0 auto var(--s4); }
.skeleton { padding: var(--s5); }
.skeleton .bar {
  height: 12px; margin-bottom: var(--s3); border-radius: var(--r1);
  background: var(--line-soft);
  animation: pulse 1.4s var(--ease) infinite;
}
@keyframes pulse { 50% { opacity: 0.45; } }
.toast {
  position: fixed; bottom: var(--s5); left: 50%; transform: translateX(-50%);
  z-index: var(--z-toast); padding: var(--s3) var(--s4);
  background: var(--rail); border: 1px solid var(--amber-dim); border-radius: var(--r2);
  font-size: var(--t-sm); box-shadow: 0 6px 24px oklch(0 0 0 / 0.4);
}
.toast[hidden] { display: none; }
.toast.err { border-color: var(--clay); color: var(--clay); }

/* ---------- responsive ------------------------------------------------------ */
@media (max-width: 1000px) {
  .dossier, .quad-wrap, .fx-wrap { grid-template-columns: 1fr; }
}
/* Columns stop fitting before the mobile breakpoint, so the table gets its own
   horizontal scrollport here — and with it, the head stops being viewport-sticky
   (see the note above the table rules). */
@media (max-width: 1100px) {
  .tbl-scroll { overflow-x: auto; }
  table.atlas th { top: 0; }
}
@media (max-width: 820px) {
  /* on a phone the wrapped chrome is ~230px tall — sticky, it would own a quarter of every
     screen. It scrolls away; the content owns the phone. */
  .topbar { position: static; flex-wrap: wrap; gap: var(--s2); height: auto; padding: var(--s2) var(--s3); }
  .search { order: 3; width: 100%; margin-left: 0; }
  .search input { width: 100%; }
  /* Six view buttons no longer fit one phone row (Frontier was the sixth). The topbar
     wraps but .nav did not, so the row ran 411px wide inside a 390px viewport and took
     the whole body sideways with it. Let the nav itself wrap. */
  .nav { flex-wrap: wrap; }
  .nav kbd { display: none; }
  .atlas-wrap { grid-template-columns: 1fr; }
  .rail {
    position: static; max-height: none; border-right: none;
    border-bottom: 1px solid var(--line);
  }
  .rail-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--s3); }
  .field { margin-bottom: 0; }
  .panel, .detail { padding: var(--s3); }
  .detail-head h2 { font-size: var(--t-2xl); }
  td.name { max-width: 22ch; }
  .thesis { padding: var(--s4); }
  .thesis .lede { font-size: var(--t-xl); }
}

/* reduced motion is not optional */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important; scroll-behavior: auto !important;
  }
}

/* a11y P2-12: at phone width every control clears the 44px touch floor (heights were 17–38px;
   the zoom pair was 30×26). The drag lanes stay lanes but thicken to a usable 32px. */
@media (max-width: 430px) {
  .nav button, #theme-btn, .fx-chip, .fx-sel-chip, .fx-sel-clear,
  .fx-rng-mode button, .fx-rng-hide, .fx-keep { min-height: 44px; }
  #fx-in, #fx-out, #fx-fit { min-height: 44px; min-width: 44px; }
  .fx-dim-track { height: 32px; }
}

/* ---------- capability matrix (VISION L46/L63/L67) --------------------------- */
.cap h3 { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }
/* relative unlock reach, amber because the matrix is a data verdict; no axis, no claim of
   scale beyond "this skill opens more doors than that one" */
.cap-meter { flex: 0 1 180px; height: 6px; background: var(--rail); border-radius: 3px; }
.cap-meter i { display: block; height: 100%; background: var(--amber); border-radius: 3px; }
.cap-ops { display: grid; gap: 2px; }
.cap-op {
  display: grid; grid-template-columns: minmax(0, 1fr) 4ch auto; gap: var(--s3);
  align-items: baseline; padding: 6px var(--s2); border-radius: var(--r1);
  text-decoration: none; color: var(--ink);
}
.cap-op:hover { background: var(--sel); }
.cap-op .pn { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cap-op .num { color: var(--amber-txt); text-align: right; }
@media (max-width: 720px) {
  .cap-op { grid-template-columns: minmax(0, 1fr) auto; }
  .cap-op .chip { display: none; }   /* gate still one tap away on Detail */
}
.cap-op .unit { font: var(--t-xs)/1 var(--font-ui); font-style: normal; color: var(--muted); margin: 0 var(--s2) 0 4px; }
