/* ═══════════════════════════════════════════════════════════════════════════
   Kismet Signal — slate table + report drawer.

   SIX SCALES, and nothing off them. The point is not brevity, it is that a value which
   appears twice means the same thing both times; the last version rotted because it did not.

     COLOUR    White is the default. --ink / --ink-muted / --ink-faint are the three text inks,
               defined in tokens.css. --demand / --fit / --pos / --neg are DATA; ink is
               interaction (law 03). Watch UNSET properties: an unstyled ::placeholder inherited
               Chrome's #757575 for a while — outside the palette, and invisible to any audit
               that only checks what we DO write.
     SIZE      the five --type-* steps in tokens.css; tracking is --track-*, never literal.
     WEIGHT    400 default, 600 labels and data (--weight-data; 700 retired in slice 1).
     SPACE     --s-1..--s-6 (4/8/12/16/24/40), plus three role aliases for the values that kept
               being re-decided: --pad-cell (8px 12px, table/slate cells and rows), --pad-card
               (16px, card interiors), --pad-pane (24px, drawer panes/sections). Slice 6
               (2026-09-01) rounded the old ten-rung scale onto these six; a value that isn't a
               rung rounds to the nearest one rather than minting a seventh.
     RADIUS    --r-media (2, stills/swatches) · --r-control (4, controls) · --r-surface
               (8, containers) · --r-pill (999px — a capsule at any height, and what a circle on
               a square element is really asking for).
     DURATION  --dur-state (120ms — colour/opacity/border state changes) · --dur-reveal
               (200ms — hover-reveal and open/close motions). A keyframe `animation` is a
               separate axis (the spinner, the reasoning caret's pulse) and stays literal.
     RANK      Hierarchy comes from UPPERCASE + weight + colour — never from size.
     WIDTH     Fluid. Text columns are minmax(floor, fr), drawer panes carry no max-width.

   Repeated SHAPES live once, in `shared primitives` below. Each surface then keeps only what
   makes it different. Adding a fifth type step, a third grey or an off-scale radius is how the
   last version rotted — take the nearest rung instead.
   ═══════════════════════════════════════════════════════════════════════════ */

* { box-sizing: border-box; }

html, body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

.hidden { display: none !important; }

/* A THEME FLIP MUST NOT ANIMATE, and this is a correctness rule, not taste. Roughly a dozen
   rules here carry `transition: color/background` — the nav tabs, the sub-tabs, the drawer
   tabs, the seg buttons, every table row. When the palette changes under one of those, Blink
   leaves the element painting its OLD value and never settles: after switching to light mode
   the whole nav stayed on the dark theme's #ccc while the muted ink at that very element already
   read #6d6862, and the winning rule was the one you would expect. Measured, not theorised.
   theme.js adds this class, flips data-theme, forces the recalc and drops it on the next
   frame. It also stops the 150ms cross-fade a theme swap would otherwise smear across
   everything. */
.theme-swap, .theme-swap *, .theme-swap *::before, .theme-swap *::after {
  transition: none !important;
}
button { font: inherit; cursor: pointer; color: inherit; }
h1, h4 { margin: 0; font-weight: 400; }
a { color: var(--ink); }

/* ═══════════════════════ shared primitives ═══════════════════════
   The SHARED half of every repeated shape. These sit ABOVE the surface sections on purpose:
   same-specificity ties then go to the surface, which is what lets a surface override its own
   shape without !important. Adding a selector here is a claim that it IS that shape.

   This is also the theming seam — nearly every colour in the app now reaches --ground / --surface /
   --surface-raised / --rule / --ink / --ink-muted / --ink-faint through one of these groups.
   ═══════════════════════════════════════════════════════════════ */

/* .ks-label — replaces the three tracking/uppercase tag groups (.fmt/.chip-k/.think-tool/
   .tip-head/.think-sec-lbl/.rs-tick/.rs-axislab, .av/.cov-tier/.board-prov/.gate-verdict/
   .avtag/.rxmarket/.pulse-status, .cov-ax/.chip-comp-t) and the SHARED third of the old uppercase
   label rule (.lbl, .chips-label, .band > div, .hrow > div, .tagline, .tab, .logout, .themetog,
   .gauge-label, .sigrow .k, .dw-line .lb, .gauge .d, .band-chip, .think-label, .think-tag): a
   label NAMES a value, so it is always uppercase at micro size and micro tracking. The label
   list's other two properties — face-label, weight-label — are NOT part of this shape: a tag
   beside a data-sized value stays on the page's own face and weight, so those fifteen members
   keep face-label/weight-label on their own already-existing rule below, never a second list. */
.ks-label { text-transform: uppercase; letter-spacing: var(--track-micro); font-size: var(--type-micro); }

/* .ks-chip — replaces the pill/chip list (.sig/.cov-tier/.cov-ax/.board-prov/.gate-verdict for
   the shared border+colour; .chip/.av/.sig/.cov-tier for the pill radius): a bordered token
   carrying a one-word state, neutral until a state class colours it. --pill is the capsule
   radius, kept separate since .cov-ax/.board-prov/.gate-verdict take --r-control instead;
   --pos/--neg are the accent a state earns, replacing three copies of the same colour+border
   pairing each (.av.yes/.cov-tier.t-solid/.gate-verdict.v-pass and their negative twins) with
   one rule each — .av gains the base too, so its yes/no states need only the modifier's
   border-COLOUR rather than the full border shorthand they used to restate. */
.ks-chip { border: 1px solid var(--rule); color: var(--ink-muted); }
.ks-chip--pill { border-radius: var(--r-pill); }
.ks-chip--pos { color: var(--pos); border-color: var(--pos); }
.ks-chip--neg { color: var(--neg); border-color: var(--neg); }
/* named in CLAUDE.md's primitive list but unemitted until Scout's multi-market pill and
   Multi-market badge (README §6a) – the one place demand appears outside the chart and score */
.ks-chip--demand { color: var(--demand); border-color: var(--demand); }
/* the primitive's held state (CLAUDE.md lists .is-on beside --pill/--pos/--neg): ink, like every
   other held state (.fstop.on, .ks-chip.is-on) - it is a control's state, never a datum's accent */
.ks-chip.is-on { background: var(--ink); color: var(--ground); border-color: var(--ink); }

/* .ks-surface — replaces the 10-selector container list (.card, .rs-card, .login-card, .modal,
   .icard, .chart-tip, .rs-tip, .terr-map, .striped, .quad): shared edge, radius and ground.
   --sunken is the inset well (.icard, .rs-tip, .terr-map); a member needing a different ground
   (.striped's loading pattern, .quad's transparent backdrop) overrides background in its own
   rule below, where source order settles the tie against this same-specificity default. */
.ks-surface { border: 1px solid var(--rule); border-radius: var(--r-surface); background: var(--surface); }
.ks-surface--sunken { background: var(--ground); }
.card { overflow: hidden; }

/* .ks-section — replaces the card-section padding/hairline group (.toolbar, .brief-chips,
   .brief-think, .brief-dna, .wl-editbar, .bk-mergebar, .pager, .legend, .rs-head, .board-head,
   .ig-sec, .pulse-row, .fit-deep, .drow): one gutter, one hairline, and the
   edge states whether the section leads its neighbour (hairline below) or follows it (hairline
   above). A section with neither modifier just carries the gutter. */
.ks-section { padding: var(--s-3) var(--s-4); }
.ks-section--lead { border-bottom: 1px solid var(--rule); }
.ks-section--follow { border-top: 1px solid var(--rule); }

/* .ks-field — replaces the form-control shape list (.wl-pick select, .wl-pick
   input[type="text"], .t2form select, .t2form input[type="text"], .fit-deep textarea,
   .toolbar select:not(.fstop-sel), .pager select, .modal select, .modal input[type="text"],
   .bk-addid select, .bk-addid input, .bk-input, .chip-edit, .bk-level-sel) plus its
   scattered per-surface :focus overrides — those existed only because a compound selector like
   `.modal input[type="text"]` outscored a bare `input:focus`; a shared CLASS carries the same
   specificity everywhere it lands, so the workaround goes with the list it was written for.
   The GROUND still varies by depth, so background stays with each control below; `font: inherit`
   is a shorthand, so a control that must NOT take the page's data size sets its own size in a
   later rule. --armed is the two LIVE edits (.bk-input, .chip-edit): an ink edge worn at rest,
   not only on focus, since it marks a state you are in rather than a value reported. --mono is
   the identifier fields (.bk-input, .bk-addid input). */
.ks-field {
  font: inherit; color: var(--ink); border: 1px solid var(--rule); border-radius: var(--r-control);
}
.ks-field:focus { border-color: var(--ink); outline: none; }
.ks-field--armed { border-color: var(--ink); }
.ks-field--mono { font-family: var(--face-mono); }

/* .ks-btn — replaces the quiet-outline-button list (.logout, .themetog, .chip-btn, .fchip,
   .bk-del, .mg-resolve, .mg-del, .bk-add-go, .ecard-act button, .ex-link, .mg-apply,
   .mg-sortbtn, .audit-run, .audit-apply) plus the four primary-action selectors (.login-card
   button, #run, #bk-go, #wl-load). Quiet — no fill, a hairline — is the unmarked default, the
   same convention .ks-surface's bare form reads as "raised"; hover states the consequence in
   colour (ink to act, neg to destroy, fit to ask). Retiring the `.ecard-act button` descendant
   rule also settles a live disagreement it was hiding: that rule's own specificity (one class
   plus the `button` element) outranked .mg-del's and .mg-resolve's dedicated hover colours
   wherever they sat inside .ecard-act, so Delete hovered ink instead of neg, and the merge
   card's "Resolve now" hovered ink instead of fit. Every .ks-btn now reads its own modifier,
   everywhere it appears — .ks-btn--danger is the destructive controls, .ks-btn--ask is the
   Opus-advisory ones (.mg-resolve). */
.ks-btn { background: none; border: 1px solid var(--rule); border-radius: var(--r-control); padding: var(--s-1) var(--s-3); }
.ks-btn:hover { border-color: var(--ink); color: var(--ink); }
.ks-btn--danger:hover { border-color: var(--neg); color: var(--neg); }
.ks-btn--ask:hover { border-color: var(--fit); color: var(--fit); }
/* ink-filled, ground-coloured text — never gold; gold means demand and nothing else */
.ks-btn--primary {
  background: var(--ink); border: 0; border-radius: var(--r-control); padding: var(--s-3) var(--s-5);
  color: var(--ground); font-weight: var(--weight-data); text-transform: uppercase;
  letter-spacing: var(--track-data);
}
/* .ks-btn:hover above paints the text ink for the quiet buttons, and ink on an ink fill is no
   text at all — every primary label vanished on hover in BOTH themes (shipped with slice 7; the
   computed-style diffs that verified it were resting-state only). The fill is the affordance;
   the text stays ground. Found 2026-09-02 in the retune pass. */
.ks-btn--primary:hover { color: var(--ground); }

/* The single disabled treatment — replaces the ten-selector disabled list plus the seven-
   selector hover-suppression list, keyed to :disabled because :hover and :active never match a
   native disabled element (HTML spec) — the hover half is defensive. The primitive (CLAUDE.md)
   also names a class for a non-native control needing the same dimming; no live surface needs
   it right now, so it returns beside whichever one does. */
:disabled { opacity: var(--off); cursor: default; }
:disabled:hover { border-color: var(--rule); color: var(--ink-faint); }

/* a series row's member chips (D7) are real <button>s wearing .sig — everything else .sig
   sets (border/radius/padding/colour/size) already overrides the UA button style; background
   is the one property it doesn't touch, and `none` is theme-neutral so both palettes need no
   separate entry here. */
button.sig { background: none; }

/* the volume chip (series-surfaces design section 5): inline beside its neighbour wherever it
   lands — the drawer's "Part of" line and the Works list still need its own gap and baseline
   alignment. On a slate row it moved to .titlemeta, which zeroes the margin in favour of the gap. */
.serchip { margin-left: var(--s-2); vertical-align: baseline; }

/* Cover images — three sizes of still; only the size differs. */
.thumb, .ytthumb, .btthumb {
  display: block; object-fit: cover; border-radius: var(--r-media);
  border: 1px solid var(--rule); background: var(--rule-soft);
}
/* legend swatches */
.sw-fit, .tip-row .sw, .lg-chip .sw { border-radius: var(--r-media); }

/* .ks-meter — replaces the meter/track rules (.sigrow .track, .terr-row .br, .rxbar and their
   `i` fills): one height, a line-coloured track, and a fill that INHERITS height/radius from it
   so resizing the track needs no second edit. --fit is declared for a fit-coloured fill; no live
   meter reads one today, so only the unqualified (--demand) fill is emitted here. .rs-ramp
   shares this rule's DIMENSIONS only — it is a legend swatch whose gradient IS its own
   background, never a value-driven fill — so it keeps its height/radius with its own rule
   below instead of taking this class. */
.ks-meter { height: 6px; border-radius: var(--r-pill); background: var(--rule); }
.ks-meter i { display: block; height: inherit; border-radius: inherit; background: var(--demand); }

/* .ks-readout — replaces the gauge headline-figure numeral (.gauge .n): a large tabular
   readout. It was the mono face until 2026-09-16 (owner call): a monospaced decimal point
   takes a full cell, so "3.9" read as three spaced glyphs. This primitive has one consumer. */
.ks-readout {
  font: 400 var(--type-numeral)/1 var(--face);
  letter-spacing: var(--track-numeral);
  font-variant-numeric: tabular-nums;
}

/* .ks-reveal — replaces the hand-written hover-reveal copies (.c-rm button, .bk-edit, .idval
   button). `.ecard-act` is named alongside these in the design system but is NOT one of them:
   its buttons were already migrated to .ks-btn in slice 7b, and it never implemented the
   opacity:0 reveal the other three did (always visible, colour-only on hover) — there is
   nothing left here for it to join. Fixes the regression the hand-copies all shared: their
   `:focus` lit only the ONE control that itself had focus, so a second reveal in the same row
   with no tab stop of its own stayed invisible while the row was plainly active under keyboard
   navigation. `:focus-visible` now covers the control itself; each member keeps its own row
   `:hover`/`:focus-within` trigger alongside its surface rule below, since the row class differs
   per member and folding it here would just rebuild the joined list this primitive replaces. */
.ks-reveal {
  background: none; border: 0; color: var(--ink-muted); line-height: 1;
  opacity: 0; transition: opacity var(--dur-state), color var(--dur-state);
}
.ks-reveal:focus-visible { opacity: 1; }

/* every checkbox and radio in the app is ink when on */
.c-sel input, .bk-pick input, .bk-pick-one input, .bk-survivors input,
.mg-drop input { accent-color: var(--ink); }
.c-sel input, .bk-pick-one input { width: 16px; height: 16px; cursor: pointer; }

/* ───────────────────────── gate ───────────────────────── */

.gate { min-height: 100vh; display: grid; place-items: center; padding: var(--pad-pane); }
.login-card {
  width: 360px; padding: var(--pad-pane); display: flex; flex-direction: column; gap: var(--s-3);
}
/* the wordmark: --face-wordmark is a brand asset, not a type step (CLAUDE.md Typeface
   scope) — this and .brand h1 below are its only two uses. No --track-wordmark token exists;
   .06em (--track-section) is the nearest rung. */
.login-card h1 { font: 400 var(--type-numeral)/.9 var(--face-wordmark); letter-spacing: var(--track-section); }
.login-card .sub { margin: 0; text-transform: uppercase; letter-spacing: var(--track-micro); font-size: var(--type-micro); }
input, textarea {
  background: var(--ground); border: 1px solid var(--rule); border-radius: var(--r-control);
  padding: var(--s-3); color: var(--ink); font: 400 var(--type-data)/1.5 var(--face);
}
input:focus, textarea:focus { outline: none; border-color: var(--ink); }
/* Set EXPLICITLY: left unset, Chrome supplies its own #757575 — outside the palette entirely. */
::placeholder { color: var(--ink-muted); opacity: 1; }
/* the four PRIMARY actions (.login-card button, #run, #bk-go, #wl-load) carry .ks-btn--primary
   now (rule at the top of the shared primitives section); the two that sit inline with inputs
   still need their own height, not #run's */
#bk-go, #wl-load { padding: var(--s-2) var(--s-4); }
.error { margin: 0; color: var(--neg); }

/* ───────────────────────── header ───────────────────────── */

.app { padding: 0 var(--s-6) var(--s-6); }
.topbar {
  display: flex; align-items: flex-end; justify-content: space-between;
  padding: var(--s-6) 0 var(--s-4); border-bottom: 1px solid var(--rule); margin-bottom: var(--s-6);
}
.brand h1 { font: 400 var(--type-numeral)/.9 var(--face-wordmark); letter-spacing: var(--track-section); }
/* the label shape (CLAUDE.md Typeface scope: "The header tagline is --face-label at
   --type-micro"), sized explicitly since .topbar sets no size of its own to inherit */
.tagline { display: block; margin-top: var(--s-2); font-family: var(--face-label); font-weight: var(--weight-label); }
.tabs { display: flex; align-items: center; gap: var(--s-1); }
/* groups the nav by kind — discovery | saved work | operator */
.tabdiv { width: 1px; height: 16px; background: var(--rule); margin: 0 var(--s-2); }
.tab {
  background: none; border: none; border-bottom: 2px solid transparent;
  transition: color var(--dur-state);
}
/* every button in the nav shares the label shape — the five tabs and the session's own three
   controls (feedback, theme, sign out) — so a new control composes it by being there, not by
   joining a selector list */
.tabs > button {
  padding: var(--s-2) var(--s-3); font-family: var(--face-label); font-weight: var(--weight-label);
}
.tab.active { border-bottom-color: var(--ink); }
.tab:not(.active) { color: var(--ink-muted); }
.tab:hover { color: var(--ink); }
.logout { margin-left: var(--s-3); }
/* The theme button's word is CSS, not JS: a module script runs after the first paint, so a
   JS-written label flashes the wrong state for a frame. It names the DESTINATION. */
.themetog { margin-left: var(--s-2); color: var(--ink-muted); }
.themetog::after { content: "Light"; }
:root[data-theme="light"] .themetog::after { content: "Dark"; }

/* ───────────────────────── panels ───────────────────────── */

/* One panel per tab, all mounted, all but one hidden — a module wires its controls at import,
   so a panel that left the DOM would break its own wiring. */
main { display: flex; flex-direction: column; }
.panel { display: flex; flex-direction: column; gap: var(--s-5); }

/* ───────────────────────── ask ───────────────────────── */

.ask { display: flex; flex-direction: column; gap: var(--s-3); }
.ask textarea { width: 100%; resize: vertical; }
.ask-actions { display: flex; align-items: center; gap: var(--s-4); flex-wrap: wrap; }
.check { cursor: pointer; }
#run { margin-left: auto; }

/* the watchlist picker row — sits where Search's textarea does: this tab's ask */
.wl-pick { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
/* data size, not t2 (owner, 2026-07-29): four ~260px dropdowns were the loudest type on the page
   before any data appeared, and the toolbar's own selects have always been data-sized. */
.wl-pick select {
  background: var(--surface); padding: var(--s-2) var(--s-3);
  min-width: 260px; cursor: pointer;
}
/* the box also lights on hover, not only on focus — .ks-field:focus covers the focus half */
.wl-pick select:hover { border-color: var(--ink); outline: none; }
/* E1: the stage is a short word, not a list name — it sizes to its options */
.wl-pick #wl-stage { min-width: 0; }

/* The staged-removal bar, between the slate and the legend and inside the card, so it reads as
   a state of THIS table rather than a floating toast. */
.wl-editbar {
  display: flex; align-items: center; gap: var(--s-3);
  background: var(--rule-soft);
}
#wl-edit-msg { color: var(--neg); }

/* ───────────────────────── cards ───────────────────────── */

.spacer { flex: 1; }

/* the brief box: stacked SECTIONS separated by hairlines, all on the card's own ground */
.brief-card { display: flex; flex-direction: column; }
.brief-chips { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.brief-chips:empty { display: none; }
/* The brief card's sections fold under their own heads — Tags, the reference breakdown, the
   reasoning — with one gesture: chevron first, body hidden while the host is .collapsed. The
   chevron is drawn off the host's state so no script has to flip it. */
.fold-head { display: flex; align-items: center; gap: var(--s-2); cursor: pointer; }
.fold-head .chev::before { content: "▾"; }
.collapsed > .fold-head .chev::before { content: "▸"; }
.collapsed > .fold-body { display: none; }
/* the tags head shares its gutter with the chip rows beneath it: no bottom pad while they show,
   its own once they fold */
.brief-tags .fold-head { padding-bottom: 0; }
.brief-tags.collapsed .fold-head { padding-bottom: var(--s-3); }
.chips-label, .lbl { color: var(--ink-muted); font-family: var(--face-label); font-weight: var(--weight-label); }

/* Chips are CONTROLS: the value is click-to-edit, the × drops the filter. */
.chip {
  display: inline-flex; align-items: center; gap: var(--s-1);
  border: 1px solid var(--rule); padding: var(--s-1) var(--s-2) var(--s-1) var(--s-3);
}
.chip-k { color: var(--ink-muted); font-weight: 600; }
.chip-v { cursor: pointer; }
.chip-v:hover { color: var(--ink); }
.chip-x { background: none; border: none; color: var(--ink-muted); padding: 0 var(--s-1); line-height: 1; }
.chip-x:hover { color: var(--neg); }
.chip-edit { background: var(--ground); padding: var(--s-1) var(--s-1); }
/* a reference work is a different KIND of constraint, so it keeps an ink edge — a held state;
   its value is not editable (remove and retype), so no pointer on it */
.chip-comp { border-color: var(--ink-muted); }
.chip-comp .chip-v { cursor: default; }
.chip-comp .chip-v:hover { color: var(--ink); }
.chip-comp-t { color: var(--ink-muted); margin-left: var(--s-1); }
.chip-note { color: var(--ink-muted); }
/* the comp-cap casualty list — muted like any other note, not because it is an error (the brief
   still ran): a note isn't a held state, so it doesn't take .chip-comp's ink edge */
.chip-note-drop { color: var(--ink-muted); }

/* Live reasoning — the model WORKING, not findings. A SECTION of the brief card; its
   transience is carried by behaviour and the pulsing caret, not by a floating dashed box.
   (A sibling-selector rule collapsing the hairline when the chip rows are empty was deleted
   here, fix round 1: #brief-dna sits between #brief-chips and #brief-think in index.html, so it
   never matched even before #brief-facets widened the gap further — .brief-chips:empty's own
   `display: none` above is what actually does that job.) */
/* the caret is the RUNNING indicator and nothing else, so it exists only while streaming */
.think-caret { display: none; flex: none; width: 6px; height: 6px; border-radius: var(--r-pill);
               background: var(--ink); }
.think-active .think-caret { display: block; animation: pulse 1.1s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { opacity: .35 } 50% { opacity: 1 } }
/* A label with work in flight (the memo POST) wears the same pulsing dot as the reasoning
   caret; the class exists only while the request is open. */
.ks-label.is-live::before { content: ""; display: inline-block; width: 6px; height: 6px;
                            border-radius: var(--r-pill); background: var(--ink);
                            margin-right: var(--s-1); vertical-align: middle;
                            animation: pulse 1.1s ease-in-out infinite; }
.think-label { font-family: var(--face-label); font-weight: var(--weight-label); }
.think-tag { margin-left: auto; color: var(--ink-muted); border: 1px solid var(--rule);
             border-radius: var(--r-control); padding: var(--s-1) var(--s-1);
             font-family: var(--face-label); font-weight: var(--weight-label); }
.think-body {
  margin-top: var(--s-2); line-height: 1.6; font-style: italic;
  white-space: pre-wrap; max-height: 220px; overflow-y: auto;
  transition: max-height var(--dur-reveal) ease, opacity var(--dur-reveal) ease;
}
.collapsed .think-body { max-height: 0; opacity: 0; margin-top: 0; overflow: hidden; }

/* ── toolbar ── */
.toolbar { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.gap { margin-left: var(--s-3); }
/* The toolbar CONTROLS are data-sized (slice 5: a control is data size per CLAUDE.md hard
   constraint 2, no longer the label's size and face) — the row still separates by COLOUR
   alone: active ink, everything else (labels and inactive controls alike, since slice 4) muted. */
.seg { display: inline-flex; border: 1px solid var(--rule); border-radius: var(--r-control); overflow: hidden; }
/* a sort control is ONE flex item — label and buttons must never land on different rows */
.sorter { display: inline-flex; align-items: baseline; gap: var(--s-4); }
.seg-btn { padding: var(--s-1) var(--s-3); color: var(--ink-muted); cursor: pointer; transition: all var(--dur-state); }
.seg-btn:hover { color: var(--ink); }
.seg-btn.is-on { background: var(--surface-raised); color: var(--ink); }
/* dashed rather than solid: it ADDS a constraint, so it reads as an empty slot */
.chip-btn { border-style: dashed; }

/* ───────────────────────── slate ───────────────────────── */

.slate-scroll { overflow-x: auto; }
.slate { min-width: var(--slate-min); }

/* Text columns are minmax(floor, fr): at --slate-min they sit on their floors and every pixel
   past that is shared out, so the table tracks the window. The primitives strip stays FIXED —
   it is a glyph ruler, and stretching it would make the profile unreadable. */
.slate .hrow, .slate .brow {
  display: grid; align-items: center;
  grid-template-columns: var(--tracks);
}

/* the per-row remove control — quiet until the row is under the cursor, so an editable list
   still reads as a table rather than a form */
.c-rm { text-align: center; }
.c-rm button { padding: var(--s-1) var(--s-1); }
.brow:hover .c-rm button, .brow:focus-within .c-rm button { opacity: 1; }
.c-rm button:hover { color: var(--neg); }

.sep { border-left: 1px solid var(--rule); }

/* .band: the dropped table's heading band (progress.js). The slate's own band row (Identity /
   Demand / Evaluation) left on 2026-09-16 (owner call); the header row wears its ground. */
.band { background: var(--rule-soft); border-bottom: 1px solid var(--rule); }
.band > div {
  padding: var(--pad-cell); color: var(--ink-muted);
  font-family: var(--face-label); font-weight: var(--weight-label);
}

.hrow { background: var(--rule-soft); border-bottom: 1px solid var(--rule); }
.hrow > div {
  padding: var(--s-3); white-space: nowrap; cursor: pointer;
  transition: color var(--dur-state); font-family: var(--face-label); font-weight: var(--weight-label);
}
.hrow > div:hover { color: var(--ink); }
.hrow > div.nosort { cursor: default; }
.hrow > div.nosort:hover { color: var(--ink); }
.num { text-align: center; }

.brow { border-bottom: 1px solid var(--rule-soft); cursor: pointer; transition: background var(--dur-state); }
.brow:hover { background: var(--surface-raised); }
.brow.open { background: var(--surface-marked); }
.brow > div { padding: var(--s-3); }

/* The sticky title column needs its own opaque ground at every row state. The HEADER cell
   must stick too, or "Title" slides off while the titles hold their place. */
.c-sel { text-align: center; }
.c-title { position: sticky; left: 0; z-index: 2; transition: background var(--dur-state); }
.c-title { background: var(--surface); }
.hrow .c-title { background: var(--rule-soft); }
.brow:hover .c-title { background: var(--surface-raised); }
.brow.open .c-title { background: var(--surface-marked); }

.titlewrap { display: flex; gap: var(--s-2); align-items: center; }
.chev { flex: none; color: var(--ink); font-family: var(--face-mono); }
.thumb { flex: none; width: 38px; height: 57px; }
.titletext { min-width: 0; }
/* SCOPED. Unscoped, `.t` matched anything carrying the class — it was silently restyling the
   must-solve callout in the drawer to row-title size and weight. */
.c-title .t { font-weight: var(--weight-data); }
.fmt { display: block; color: var(--ink-muted); font-weight: 600; }
/* the row title's second line: the format chip, the volume chip and any source links sit in one
   wrapping tag row. The gap owns the spacing here, so the volume chip drops its own margin. */
.titlemeta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }
.titlemeta .serchip { margin-left: 0; }
/* the format chip reads as a name, not a label, so it skips .ks-label's uppercase and sits at the
   volume chip's size. Stored format casing varies by source ("book", "Manga", "game"), so the
   chip normalises it here rather than at every writer. */
.fmtchip { text-transform: capitalize; }

/* Country rides .c-genre — both are plain identity text, neither is a status chip. These WRAP
   rather than ellipsing (owner call): an author, a country and a genre list are the facts you
   scan to tell two same-titled works apart, and "Ursula K. Le Gu…" fails at exactly that. */
.c-author, .c-genre { white-space: normal; overflow-wrap: anywhere; }

/* the fill carries the score; the text colour follows the fill's own alpha (js/tokens.js
   heatText — accessibility floor, CLAUDE.md) since a fixed ink failed AA once the fill
   thickened past .5 alpha, measured 3.1:1 at the old alpha .72 */
.heat { display: inline-block; min-width: 36px; text-align: center; padding: var(--s-1) var(--s-2);
        border-radius: var(--r-control); font-weight: var(--weight-data); }

/* the three evaluation rings — Demand, Creative, Kismet — one shape, one size, the colour the
   only difference (slate.js ring). The arc and the numeral both read currentColor, so a ring's
   accent is set once on the wrapper. The track is the rule, like the drawer gauge's. */
.ring { position: relative; display: inline-grid; place-items: center; width: 36px; height: 36px;
        font-weight: var(--weight-data); vertical-align: middle; }
.ring svg { position: absolute; inset: 0; transform: rotate(-90deg); }
.ring circle { fill: none; stroke: var(--rule); stroke-width: 3; }
.ring .arc { stroke: currentColor; stroke-linecap: round; }
.ring-n { position: relative; }
.ring--demand { color: var(--demand); }
.ring--fit { color: var(--fit); }
.ring--kis { color: var(--ink); }

/* the works-row primitive strip (series_pure.js bars) */
.bars { display: inline-flex; align-items: flex-end; gap: var(--s-1); height: 24px; }
.bars i { display: block; width: 6px; border-radius: var(--r-media) var(--r-media) 0 0; }

.av { display: inline-block; padding: var(--s-1) var(--s-3); white-space: nowrap;
      font-weight: var(--weight-data); }
.av.unknown { color: var(--ink-faint); border: 1px solid var(--rule); }

.c-why { display: flex; gap: var(--s-2); white-space: normal; }
.c-why .dot { flex: none; width: 8px; height: 8px; border-radius: var(--r-pill); margin-top: var(--s-2); }
.dot.high { background: var(--pos) } .dot.mid { background: var(--ink-muted) } .dot.low { background: var(--neg) }

.none { color: var(--ink-muted); }

/* ── the operator surface's human levers ── */

/* the controlled-genre picker: a CLOSED vocabulary, so checkboxes rather than free text, and
   boxes disable at the cap instead of the server silently truncating a fourth pick */
.bk-pick { display: flex; flex-wrap: wrap; gap: var(--s-1) var(--s-3); align-items: center; }
.bk-pick label, .bk-survivors label { display: inline-flex; align-items: center; gap: var(--s-1);
                                      cursor: pointer; }
.bk-pick input:disabled + *, .bk-pick label:has(input:disabled) {
  color: var(--ink-faint); opacity: var(--off);
}
.bk-pick-act { display: flex; align-items: center; gap: var(--s-2); width: 100%; margin-top: var(--s-1); }

/* selecting entities for a manual merge, and the destructive control beside them */
.bk-pick-one { display: flex; align-items: center; }
.bk-del { margin-left: auto; align-self: start; color: var(--ink-muted); }

/* the manual-merge bar, hidden until two records are ticked because "merge" is meaningless
   below that; the survivor is an explicit radio, never an accident of ordering */
/* Backend Overview: count headers stay on one line; the table scrolls in its own box */
.bk-overview th { white-space: nowrap; }
.bk-overview .res-low { color: var(--neg); }
/* the Total row sits between brighter rules (the rule above it is the last body row's, since
   borders collapse); the % Total row under it reads lighter */
.bk-overview .ov-total > *, .bk-overview tbody tr:last-child > * { border-bottom-color: var(--ink-muted); }
.bk-overview .ov-share > * { font-weight: var(--weight-text); font-style: italic; }
.bk-mergebar { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
               background: var(--surface-raised); }
.bk-survivors { display: flex; gap: var(--s-3); flex-wrap: wrap; }

/* one flagged entity in the fidelity queue — the banner naming why it's here, Resolve/Dismiss,
   then the same editable entityCard() the Registry lane renders */
.bk-fidelity + .bk-fidelity { border-top: 1px solid var(--rule); }
.bk-fid-head { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-2); }
.bk-fid-head .lbl { margin-right: auto; }
.bk-fid-detail { margin: 0 0 var(--s-2); padding-left: var(--s-4); color: var(--ink-muted);
                  font-size: var(--type-data); }
.bk-fid-detail:empty { display: none; }

/* the stored Opus Resolve proposal — bordered and labelled as an opinion, the same "advisory,
   not a decision" framing the retired Ask Opus hint used, but now foldable into an Apply that
   actually executes what the human leaves checked below it. */
.mg-prop {
  margin-top: var(--s-2); padding: var(--s-2) var(--s-3); border: 1px solid var(--rule); border-radius: var(--r-control);
  display: flex; flex-direction: column; gap: var(--s-1);
}
.mg-prop .bk-hint-foot { display: block; margin-top: var(--s-1); color: var(--ink-muted); }
.mg-hint:empty { display: none; }
/* the per-identifier verdict chips ("target" vs. a flagged wrong-work/series-level id) */
.mg-chips { display: flex; flex-wrap: wrap; gap: var(--s-1); }
/* the drop list — one checkbox per identifier the proposal wants removed, all checked by
   default; only the ones still checked at Apply time ride into the request */
.mg-drops { display: flex; flex-direction: column; gap: var(--s-1); }
.mg-drop { display: flex; align-items: center; gap: var(--s-1); cursor: pointer; }
.mg-apply { align-self: flex-start; color: var(--ink-muted); }
/* Resolve now sits apart from the four irreversible controls, at the far right of the row */
.ecard-act .mg-resolve { margin-left: auto; }
/* one of .mg-resolve's three renders (the per-card "Resolve now") used to sit inside .ecard-act
   and pick up its colour/nowrap/transition there; the other two (#mg-kick, #mg-accept) did not.
   Folding all three onto one class means all three read alike now, matching the specimen's
   quiet-but-advisory "ask" family rather than differing by which container happened to hold
   them. */
.mg-resolve {
  color: var(--ink-muted); white-space: nowrap;
  transition: color var(--dur-state), border-color var(--dur-state);
}
/* .mg-del has one render, always inside the retired .ecard-act — same normalization, no change */
.mg-del {
  color: var(--ink-muted); white-space: nowrap;
  transition: color var(--dur-state), border-color var(--dur-state);
}
/* the queue-header sort toggle — a small quiet control, not a primary action */
.mg-sortbtn { color: var(--ink-muted); padding: var(--s-1) var(--s-3); }



/* the Creative Fit deep-read form — folded away until asked for: it is a thing you go and do,
   and the pane's job is the reading, not the re-reading */
.fit-deep { display: flex; flex-direction: column; gap: var(--s-2); }
.fit-deep textarea { background: var(--surface-raised); padding: var(--s-2) var(--s-3); resize: vertical; }
.fit-deep-act { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }

/* ── the filter lens ── chips read as OFF by default and light up on: the resting state of a
   slate is unfiltered, so the bar should look like nothing is happening until something is. */
.lens { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; padding: var(--s-3) var(--s-4) 0; }
.lens:empty { display: none; }
/* in the toolbar the lens is a GROUP inside a row, not a row of its own */
.toolbar .lens { padding: 0; }
.fchip { color: var(--ink-muted); }
.fchip.on { border-color: var(--ink); color: var(--ink); }
.fchip.fclear { border-style: dashed; }
/* The threshold pickers are SPLIT BUTTONS: one box, two targets. The label half toggles the
   axis, the caret half opens the stop list, the hairline between them says so before you click,
   and the caret sits INSIDE the border because an affordance belongs within the control it acts
   on. The BOX owns the border and the colour state so both halves move together. */
.fstop {
  position: relative; display: inline-flex; align-items: stretch;
  border: 1px solid var(--rule); border-radius: var(--r-control); color: var(--ink-muted);
  /* ONE width for the caret half and the select laid over it — sized separately they drift by
     the glyph's metrics and the overhang lands on the LABEL half. */
  --caret-w: 28px;
}
.fstop:hover { border-color: var(--ink); color: var(--ink); }
.fstop.on, .fstop:focus-within { border-color: var(--ink); }
.fstop.on { color: var(--ink); }
.fstop-k { background: none; border: 0; padding: var(--s-1) var(--s-3); color: inherit; }
/* `inherit` on a border colour takes the BOX's, not the text's — the divider is a seam in the
   border, so it must track the border and not the brighter label */
.fstop-c {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--caret-w); border-left: 1px solid; border-color: inherit; pointer-events: none;
}
/* The caret is an SVG MASK, and the mask IS the alignment fix: the ink fills its own viewBox
   symmetrically, so flex centring has nothing left to get wrong and there is no counterweight
   to tune. The two earlier versions — the "⌄" glyph and a rotated square — both centred a box
   whose ink sat elsewhere inside it, and rasterized differently per engine. `stroke` here is a
   STENCIL, not a colour: a mask reads alpha or luminance and white shows under both, while the
   visible colour comes from `background: currentColor`, so the caret tracks all three states. */
.fstop-c::before {
  content: ""; width: 8px; height: 5px; background: currentColor;
  --caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 5'%3E%3Cpath d='M0.5 0.75L4 4.25L7.5 0.75' fill='none' stroke='white' stroke-width='1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  -webkit-mask: var(--caret) center / 8px 5px no-repeat;
  mask: var(--caret) center / 8px 5px no-repeat;
}
/* a real <select>, laid over the caret half and invisible: native keyboard, native touch, no
   popup of our own to keep alive. Focus shows on the box, since the thing taking it cannot. */
.fstop-sel {
  position: absolute; right: 0; top: 0; height: 100%; width: var(--caret-w);
  opacity: 0; cursor: pointer; border: 0; font: inherit;
}
/* the open list is drawn by the OS, so its options carry the palette or inherit a white menu */
.fstop-sel option { background: var(--surface-raised); color: var(--ink); }
/* a disabled fstop dims the BOX, not each half separately — a caret left bright beside a dead
   label reads as a control that still does something. The overlay select is INVISIBLE by design
   (opacity 0 above) – disabled must not reveal it, so it keeps 0 while the visible half keeps 1. */
.fstop-k:disabled { opacity: 1; }
.fstop-sel:disabled { opacity: 0; }

.sig { display: inline-block; padding: var(--s-1) var(--s-2); white-space: nowrap; }
/* the tool's OWN read is the one that carries weight, so it is the one that gets colour */
.sig-em { color: var(--ink); border-color: var(--ink); font-weight: 600; }
.sig-nyt { color: var(--ink); }

/* ───────────────────────── sub-tabs ───────────────────────── */

/* one row below the main nav, quieter than the tabs — a lane within a surface, not a surface */
.subtabs { display: flex; gap: var(--s-1); flex-wrap: wrap; border-bottom: 1px solid var(--rule); }
/* data size (slice 5) — stays at weight-text, quieter than a drawer tab's weight-data, since
   this row is itself the quieter one per the comment above */
.subtab {
  background: none; border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px;
  padding: var(--s-2) var(--s-4); color: var(--ink-muted); transition: color var(--dur-state); font-weight: var(--weight-data); }
.subtab:hover { color: var(--ink); }
.subtabs .tabdiv { align-self: center; }
/* count tag — empty until its queue loads */
.subtab .ks-chip { margin-left: var(--s-2); padding: 0 var(--s-2); }
.subtab .ks-chip:empty { display: none; }
.subtab.active { color: var(--ink); border-bottom-color: var(--ink); }

/* ───────────────────────── explore cards ───────────────────────── */

.ecards {
  display: grid; gap: var(--s-1); background: var(--rule);
  grid-template-columns: repeat(auto-fill, minmax(420px, 1fr));
}
.ecard { display: grid; grid-template-columns: 92px 1fr; gap: var(--s-4);
         padding: var(--pad-card); background: var(--surface); }

.ecard-art { position: relative; }
.ecard-art img { width: 92px; border-radius: var(--r-control); display: block; }
/* the rank badge sits ON the art — the position IS the list's claim about the title */
.ecard-rank {
  position: absolute; top: -4px; left: -6px; min-width: 20px; text-align: center;
  padding: var(--s-1) var(--s-1); border-radius: var(--r-control); background: var(--demand); color: var(--ground);
  font-weight: var(--weight-data);
}
.ecard-body { display: flex; flex-direction: column; gap: var(--s-2); min-width: 0; }
.ecard-head { display: flex; flex-direction: column; gap: var(--s-1); }
.ecard-head .t { font-weight: var(--weight-data); }
.ecard-chips { display: flex; gap: var(--s-1); flex-wrap: wrap; }
.ecard-note {
  margin: 0; color: var(--ink-muted);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.ecard-act { display: flex; gap: var(--s-2); align-items: center; margin-top: auto; padding-top: var(--s-1); }
/* the members the old `.ecard-act button` descendant rule reached (.mg-del and .mg-resolve
   carry the same colour/nowrap/transition on their own rule, above) — an <a> needs the explicit
   underline reset a <button> never did */
/* the quiet variant: muted at rest, no underline, never wraps – a modifier, not a list (law 07) */
.ks-btn--quiet {
  color: var(--ink-muted); text-decoration: none; white-space: nowrap;
  transition: color var(--dur-state), border-color var(--dur-state);
}

/* ───────────────────────── backend ───────────────────────── */

.wl-pick input[type="text"] {
  background: var(--surface); padding: var(--s-2) var(--s-3); min-width: 340px;
}

/* Backend is the OPERATOR surface and is deliberately denser than the rest (owner call): a
   workbench for scanning dozens of identifiers, not a report to read. Values are monospaced so
   ids line up column-wise and a transposed digit is visible. */
.bk-entity { padding: var(--s-3) var(--s-4); border-top: 1px solid var(--rule); }
.bk-entity:first-child { border-top: 0; }
.bk-ent-head { display: flex; gap: var(--s-3); align-items: flex-start; margin-bottom: var(--s-2); }
.bk-ent-head .thumb { width: 34px; height: 51px; }
.bk-ent-head .t { font-weight: var(--weight-data); margin-bottom: var(--s-1); }
.bk-sec { display: block; margin-bottom: var(--s-1); }
/* the count riding beside a section label reads as body text, not as part of the label — un-
   inherits the .lbl ancestor's uppercase/tracking; font-weight is left to inherit --weight-label
   (500) from that same ancestor, the one --face consumer of it */
.bk-sec-n {
  font-family: var(--face); text-transform: none; letter-spacing: var(--track-data);
  color: var(--ink-muted); font-size: var(--type-data);
}
.bk-level { text-transform: uppercase; }
.bk-level-sel { padding: var(--s-1) var(--s-1); }
.bk-rej-head { margin-top: var(--s-3); padding-top: var(--s-3); border-top: 1px solid var(--rule); }
.bk-cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-1) var(--s-5); align-items: start; }

/* label · value · provenance · action — one grid so every row's columns align down the card */
.bk-row {
  display: grid; grid-template-columns: 92px 1fr auto 20px;
  gap: var(--s-2); align-items: baseline; padding: var(--s-1) 0; min-height: 20px;
}
.bk-row .lbl { font-size: var(--type-data); }
.bk-val { font-family: var(--face-mono); word-break: break-word; }
.bk-idrow { grid-template-columns: 150px 1fr; }

/* the edit pencil stays quiet until the row is under the cursor — the card should read as a
   record first and a form second */
.bk-edit { padding: 0; }
.bk-row:hover .bk-edit, .bk-row:focus-within .bk-edit { opacity: 1; }
.bk-edit:hover { color: var(--ink); }
/* the pen-name repair buttons (identityRow) – same reveal-on-row-hover contract as .bk-edit
   above; unlink mirrors .bk-rm-id's remove colour, reverse mirrors .bk-verify's affirm colour */
.bk-row:hover .bk-unlink, .bk-row:focus-within .bk-unlink,
.bk-row:hover .bk-reverse, .bk-row:focus-within .bk-reverse { opacity: 1; }
.bk-unlink, .bk-reverse { padding: 0 var(--s-1); }
.bk-unlink:hover { color: var(--neg); }
.bk-reverse:hover { color: var(--pos); }
.bk-input { background: var(--ground); padding: var(--s-1) var(--s-1); width: 100%; }

.bk-msg { margin: 0 0 var(--s-2); min-height: 0; }
.bk-msg:empty { display: none; }
.bk-msg.err { color: var(--neg); }
/* a contested identity is not a failure — it is a state the operator created and must see */
.bk-msg.warn { color: var(--ink); }

.idvals { display: flex; gap: var(--s-1); flex-wrap: wrap; }
/* verified vs provisional is the registry's whole point, so it is visible on every value */
.idval {
  font-family: var(--face-mono);
  display: inline-flex; align-items: center; gap: var(--s-1);
  border-bottom: 1px solid transparent; padding-bottom: var(--s-1);
}
.idval a { color: inherit; text-decoration: none; }
.idval a:hover { color: var(--ink); }
.idval.ok { color: var(--ink); border-bottom-color: var(--pos); }
.idval.prov { color: var(--ink-muted); border-bottom-color: var(--rule); }
/* the origin anchor: which identifier this entity was minted from — the one id on the card a
   cleanup decision hangs on. Muted, not an accent (slice 4): it names no demand/fit/pos/neg
   fact. Owner decision 2026-09-02: a colour-only cue is not enough, so a text label rides
   beside the mark (the .ks-label shape stated directly, since the pair sits inside an .idval
   chip rather than a labelled row) — the tooltip stays, this is in ADDITION to it, and the
   label is --ink (readable) while the mark itself stays muted (a note, not the datum). */
.idorigin { color: var(--ink-muted); line-height: 1; cursor: help; }
.idorigin-label { color: var(--ink); font-family: var(--face-label); font-weight: var(--weight-label);
                   margin-right: var(--s-1); cursor: help; }
.idval button { padding: 0 var(--s-1); }
.bk-idrow:hover .idval button, .bk-idrow:focus-within .idval button { opacity: 1; }
.bk-verify:hover { color: var(--pos); }
.bk-rm-id:hover, .bk-rm-tag:hover { color: var(--neg); }

.bk-addid { display: flex; gap: var(--s-1); align-items: center; margin-top: var(--s-2); }
.bk-addid select, .bk-addid input { background: var(--ground); padding: var(--s-1) var(--s-2); }
.bk-addid input { flex: 1; min-width: 0; }
.bk-add-go { color: var(--ink-muted); }
/* The series-position editor sits INLINE in the Series row's sentence, so it borrows the
   shared field/button primitives and only sets what an inline control needs: a number-wide
   box and the row's own baseline. */
.bk-pos { display: inline-flex; gap: var(--s-1); align-items: baseline; }
.bk-pos-val { background: var(--ground); padding: 0 var(--s-1); width: 3.5em; }
.bk-pos-go { color: var(--ink-muted); }

/* The Opus review section (2026-08-27 design section 4) — reads like the stored Opus Resolve
   proposal above it (.mg-prop): bordered, labelled as an opinion, one Apply per suggestion. */
.audit {
  margin-top: var(--s-3); padding: var(--s-3); border: 1px solid var(--rule); border-radius: var(--r-control);
  display: flex; flex-direction: column; gap: var(--s-2);
}
.audit p { margin: 0; }
.audit header { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
.audit-ts { color: var(--ink-muted); }
.audit-row { display: flex; gap: var(--s-2); align-items: baseline; }
.audit-why { color: var(--ink-muted); font-style: italic; }
/* two distinct micro-labels sharing one shape — "manual" (a note, unactionable here) and
   "applied" (this human just pressed Apply) are opposite states and must be able to diverge in
   colour without one rule silently carrying both */
.audit-manual, .audit-applied { text-transform: uppercase; letter-spacing: var(--track-micro); }
.audit-manual { color: var(--ink-muted); }
.audit-applied { color: var(--pos); }
.audit-run, .audit-apply { color: var(--ink-muted); }

/* Both sides start at the TOP. They were centred, which was fine while each carried one line of
   identifiers and became unreadable once they carry the full grouped table: a side with two ids
   floated to the vertical middle of a side with forty, so the two titles no longer sat on the
   same line and the eye had nothing to compare across. */
.mg-pair { display: grid; grid-template-columns: 1fr auto 1fr; gap: var(--s-3); align-items: start;
           margin: var(--s-2) 0; }
.mg-side { display: flex; flex-direction: column; gap: var(--s-1); min-width: 0; }
.mg-side .t { font-weight: var(--weight-data); }
/* the section label needs air above it, but not on the chips/title/note stack it follows */
.mg-idhead { margin-top: var(--s-2); }
/* sits on the TITLE line rather than the middle of the row — centring it in a forty-id card put
   the word half a screen below the two things it joins. Decorative divider, carries no data,
   so it is faint rather than muted (owner decision, 2026-09-02). */
.mg-vs { color: var(--ink-faint); padding-top: var(--s-5); }

/* A merged pair does not leave the queue — it becomes the survivor's own editable card and stays
   until the page is left, because a merge is usually right about the pair and only about 90%
   right about the RESULT. The done edge marks it as settled work sitting above live work. */
.mg-done { border-left: 2px solid var(--pos); padding-left: var(--s-3); margin-bottom: var(--s-3); }
.mg-done .bk-entity { border-top: 0; }

/* Coverage: one row per Format × Market cell. */
.cov { display: flex; flex-direction: column; }
.cov-row {
  display: grid; align-items: center; gap: var(--s-3);
  /* Format·Market | Axes | Entities | Cross-matched | Anchor | Metadata | Tier | Limited by */
  grid-template-columns: minmax(200px, 1.6fr) 150px 80px 118px minmax(150px, 1fr) 90px 90px
                         minmax(120px, 1fr);
  padding: var(--s-1) var(--s-4); border-top: 1px solid var(--rule);
}
/* the column-header row: the .ks-label shape stated directly, since .cov-head isn't part of
   the shared label rule above (the coverage cells are their own grid, not .hrow) */
.cov-head {
  border-top: 0; color: var(--ink-muted); font-size: var(--type-micro);
  font-family: var(--face-label); font-weight: var(--weight-label);
  text-transform: uppercase; letter-spacing: var(--track-micro);
}
.cov-row .t { font-weight: 600; }
.cov-axes { display: flex; gap: var(--s-1); }
.cov-ax { padding: var(--s-1) var(--s-1); border-radius: var(--r-control); }
/* the capping axis is the finding — it is the only one that gets emphasis */
.cov-ax.capping { font-weight: var(--weight-data); }
.cov-tier { padding: var(--s-1) var(--s-2); font-weight: var(--weight-data); }
.cov-tier.t-partial { color: var(--ink-muted); border-color: var(--ink-muted); }

/* ───────────────────────── Backend › Rights review ───────────────────────── */

/* The census is descriptive and carries no accent (law 9) — it says how much evidence exists,
   not whether anything is wrong. */
.rq-census { display: flex; flex-wrap: wrap; gap: var(--s-4); align-items: baseline; }
.rq-cell b { font-weight: var(--weight-data); }
.rq-group + .rq-group { border-top: 1px solid var(--rule); }
.rq-head { padding: var(--s-3) var(--s-4) var(--s-1); color: var(--ink-muted); }
.rq-row { padding: var(--s-3) var(--s-4); border-top: 1px solid var(--rule-soft); }
.rq-row .t { font-weight: var(--weight-data); }
.rq-grain { font-family: var(--face-mono); }
/* the evidence block: verbatim document text, so it is quoted and height-clamped rather than
   truncated — a snippet cut server-side would be evidence we edited */
.rq-ev { margin: var(--s-2) 0; border-left: 2px solid var(--rule); padding-left: var(--s-3); }
.rq-snip { max-height: var(--s-6); overflow-y: auto; }
.rq-form { display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center;
           margin-top: var(--s-2); }
.rq-form label { display: flex; flex-direction: column; flex: 1 1 16rem;
                 gap: var(--s-1); min-width: 0; max-width: 100%; }
.rq-form .ks-field { width: 100%; }
.rq-msg:empty { display: none; }
.rq-msg { margin-top: var(--s-2); color: var(--neg); }

.ig-head { padding: var(--pad-card); display: flex; flex-direction: column; gap: var(--s-2); }
.ig-sec { display: flex; flex-direction: column; gap: var(--s-2); }
.ig-sec .pulse-row { padding: var(--s-2) 0; }

/* Explore › Amazon: bestsellers and climbers are two reads of ONE bucket, so they sit as a
   PAIR. min-width: 0 because a grid child floors at its content width — without it a long
   title pushes its column wide and starves the other. */
.az-cols { display: grid; grid-template-columns: 1fr 1fr; }
.az-cols > .ig-sec { min-width: 0; }
.az-cols > .ig-sec + .ig-sec { border-left: 1px solid var(--rule); }
/* one card per column here: .ecards' 420px track was sized for the full content width */
.az-cols .ecards { grid-template-columns: minmax(0, 1fr); }

/* ───────────────────────── the reference breakdown (DNA) ───────────────────────── */

/* between the chips and the reasoning stream: input, then the derived read, then the working */
.dna-head { margin-bottom: var(--s-3); }
.brief-dna.collapsed .dna-head { margin-bottom: 0; }

/* flat sections matching the reasoning block below — a bordered card here sat inside the brief
   card and read as a nested panel */
.dna-sec + .dna-sec { margin-top: var(--s-3); padding-top: var(--s-3); border-top: 1px solid var(--rule); }
.dna-title { display: flex; align-items: baseline; gap: var(--s-2); flex-wrap: wrap; margin-bottom: var(--s-2); }
.dna-title .t { font-weight: var(--weight-data); }
.dna-row { display: grid; grid-template-columns: 108px 1fr; gap: var(--s-3); align-items: baseline;
           padding: var(--s-1) 0; }
.dna-row .lbl { font-size: var(--type-data); }

/* each phase appends its own titled section, so a two-comp brief keeps both readable */
.think-sec + .think-sec { margin-top: var(--s-3); padding-top: var(--s-2); border-top: 1px solid var(--rule); }
.think-sec-lbl { color: var(--ink-muted); margin-bottom: var(--s-1); }
/* with one section the label just repeats the panel header verbatim */
.think-sec:only-child .think-sec-lbl { display: none; }

/* the rights read streams into the drawer's Availability tab — a pane, not a card section, so
   it needs the hairline without the card's gutter */
.avail-think { border-top: 1px solid var(--rule); margin-top: var(--s-3); padding-top: var(--s-3); }

/* one search the model ran. NOT the model's voice — a FACT about the run — so it drops the
   surrounding prose's italic: which sources it reached for is a confidence signal. */
.think-note { font-style: normal; margin: var(--s-1) 0 var(--s-1); }
.think-tool { color: var(--ink-muted); margin-right: var(--s-2); }
.think-q { color: var(--ink); }

/* ───────────────────────── bulk actions, pager, modal ───────────────────────── */

/* the selection count is the one thing in the toolbar that appears and disappears, so it
   carries the accent — it is a state you are IN, not a label */
.sel-note { color: var(--ink); }
.sel-note:empty { display: none; }
/* .fstop is EXEMPT from the select styling: it carries its own states on the BOX, and this
   would repaint the overlay select that is meant to be invisible. */
.toolbar select:not(.fstop-sel), .pager select {
  background: var(--surface); padding: var(--s-1) var(--s-2); cursor: pointer;
}

/* the pager mirrors the toolbar on the FOOT of the card. A disabled Prev/Next stays in place
   rather than disappearing — the row must not change width as you reach either end. */
.pager { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.pg-note { color: var(--ink-muted); }

.modal-back {
  position: fixed; inset: 0; z-index: 50; display: flex; align-items: center;
  justify-content: center; background: var(--scrim); padding: var(--pad-pane);
}
.modal {
  padding: var(--pad-pane); width: 100%; max-width: 440px;
  display: flex; flex-direction: column; gap: var(--s-2);
}
.modal-title { margin: 0; font-size: var(--type-section); font-weight: var(--weight-data); letter-spacing: var(--track-section); }
.modal-sub { margin: 0 0 var(--s-1); color: var(--ink-muted); }
.modal select, .modal input[type="text"] {
  background: var(--ground); padding: var(--s-2) var(--s-3); width: 100%;
}
.modal-msg { margin: 0; min-height: 1.2em; color: var(--neg); }
.modal-act { display: flex; gap: var(--s-2); justify-content: flex-end; margin-top: var(--s-1); }

/* B3 decisions and Feedback: the native <dialog> takes the .modal card look; the four kinds are toggle buttons */
dialog.modal { border: 0; color: inherit; }
/* every dialog form is one column at the modal's own gap — Decide and Feedback both */
dialog.modal form { display: flex; flex-direction: column; gap: var(--s-2); }
dialog.modal::backdrop { background: var(--scrim); }
.dec-kinds { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.dec-kind input { position: absolute; opacity: 0; pointer-events: none; }
.dec-kind:has(input:checked) { border-color: var(--ink); color: var(--ink); }
.dec-kind:has(input:focus-visible) { outline: 2px solid var(--ink); outline-offset: 2px; }
.dec-known { display: flex; gap: var(--s-2); align-items: center; color: var(--ink-muted); }
.dec-btn { white-space: nowrap; }
.dec-passed, .dec-dismissed { color: var(--ink-muted); }

/* the BookTok evidence rows (Explore's Ignition board) */
.pulse-row:first-child { border-top: 0; }
.pulse-title { color: var(--ink-muted); }
.pulse-status { margin-left: var(--s-2); }
.pulse-caption { color: var(--ink-muted); margin-top: var(--s-1); }

.legend {
  display: flex; gap: var(--s-4); align-items: center; flex-wrap: wrap;
  color: var(--ink-muted);
}
.lg { display: inline-flex; align-items: center; gap: var(--s-1); }
.sw-fit { width: 10px; height: 10px; background: var(--rule); }
.sw-fit.on { background: var(--fit); }

/* the app talking to the user — often the only thing on screen, so it is not dimmed */
.status { padding: var(--pad-pane); }
.status.err { color: var(--neg); }

/* the spinner: proof the run is alive through the long silent stretches. Written once and never
   re-rendered — rebuilding the element restarts the animation and it stutters. */
.spinner {
  display: inline-block; width: 12px; height: 12px; margin-right: var(--s-2); vertical-align: -2px;
  border: 2px solid var(--rule); border-top-color: var(--ink); border-radius: var(--r-pill);
  animation: spin .7s linear infinite;
}
.spinner.sm { width: 10px; height: 10px; margin-right: 0; vertical-align: 0; }
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 2.4s; } }

/* ── the live candidate board: the loading view a brief gets instead of one status line ── */
#board:not(.hidden) { display: block; }
.board-head { display: flex; align-items: baseline; gap: var(--s-3); flex-wrap: wrap; }
.board-n { color: var(--ink-muted); font-weight: var(--weight-data); }
.board-hint { color: var(--ink-muted); }
.board-list { list-style: none; margin: 0; padding: 0; }
.board-row { display: grid; grid-template-columns: 20px 1fr auto auto; gap: var(--s-3);
             align-items: center; padding: var(--s-2) var(--s-4);
             border-bottom: 1px solid var(--rule-soft); }
.board-state { display: inline-flex; justify-content: center; }
.tick { color: var(--pos); } .dot { color: var(--ink-faint); }
.board-by { color: var(--ink-muted); }
.board-prov, .gate-verdict {
  font-weight: 600; border-radius: var(--r-control); padding: var(--s-1) var(--s-1); white-space: nowrap;
}
.board-note { color: var(--ink-muted); white-space: nowrap; }

/* ── the dropped table: what was considered and not slated ── */
.toolbar[data-toggle] { cursor: pointer; }
.toolbar[data-toggle]:hover .lbl { color: var(--ink); }
#dropped-card .band { display: grid; grid-template-columns: 1fr auto; }
.dcount { color: var(--ink-muted); text-align: right; }
.drow { display: grid; grid-template-columns: minmax(240px, 1fr) 2fr auto; gap: var(--s-4);
        align-items: baseline; border-bottom: 1px solid var(--rule-soft); }
.dtitle { font-weight: var(--weight-data); }
.dreason { display: flex; align-items: baseline; gap: var(--s-2); flex-wrap: wrap; color: var(--ink-muted); }
.gate-verdict.v-near_miss { color: var(--ink-muted); border-color: var(--ink-muted); }
.drop-note { flex-basis: 100%; font-style: italic; }
/* the way back in: each row's Promote action. An AUTO track, so adding it left the title and
   reason columns exactly the widths they had. */
.dact { display: flex; align-items: baseline; justify-content: flex-end; gap: var(--s-2);
        white-space: nowrap; }
.dnote { color: var(--ink-muted); }
.dnote.ok { color: var(--pos); }
.dnote.err { color: var(--neg); white-space: normal; }

/* ── availability tab ── ONE row shape across every section — a fixed WHEN column then the
   content — so the prose shares a left edge from Dealmaking through Fan reception. */
.avhead { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.avlede { margin: 0; line-height: 1.7; }
.avhead + .avlede { margin-top: var(--s-3); }
.avlede + .avlede { margin-top: var(--s-2); }
.avtag { color: var(--ink-muted); font-weight: 600; white-space: nowrap; }
.avname { font-weight: var(--weight-data); }
.avrow { display: grid; grid-template-columns: 96px 1fr; gap: var(--s-4); align-items: baseline;
         padding: var(--s-2) 0; border-bottom: 1px solid var(--rule-soft); }
.avrow:last-child { border-bottom: 0; }
/* nowrap in a fixed track, so a malformed free-text date truncates instead of colliding */
.avwhen { color: var(--ink-muted); font-variant-numeric: tabular-nums; white-space: nowrap;
          overflow: hidden; text-overflow: ellipsis; }
.avrow--undated { grid-template-columns: minmax(0, 1fr); }
.avbody { line-height: 1.7; min-width: 0; overflow-wrap: anywhere; }
.avbody .avtag { margin-left: var(--s-2); }
.avcontacts { width: 100%; table-layout: fixed; border-collapse: collapse; }
.avcontacts th, .avcontacts td { padding: var(--s-2) 0; text-align: left; vertical-align: top;
                                border-bottom: 1px solid var(--rule-soft); }
.avcontacts th { width: 10em; padding-right: var(--s-4); font-weight: var(--weight-text); color: var(--ink-muted); }
.avcontacts td { overflow-wrap: anywhere; }
.avcontacts tr:last-child > * { border-bottom: 0; }
/* the "not this title" reject on an unverified IMDbPro row: a remove control, revealed by its row */
.avrow:hover [data-imdbpro-reject], .avrow:focus-within [data-imdbpro-reject] { opacity: 1; }
[data-imdbpro-reject]:hover { color: var(--neg); }
/* attribution trails its claim as a footnote, never as another box */
.avcite { color: var(--ink-muted); text-decoration: none; border-bottom: 1px dotted var(--rule);
          white-space: nowrap; }
a.avcite:hover { color: var(--ink); border-bottom-color: var(--ink); }

/* ── series drawer: the Works tab ── one row per known volume (series-surfaces design §4) */
/* Two of the row's 7 logical columns are conditionally present (the carries-mark, the Scan/
   Rescan button — dropped entirely on a "scanning" row): a missing DOM child shifts every grid
   item after it via auto-placement, so every column is pinned explicitly by name rather than by
   position. `.bars` is shared with slate.js's own table row, hence scoped to `.works-row`. */
.works { display: flex; flex-direction: column; }
.works-row { display: grid; grid-template-columns: 32px minmax(160px, 2fr) 56px 128px minmax(96px, 1fr) minmax(220px, 2fr) auto;
             gap: var(--s-3); align-items: center; padding: var(--s-2) 0; border-bottom: 1px solid var(--rule-soft); }
.works-row:last-child { border-bottom: 0; }
.works-pos { grid-column: 1; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.works-title { grid-column: 2; background: none; border: 0; padding: 0; color: var(--ink); text-align: left; cursor: pointer; font: inherit; }
.works-title:hover { text-decoration: underline; }
.works-year { grid-column: 3; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.works-row .bars { grid-column: 4; }
.works-carry { grid-column: 5; color: var(--ink-muted); }
.works-cell { grid-column: 6; display: flex; gap: var(--s-3); align-items: center; }
.works-scan { grid-column: 7; }

/* ───────────────────────── drawer ───────────────────────── */

.drawer { border-bottom: 1px solid var(--rule); }

.dw-hero {
  display: grid; grid-template-columns: 168px 1fr; gap: var(--s-5);
  padding: var(--pad-pane); background: var(--ground); border-bottom: 1px solid var(--rule);
}
.dw-cover { width: 168px; height: 252px; border-radius: var(--r-surface); overflow: hidden;
            border: 1px solid var(--rule); background: var(--rule-soft);
            display: grid; place-items: center; }
.dw-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.dw-right { display: flex; flex-direction: column; gap: var(--s-4); min-width: 0; }

.dw-links { display: flex; align-items: baseline; gap: var(--s-4); flex-wrap: wrap; }
.dw-link { font-weight: var(--weight-data); text-decoration: none; border-bottom: 1px solid var(--rule); }
.dw-link:hover { border-bottom-color: var(--ink); }
.dw-cached { margin-left: auto; color: var(--ink-muted); }

.dw-metrics { display: flex; gap: var(--s-5); align-items: center; flex-wrap: wrap; }
.gauge-wrap { display: flex; gap: var(--s-3); align-items: center; }
.gauge { position: relative; width: 104px; height: 104px; flex: none; }
.gauge svg { display: block; }
.gauge .val { position: absolute; inset: 0; display: flex; flex-direction: column;
              align-items: center; justify-content: center; }
.gauge .d { color: var(--ink-muted); font-family: var(--face-label); font-weight: var(--weight-label); }
.gauge-label { font-family: var(--face-label); font-weight: var(--weight-label); }
.gauge-label.demand { color: var(--demand) } .gauge-label.fit { color: var(--fit) }
.gauge-note { color: var(--ink-muted); max-width: 120px; line-height: 1.45; }
.band-chip { display: inline-block; margin-top: var(--s-2); border: 1px solid var(--rule);
             border-radius: var(--r-control); padding: var(--s-1) var(--s-2);
             font-family: var(--face-label); font-weight: var(--weight-label); }

.sigbars { display: flex; flex-direction: column; gap: var(--s-2); min-width: 280px; flex: 1; }
.sigrow { display: grid; grid-template-columns: 108px 1fr 36px; gap: var(--s-3); align-items: center; }
.sigrow .k { font-family: var(--face-label); font-weight: var(--weight-label); }
.sigrow .v { text-align: right; font-weight: var(--weight-data); }

/* transparent, not the primitive's default surface ground — .quad sits over whatever pane hosts it */
.quad { position: relative; width: 140px; height: 140px; flex: none; background: none; }
.quad .vx { position: absolute; left: 50%; top: 8px; bottom: 8px; width: 1px; background: var(--rule); }
.quad .hz { position: absolute; top: 50%; left: 8px; right: 8px; height: 1px; background: var(--rule); }
.quad .pt { position: absolute; width: 6px; height: 6px; border-radius: var(--r-pill); background: var(--ink-muted); }
.quad .pt.me { width: 12px; height: 12px; background: var(--demand);
               box-shadow: 0 0 0 4px rgba(var(--demand-rgb),.25); }
.quad .ax { position: absolute; color: var(--ink-faint); font-weight: 600; }
.quad .ax.y { top: 6px; left: 8px } .quad .ax.x { bottom: 6px; right: 8px }

.dw-line { display: flex; gap: var(--s-4); align-items: baseline; }
.dw-line.top { border-top: 1px solid var(--rule); padding-top: var(--s-4); }
.dw-line .lb { flex: none; min-width: 74px; font-family: var(--face-label); font-weight: var(--weight-label); }
.dw-line .lb.gold { color: var(--ink-muted) } .dw-line .lb.blue { color: var(--fit) }
.logline { font-weight: var(--weight-data); line-height: 1.45; }
/* the verdict reads as the logline's pair — same size and weight, italic to mark it as the
   tool's judgement rather than the book's own description */
.verdict { font-weight: var(--weight-data); line-height: 1.45; font-style: italic; }

/* Two things, both about a stray scrollbar this row used to grow. overflow-y is EXPLICIT
   because when one axis is not `visible` the other computes to `auto`; and the hairline is a
   BACKGROUND, not a border, so the active tab's underline sits on top of it without the
   `margin-bottom: -1px` that pushed scrollHeight 1px past clientHeight. */
.dw-tabs { display: flex; gap: var(--s-5); padding: 0 var(--s-5);
           background: linear-gradient(to top, var(--rule) 1px, transparent 1px);
           overflow-x: auto; overflow-y: hidden; }
/* data at weight-data (slice 5) — a tab is read like a row title, not named like a label, so
   it drops the uppercase treatment it used to inherit from the label rule */
.dw-tab { padding: var(--s-3) var(--s-1); cursor: pointer; white-space: nowrap; font-weight: var(--weight-data);
          color: var(--ink-muted); border-bottom: 2px solid transparent;
          transition: color var(--dur-state); }
.dw-tab:hover, .dw-tab.is-on { color: var(--ink); }
.dw-tab.is-on { border-bottom-color: var(--ink); }

.pane { padding: var(--pad-pane); display: flex; flex-direction: column; gap: var(--s-5); }

/* overview — two halves: summary + beats left, reception right */
.ov-split { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-5); align-items: start; }
.ov-half { display: flex; flex-direction: column; gap: var(--s-5); }
/* one column inside a half — the beats had the pane's full width to run two abreast */
.beats { display: grid; grid-template-columns: 1fr; gap: var(--s-4); }
/* the numbered marker — a key beside the beat's own heading, so it takes the label shape.
   An ordinal beside a heading or value is a SECTION NUMERAL, faint — it locates, it is never
   itself the datum (owner decision, 2026-09-02; .terr-row .rk carries the same rule). */
.beat .n { color: var(--ink-faint); font-family: var(--face-label); font-size: var(--type-micro);
           font-weight: var(--weight-label); letter-spacing: var(--track-micro); margin-right: var(--s-2); }
.beat .h { font-weight: var(--weight-data); }
/* inline grounding citations (profile drawer, 2026-09-13) — a numbered superscript per Gemini
   grounding support, linking straight to its resolved source. Same shape as the section-numeral
   rule above: the number LOCATES a claim, it is never itself the datum, so it stays faint and
   never borrows an accent (accents are data — law 3/4). */
.cite { margin-left: var(--s-1); }
.cite-link { color: var(--ink-faint); text-decoration: none; font-family: var(--face-label);
             font-weight: var(--weight-label); font-size: var(--type-micro); letter-spacing: var(--track-micro); }
.cite-link:hover { color: var(--ink); }
.cite-link + .cite-link { margin-left: var(--s-1); }
.icard { padding: var(--pad-card); }
/* data, matching the drawer tabs (slice 5) — card headings inside a pane, a rank below the
   pane's own; size drops out (inherits data), and the weight overrides the h1/h4 reset above */
.icard h4 { font-weight: var(--weight-data); margin-bottom: var(--s-2); }
.icard.love h4 { color: var(--pos) } .icard.dislike h4 { color: var(--neg) }
/* pane foot — the action row under a rule; Overview's ↻, Power's Basis + Deep read, Kismet's save */
.pane-foot { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
             border-top: 1px solid var(--rule); margin-top: var(--s-5); padding-top: var(--s-4); }
.ov-note { color: var(--ink-muted); }
.knows-line { color: var(--ink-muted); }

/* demand */
.blk-head { display: flex; align-items: baseline; gap: var(--s-4); flex-wrap: wrap; margin-bottom: var(--s-3); }
/* pane/block heading (slice 5) — the .ks-label shape's face at the section step, sentence
   case: --type-section is never uppercase (design system §02) */
.blk-title {
  font-size: var(--type-section); font-family: var(--face-label);
  font-weight: var(--weight-data); letter-spacing: var(--track-section);
}
.blk-sub, .lg-chip { color: var(--ink-muted); }
.lg-chip { display: inline-flex; align-items: center; gap: var(--s-2); font-weight: 600; }
/* the demand chart's right-axis key — the SAME token the polyline strokes with, so the
   swatch cannot name a colour the chart does not draw */
.lg-chip .solid { width: 16px; height: 2px; background: var(--trend-web); }

/* the two-axis key: left-axis series on the left, right-axis series on the right, so which
   axis a line reads against is positional rather than inferred */
.axkeys { display: flex; justify-content: space-between; gap: var(--s-5); flex-wrap: wrap;
          margin: var(--s-1) 0 var(--s-1); }
.axkey.right { text-align: right; }
.axnote { color: var(--ink-faint); letter-spacing: var(--track-data); font-weight: 400; }
.axitems { display: flex; gap: var(--s-4); flex-wrap: wrap; margin-top: var(--s-1); }
.axkey.right .axitems { justify-content: flex-end; }

.trend { position: relative; }
/* the LEFT axis's own labels, on the axis they describe — not in the date row underneath */
.ycap, .yzero { position: absolute; left: 0; color: var(--ink-faint); pointer-events: none; }
.ycap { top: 12px; }
.yzero { bottom: 4px; }
.trend svg { display: block; width: 100%; height: 160px; margin-top: var(--s-4); cursor: crosshair; }
.trend-axis { display: flex; justify-content: space-between; margin-top: var(--s-2);
              color: var(--ink-faint); }

/* ── the hover read-out ── shared by the stacked area chart and the choropleth. Both used to
   draw a shape and leave the reader to carry the answer back to a legend; this states it
   instead. pointer-events: none throughout — the card must never become the hit target that
   dismisses the hover that summoned it. */
.chart-tip {
  position: absolute; top: 0; left: 0; z-index: 6; pointer-events: none;
  min-width: 208px; max-width: 300px; padding: var(--s-3);
  opacity: 0; transition: opacity var(--dur-state) ease;
}
.chart-tip.is-on { opacity: 1; }
.tip-head { font-weight: var(--weight-data); margin-bottom: var(--s-2); }
/* the swatch column stays even on rows that have no swatch, so every value lands on one edge */
.tip-row { display: grid; grid-template-columns: 14px 1fr auto; gap: var(--s-2);
           align-items: center; padding: var(--s-1) 0; color: var(--ink-muted); }
.tip-row.is-hi { color: var(--ink); font-weight: var(--weight-data); }
.tip-row .sw { width: 14px; height: 10px; }
.tip-row .vv { font-variant-numeric: tabular-nums; }
.tip-row .of { color: var(--ink-muted); font-weight: 400; }
.tip-rule { height: 1px; background: var(--rule); margin: var(--s-2) 0; }
.tip-note { color: var(--ink-muted); font-style: italic; margin-top: var(--s-2); line-height: 1.5; }

.xh { stroke: var(--ink); stroke-dasharray: 3 4; opacity: 0; transition: opacity var(--dur-state) ease; }
.xh.is-on { opacity: .6; }
/* a hovered legend chip picks its band out of the stack without a week being implied */
.axitems .lg-chip { cursor: default; }
.axitems .lg-chip.is-hi { color: var(--ink); }

.terr { display: grid; grid-template-columns: 1fr 340px; gap: var(--s-6); }
.terr-map { position: relative; padding: var(--s-3); }
.terr-map svg { display: block; width: 100%; height: auto; }
.ghost { stroke: var(--ink); stroke-width: 1.6; pointer-events: none;
         opacity: 0; transition: opacity var(--dur-state) ease; }
.ghost.is-on { opacity: 1; }
.terr-row { display: grid; grid-template-columns: 24px 1fr 40px 92px; gap: var(--s-3);
            align-items: center; padding: var(--s-2) 0; border-bottom: 1px solid var(--rule-soft); }
.terr-row.is-hi { background: var(--surface-marked); }
/* the rank ordinal — a section numeral beside the row's value, faint (see .beat .n above). */
.terr-row .rk { color: var(--ink-faint); font-weight: 600; }
.terr-row .vv { text-align: right; font-weight: var(--weight-data); }
.terr-foot { color: var(--ink-muted); font-style: italic; margin-top: var(--s-3); }
/* every territory Trends reported, not a top-5 — the tail is where a surprising market shows
   up, so the list scrolls rather than truncating at an arbitrary cut */
.terr-scroll { max-height: 420px; overflow-y: auto; }

/* ── reception ── one shared 0–5 bar track across every source, so the SPREAD between them is
   what the eye picks up. Ordered by base size, because that is the market story. */
.rxrow { display: grid; grid-template-columns: 150px 1fr 200px 86px; gap: var(--s-4);
         align-items: baseline; padding: var(--s-3) 0; border-bottom: 1px solid var(--rule-soft); }
.rxrow:last-child { border-bottom: 0; }
.rxsrc, .rxbase { font-weight: var(--weight-data); }
.rxmarket { color: var(--ink-muted); font-weight: 400; margin-top: var(--s-1); }
.rxunit, .rxof { color: var(--ink-muted); font-weight: 400; }
.rxextra { color: var(--ink-muted); font-weight: 400; margin-top: var(--s-1); line-height: 1.6; }
.rxbar { align-self: center; }
.rxval { text-align: right; font-weight: var(--weight-data); font-variant-numeric: tabular-nums; }
.rxstar { color: var(--ink-muted); font-weight: 400; margin-left: var(--s-1); }

/* ── youtube + booktok panels ── the same row grammar (shared .avwhen / .ytmain / .ytnum), and
   the WHEN column matches .avrow so every panel in the drawer shares a left edge. align-self
   keeps the still OUT of the row's baseline group — a baseline-aligned image hangs the whole
   row off its bottom edge. The BookTok still is PORTRAIT: TikTok is 9:16, and a 16:9 crop
   cuts the face out of most book videos. */
.ytrow { display: grid; grid-template-columns: 96px 120px 1fr 90px 90px 110px; gap: var(--s-4); }
.btrow { display: grid; grid-template-columns: 96px 68px 1fr 90px 90px; gap: var(--s-4); }
.ytrow, .btrow { align-items: baseline; padding: var(--s-2) 0;
                 border-bottom: 1px solid var(--rule-soft); }
.ytrow:last-child, .btrow:last-child { border-bottom: 0; }
.ytthumb { width: 120px; height: 68px; align-self: start; }
.btthumb { width: 68px; height: 121px; align-self: start; }
.ytmain { min-width: 0; }
.ytlink { color: var(--ink); text-decoration: none; font-weight: var(--weight-data);
          border-bottom: 1px solid var(--rule); }
.ytlink:hover { color: var(--ink); border-bottom-color: var(--ink); }
.ytchan { color: var(--ink-muted); margin-top: var(--s-1); }
.ytnum { text-align: right; font-weight: var(--weight-data); font-variant-numeric: tabular-nums; }
.ytunit { color: var(--ink-muted); font-weight: 400; margin-left: var(--s-1); }
.lg-chip .sw { width: 14px; height: 10px; }

/* the video PAIR. auto-fit does the real work: one panel present -> its track fills the pane;
   two -> they split it; too narrow for two readable columns -> they stack, with no breakpoint
   to keep in sync with the drawer's width. min-width: 0 lets a track shrink to its share. */
.vidpair { display: grid; grid-template-columns: repeat(auto-fit, minmax(460px, 1fr));
           gap: var(--s-3) var(--s-5); align-items: start; }
.vidpair > div { min-width: 0; }
/* The numbers ride a wrapper so the paired layout can move them as ONE unit; display: contents
   makes that wrapper invisible to the grid at full width, so the single-panel view is
   byte-identical to what it was before the wrapper existed. */
.rownums { display: contents; }
/* paired, the columns are half as wide, so the numbers drop to their own line under the title */
.vidpair.is-pair .rownums { display: flex; gap: var(--s-4); grid-column: 3 / -1; grid-row: 2;
                            margin-top: var(--s-1); }
.vidpair.is-pair .ytrow { grid-template-columns: 84px 104px minmax(0, 1fr); gap: var(--s-1) var(--s-3); }
.vidpair.is-pair .btrow { grid-template-columns: 84px 58px minmax(0, 1fr); gap: var(--s-1) var(--s-3); }
.vidpair.is-pair .ytthumb { width: 104px; height: 59px; }
.vidpair.is-pair .btthumb { width: 58px; height: 103px; }
/* the sorter wraps under the title rather than force the panel wider than its track */
.vidpair.is-pair .blk-head { flex-wrap: wrap; row-gap: var(--s-1); }
.vidpair.is-pair .blk-head .spacer { flex: 1 1 auto; }

.striped {
  min-height: 200px; display: grid; place-items: center;
  color: var(--ink-muted);
  background: repeating-linear-gradient(45deg, var(--rule-soft) 0 10px, transparent 10px 20px);
}

/* creative fit */
.fit-basis { display: flex; align-items: center; gap: var(--s-4); flex-wrap: wrap; }
.fg-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-3); margin-bottom: var(--s-3); }
/* group heading (slice 5) — same pane/block-heading shape as .blk-title/.src-head */
.fg-head .nm {
  font-size: var(--type-section); font-family: var(--face-label);
  font-weight: var(--weight-data); letter-spacing: var(--track-section); white-space: nowrap;
}
.fg-head .ds { color: var(--ink-muted); white-space: nowrap; }
.fg-head .rule, .blk-head .rule { flex: 1; height: 1px; background: var(--rule); }
.crit { display: grid; grid-template-columns: 210px 96px 1fr; gap: var(--s-4);
        padding: var(--s-3); border-radius: var(--r-control); align-items: start; }
.crit.zebra { background: var(--ground); }
.crit .nm, .crit .sc { font-weight: var(--weight-data); }
/* the tag beside a score is the hero's Logline label shape: .ks-label in the label face */
.crit .sc .tag { color: var(--ink-muted); margin-left: var(--s-2);
                 font-family: var(--face-label); font-weight: var(--weight-label); }
/* the Power read's answer to the superpower question: plain paragraphs under the Power head */
.power-read { display: flex; flex-direction: column; gap: var(--s-3); }
.power-read p { margin: 0; line-height: 1.6; }
.power-read .k { color: var(--ink-muted); font-family: var(--face-label);
                 font-weight: var(--weight-label); letter-spacing: var(--track-micro);
                 text-transform: uppercase; }

/* sources */
.sources { display: grid; grid-template-columns: 380px 1fr; gap: var(--s-6); }
.src-head {
  font-size: var(--type-section); font-family: var(--face-label); font-weight: var(--weight-data);
  letter-spacing: var(--track-section); margin-bottom: var(--s-3);
}
.idrow { display: grid; grid-template-columns: 150px 1fr; gap: var(--s-3); padding: var(--s-2) 0;
         align-items: baseline; font-family: var(--face-mono); }
.idrow .k { color: var(--ink-muted); }
.idrow a.k, .idrow a.v { color: var(--ink); text-decoration: none;
                         border-bottom: 1px solid var(--rule); word-break: break-all; }
.idrow a.v:hover { color: var(--ink); border-bottom-color: var(--ink); }
.vrow { margin-bottom: var(--s-4); }
.vrow .h { display: flex; align-items: baseline; gap: var(--s-2); }
/* row title (slice 5) — the source's own name, at its lighter .12em tracking already read as
   a rank below the pane/block headings above, which is now expressed as plain data weight */
.vrow .nm { font-weight: var(--weight-data); }
.vrow .tick, .vrow .pc { color: var(--pos); }
.vrow.bad .tick, .vrow.bad .pc, .vrow.bad .nm { color: var(--neg); }
/* The third state: KEPT because the verifier was not confident enough to drop it, which is not
   the same as vouching for it. Neutral, deliberately — green would re-tell the lie the three
   states exist to stop, and red would claim a rejection that did not happen. */
.vrow.kept .tick, .vrow.kept .pc, .vrow.kept .nm { color: var(--ink-faint); }
/* The fourth: REPOINTED — the main record was rejected and a listed alternate adopted in its
   place. Neutral for the same reason, one step brighter than `kept` because an id DID land. */
.vrow.redirect .tick, .vrow.redirect .pc, .vrow.redirect .nm { color: var(--ink-muted); }
.vrow .ex { font-style: italic; color: var(--ink-muted); margin-top: var(--s-1); }

/* ── scout ── The Weekly 20 (docs/design_handoff_scout_tab/README.md). Composes .ks-surface,
   .ks-section, .ks-btn, .ks-label, .ks-chip, .ks-reveal and .ks-readout; sct- carries only
   Scout's own layout, per CLAUDE.md's "add a class, never join a shared selector" law. Fixed
   pixel sizes below (dots, covers, the rail column, sparkline boxes, the menu width) are
   DIMENSIONS of a thing, not spacing – CLAUDE.md's hard constraint 4 exempts them the same way
   `.thumb`/`.ytthumb` sizes above do. */

/* the README's own default for a micro label on this surface (§1 shorthand: "Default ink
   --ink-muted"), stated once and added beside .ks-label wherever a band/section head or filter
   label carries no other colour of its own (a data-coloured chip code or badge already gets its
   colour from .ks-chip/.ks-chip--demand and needs no second rule) */
.sct-lbl { color: var(--ink-muted); }

.sct-tabs { display: flex; gap: var(--s-1); border-bottom: 1px solid var(--rule); }
.sct-tab {
  background: none; border: none; border-bottom: 2px solid transparent;
  display: flex; align-items: center; gap: var(--s-2); padding: var(--s-3) var(--s-4);
  transition: color var(--dur-state), border-color var(--dur-state);
}
.sct-tab:not(.is-on) { color: var(--ink-muted); }
.sct-tab:hover { color: var(--ink); }
/* the underline reads currentColor rather than restating --ink: active is the only state whose
   text colour is the inherited default, so the border just follows whatever that is */
.sct-tab.is-on { border-bottom-color: currentColor; }
.sct-dots { display: inline-flex; gap: var(--s-1); }
.sct-dot { width: 4px; height: 4px; border-radius: var(--r-pill); background: var(--rule); }
.sct-dot.is-on { background: currentColor; }

/* movers strip – attached under the tabs, no top border (the tabs' hairline is the seam).
   Only the bottom corners round; the top two stay 0 (the property's own initial value, so
   nothing here restates a literal) rather than spell a shorthand starting in a bare "0". */
.sct-movers {
  border-top: 0; border-bottom-left-radius: var(--r-surface); border-bottom-right-radius: var(--r-surface);
  padding: var(--s-3) var(--pad-pane); display: flex; align-items: center; gap: var(--s-5);
  flex-wrap: wrap;
}
.sct-mover { background: none; border: none; transition: color var(--dur-state); }
.sct-mover:not(.is-on) { color: var(--ink-muted); }
.sct-mover:hover { color: var(--ink); }
/* the counts are data and keep their accent regardless of the toggle's own state (README §3) */
.sct-count { font-family: var(--face-mono); font-weight: var(--weight-data); }
.sct-count--pos { color: var(--pos); }
.sct-count--neg { color: var(--neg); }
/* the title is ink regardless of the toggle's own state, same reasoning as the counts above */
.sct-climber-title { font-weight: var(--weight-data); color: var(--ink); }
.sct-vdiv { width: 1px; height: 12px; background: var(--rule); }
.sct-vdiv--tall { height: 16px; }
.sct-refreshed { color: var(--ink-faint); margin-left: auto; }

/* dropped-off panel – collapsed by default (.hidden, the app-wide toggle convention) */
.sct-dropped {
  margin-top: -1px; border-top: 0;
  border-bottom-left-radius: var(--r-surface); border-bottom-right-radius: var(--r-surface);
  overflow: hidden;
}
.sct-dropped-head { background: var(--surface-raised); padding: var(--s-2) var(--pad-pane); }
.sct-drop {
  display: grid; grid-template-columns: minmax(240px, 1fr) 2fr auto; gap: var(--s-5);
  padding: var(--s-2) var(--pad-pane); border-bottom: 1px solid var(--rule-soft);
}
.sct-drop:last-child { border-bottom: 0; }
.sct-drop-t { font-weight: var(--weight-data); }
.sct-drop-why { color: var(--ink-muted); }
.sct-drop-move { color: var(--ink-muted); font-family: var(--face-mono); }

/* filter bar */
.sct-filters {
  padding: var(--s-4) 0 var(--s-3); margin-top: var(--s-4); border-bottom: 1px solid var(--rule-soft);
  display: flex; align-items: center; gap: var(--s-4); flex-wrap: wrap;
}
/* the square constraint chip (README §5) – .ks-chip's own .is-on already fills it ink (known
   conflict #1: the primitive wins over the outline the handoff drew) */
.sct-genre {
  background: none; border-radius: var(--r-control); padding: var(--s-1) var(--s-3);
  transition: color var(--dur-state), background-color var(--dur-state), border-color var(--dur-state);
}
.sct-seg { display: inline-flex; border: 1px solid var(--rule); border-radius: var(--r-control); overflow: hidden; }
.sct-seg-k {
  background: none; border: none; padding: var(--s-1) var(--s-3); color: var(--ink-muted);
  transition: color var(--dur-state), background-color var(--dur-state);
}
.sct-seg-k.is-on { color: var(--ink); background: var(--surface-raised); }
.sct-mono { font-family: var(--face-mono); }
.sct-dismissed { margin-left: auto; }

/* section head */
.sct-head { padding: var(--s-5) 0 var(--s-2); display: flex; align-items: baseline; gap: var(--s-4); flex-wrap: wrap; }
.sct-h {
  margin: 0; font: var(--weight-data) var(--type-section)/1.2 var(--face-label);
  text-transform: uppercase; letter-spacing: var(--track-section);
}
.sct-state { font-family: var(--face-mono); color: var(--ink-muted); }
.sct-state.is-on { color: var(--ink); }
.sct-pool { color: var(--ink-muted); margin-left: auto; }

/* the list */
.sct-list { overflow: hidden; }
.sct-row {
  display: grid; grid-template-columns: 64px 76px minmax(0, 1fr) 168px; gap: var(--s-5);
  padding: var(--pad-pane); border-bottom: 1px solid var(--rule); cursor: pointer;
  transition: background-color var(--dur-state);
}
.sct-row:hover { background: var(--surface-raised); }
/* the row whose dossier is open (README §6a) – after the hover rule so open beats hover */
.sct-row.is-open { background: var(--surface-marked); }
.sct-row--compact {
  grid-template-columns: 64px 56px minmax(0, 1fr) 168px; padding: var(--pad-card) var(--pad-pane);
  border-bottom-color: var(--rule-soft);
}
.sct-rank { text-align: right; }
.sct-row--compact .sct-rank .ks-readout { color: var(--ink-muted); }
.sct-mv { font-family: var(--face-mono); margin-top: var(--s-1); }
.sct-mv--pos { color: var(--pos); }
.sct-mv--neg { color: var(--neg); }
.sct-mv--ink { color: var(--ink); }
.sct-mv--faint { color: var(--ink-faint); }
.sct-cover {
  width: 76px; height: 114px; border: 1px solid var(--rule); border-radius: var(--r-media);
  background: repeating-linear-gradient(45deg, var(--surface-raised) 0 8px, var(--surface) 8px 16px);
  object-fit: cover;
}
.sct-row--compact .sct-cover { width: 56px; height: 84px; }
.sct-body { min-width: 0; display: flex; flex-direction: column; gap: var(--s-3); }
.sct-row--compact .sct-body { gap: var(--s-2); }
.sct-title { font-weight: var(--weight-data); }
.sct-series { font-weight: var(--weight-text); color: var(--ink-muted); }
.sct-byline { color: var(--ink-muted); margin-top: var(--s-1); }
/* the one body-step element on a lead row, on purpose (README §6a) – the compact row inherits
   the page's own data size and restates nothing */
.sct-row--lead .sct-why { font-size: var(--type-body); }
.sct-why { margin: 0; max-width: 74ch; text-wrap: pretty; }
.sct-row--compact .sct-why { max-width: 86ch; }
.sct-badges { display: flex; gap: var(--s-2); flex-wrap: wrap; }
.sct-badges .ks-chip--pill { padding: var(--s-1) var(--s-3); }
.sct-mkt { display: inline-flex; align-items: center; gap: var(--s-2); }
.sct-rail { position: relative; display: flex; flex-direction: column; align-items: flex-end; justify-content: space-between; }
.sct-more { font-size: var(--type-body); padding: var(--s-1) var(--s-2); }
.sct-more:hover { color: var(--ink); }
/* the reveal trigger – hidden until row hover/focus, and held open while its own menu is open
   (law 08, the state on the trigger itself so .sct-row.is-open stays the drawer-open tint);
   :focus-visible itself is covered by the .ks-reveal primitive already */
.sct-row:hover .sct-more, .sct-row:focus-within .sct-more, .sct-more.is-open { opacity: 1; }
.sct-spark polyline { fill: none; stroke: var(--demand); }
.sct-spark circle { fill: var(--demand); }
.sct-score { display: flex; flex-direction: column; align-items: flex-end; margin-top: var(--s-2); }
.sct-row--compact .sct-score { margin-top: var(--s-1); }
.sct-wk { color: var(--ink-faint); }
.sct-score-n {
  font-family: var(--face-mono); font-weight: var(--weight-data); color: var(--demand);
  font-variant-numeric: tabular-nums;
}
/* an unresolved composite (score: null – no scored pass has reached this title yet) reads
   neutral-faint, never a fabricated gold zero (law 09). */
.sct-score-n--faint { color: var(--ink-faint); }

/* overflow menu – anchored inside .sct-rail, which is the position:relative above */
.sct-menu { position: absolute; right: 0; top: var(--s-5); z-index: 20; width: 196px; background: var(--surface-raised); padding: var(--s-1); }
.sct-menu-k {
  display: block; width: 100%; text-align: left; background: none; border: none;
  padding: var(--pad-cell); border-radius: var(--r-control); color: var(--ink-muted);
  transition: color var(--dur-state), background-color var(--dur-state);
}
.sct-menu-k:hover { background: var(--surface); color: var(--ink); }
.sct-menu-k--danger:hover { color: var(--neg); }

/* list footer */
.sct-foot { padding: var(--s-3) var(--pad-pane); color: var(--ink-muted); display: flex; align-items: center; }
.sct-foot .ks-label { color: var(--ink-faint); margin-left: auto; }

/* emerging shelf – a tier below the list (README §7) */
.sct-emerging { border-color: var(--rule-soft); margin-top: var(--s-5); overflow: hidden; }
.sct-em-head {
  background: var(--surface); padding: var(--s-3) var(--pad-pane); border-bottom-color: var(--rule-soft);
  display: flex; align-items: baseline; gap: var(--s-4); flex-wrap: wrap;
}
.sct-em-head span + span { color: var(--ink-muted); }
/* ponytail: README draws the grid's hairlines with `gap: 1px; background: var(--rule-soft)` –
   1px is not a space rung and the gate has none to round to, so the seam is drawn with a
   per-card hairline border instead. That leaves the last column's right edge and the bottom
   row's bottom edge sitting 1px inside the surface's own border, doubling the line. The -1px
   margin below is the gate's own documented exemption (a hairline overlap, not a spacing value):
   it grows the grid 1px past the surface's padding box on those two sides so `overflow: hidden`
   (added above) clips the redundant pixel instead of rendering it. */
.sct-em-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); margin: 0 -1px -1px 0;
}
.sct-em {
  background: var(--ground); padding: var(--pad-card); display: flex; gap: var(--s-3);
  border-right: 1px solid var(--rule-soft); border-bottom: 1px solid var(--rule-soft);
  transition: background-color var(--dur-state);
}
.sct-em:hover { background: var(--surface); }
.sct-em-cover {
  flex: none; width: 40px; height: 60px; border: 1px solid var(--rule); border-radius: var(--r-media);
  background: repeating-linear-gradient(45deg, var(--surface-raised) 0 8px, var(--ground) 8px 16px);
}
.sct-em-body { min-width: 0; }
.sct-em-title { font-weight: var(--weight-data); }
.sct-em-author { color: var(--ink-muted); margin-top: var(--s-1); }
.sct-em-why { margin: 0; color: var(--ink-muted); margin-top: var(--s-2); }
.sct-em-source { color: var(--ink-faint); margin-top: var(--s-2); }

/* ── scout: dossier drawer (README §8) ── built once on first use (picker.js pattern), appended
   to document.body – z 40, BELOW the reused .modal-back (z 50, redesign.css line ~925) so the
   remove-from-list modal stacks above an open drawer. */
.sct-dw-back {
  position: fixed; inset: 0; z-index: 40; display: flex; justify-content: flex-end;
  background: var(--scrim);
}
.sct-dw-scrim { flex: 1; }
/* .ks-surface's own border+radius suit a floating card, not an edge-to-edge panel – reset both
   (border-radius: initial, not a bare 0, so stylelint's border-radius disallowed-list still
   passes) and draw only the left hairline that separates the panel from the scrim. */
.sct-dw {
  width: 720px; max-width: 92vw; overflow-y: auto;
  border: 0; border-left: 1px solid var(--rule); border-radius: initial;
}

.sct-dw-head {
  display: grid; grid-template-columns: 168px 1fr; gap: var(--s-5); padding: var(--pad-pane);
  background: var(--ground); border-bottom: 1px solid var(--rule);
}
.sct-dw-art {
  width: 168px; height: 252px; border: 1px solid var(--rule); border-radius: var(--r-media);
  background: repeating-linear-gradient(45deg, var(--surface-raised) 0 8px, var(--ground) 8px 16px);
  display: grid; place-items: center; object-fit: cover;
}
.sct-dw-art .ks-label { color: var(--ink-muted); }
.sct-dw-headcol { display: flex; flex-direction: column; gap: var(--s-4); min-width: 0; }
.sct-dw-headtop { display: flex; align-items: baseline; gap: var(--s-3); }
.sct-dw-mv { font-family: var(--face-mono); }
.sct-dw-close {
  margin-left: auto; background: none; border: none; color: var(--ink-muted);
  font-size: var(--type-body); padding: var(--s-1) var(--s-2); transition: color var(--dur-state);
}
.sct-dw-close:hover { color: var(--ink); }
.sct-dw-title { font-weight: var(--weight-data); }
.sct-dw-byline { color: var(--ink-muted); margin-top: var(--s-1); }
.sct-dw-tags { display: flex; gap: var(--s-2); flex-wrap: wrap; }
.sct-dw-tag { padding: var(--s-1) var(--s-3); }
.sct-dw-why {
  font-size: var(--type-body); margin: 0; text-wrap: pretty;
  border-top: 1px solid var(--rule); padding-top: var(--s-4);
}
.sct-dw-stats { display: flex; gap: var(--s-5); flex-wrap: wrap; }
.sct-dw-stat-k { margin-right: var(--s-2); }
.sct-dw-stat-k--demand { color: var(--demand); }
.sct-dw-stat-v { font-family: var(--face-mono); }
.sct-dw-stat-v--faint { color: var(--ink-faint); }
/* the label and value are adjacent siblings inside one .sct-dw-stat span – Composite is the
   only stat whose value is weighted (hard rule: "the Composite label ... are --demand") */
.sct-dw-stat-k--demand + .sct-dw-stat-v { font-weight: var(--weight-data); }

.sct-dw-body { padding: var(--pad-pane); display: flex; flex-direction: column; gap: var(--s-5); }
/* a section is its heading (.sct-h, reused as-is) plus its content, gapped instead of margined –
   .sct-h stays margin:0 everywhere else it appears (the list's h2, the modal's h2), so the space
   before a section's content lives on the flex container around it, never on .sct-h itself */
.sct-dw-sec { display: flex; flex-direction: column; gap: var(--s-3); }
.sct-dw-sec-head { display: flex; align-items: baseline; gap: var(--s-4); flex-wrap: wrap; }
.sct-dw-sec-note { color: var(--ink-muted); }

/* per-source velocity grid – six cells, two columns, three rows. The Emerging shelf (PR 1) draws
   its hairlines with a `gap:1px; background` sandwich plus a -1px overflow trick because its
   card list nests inside a separate bordered/clipped surface; here the grid IS the bordered
   surface with no outer wrapper to clip against, so a per-cell border with the last row/column
   trimmed reaches the same one-hairline-per-seam result without the extra nesting. */
.sct-dw-src-grid {
  display: grid; grid-template-columns: 1fr 1fr; border: 1px solid var(--rule);
  border-radius: var(--r-surface); overflow: hidden;
}
.sct-dw-src {
  background: var(--ground); padding: var(--pad-card);
  border-right: 1px solid var(--rule-soft); border-bottom: 1px solid var(--rule-soft);
}
.sct-dw-src:nth-child(2n) { border-right: 0; }
.sct-dw-src:nth-last-child(-n+2) { border-bottom: 0; }
.sct-dw-src-top { display: flex; align-items: baseline; gap: var(--s-2); }
.sct-dw-src-name { color: var(--ink-muted); }
.sct-dw-src-delta { margin-left: auto; }
.sct-dw-src-market { color: var(--ink-faint); margin-top: var(--s-1); }
.sct-dw-spark { display: block; width: 100%; margin-top: var(--s-3); }
.sct-dw-spark polyline { fill: none; stroke: var(--demand); }
/* Amazon rank is the one inverted axis (a falling rank is a rising signal) – drawn in ink so it
   is never mistaken for the composite reading (README §8, law 10) */
.sct-dw-spark polyline.sct-spark--ink { stroke: var(--ink); }
.sct-dw-src-note { color: var(--ink-muted); margin-top: var(--s-2); }

.sct-dw-mkt-row {
  display: grid; grid-template-columns: 120px 1fr 56px; gap: var(--s-4); align-items: center;
  padding: var(--s-2) 0; border-bottom: 1px solid var(--rule-soft);
}
.sct-dw-mkt-row:last-child { border-bottom: 0; }
.sct-dw-mkt-name { display: flex; flex-direction: column; gap: var(--s-1); }
.sct-dw-mkt-n { font-weight: var(--weight-data); }
.sct-dw-mkt-tier { color: var(--ink-faint); }
.sct-dw-mkt-val { font-family: var(--face-mono); font-weight: var(--weight-data); text-align: right; }

.sct-dw-split { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-4); }
.sct-dw-split--start { align-items: start; }
/* the adaptation/rights pair's headings sit bare beside their content (no .sct-dw-sec wrapper);
   the same heading-then-content gap applies here, scoped to this modifier only so the creator/
   comps pair below (plain .sct-dw-split, its own sunken-card padding/gap) is untouched */
.sct-dw-split--start > div { display: flex; flex-direction: column; gap: var(--s-3); }
.sct-dw-card { padding: var(--pad-card); }
.sct-dw-card-head { color: var(--ink-muted); margin-bottom: var(--s-2); }
.sct-dw-creator-row {
  display: grid; grid-template-columns: 96px 1fr; gap: var(--s-3); align-items: baseline;
  padding: var(--s-1) 0;
}
.sct-dw-creator-k { color: var(--ink-faint); }
.sct-dw-comp-line { padding: var(--s-1) 0; }
.sct-dw-comp-caveat { font-style: italic; color: var(--ink-muted); margin-top: var(--s-2); }
/* creator/comps not in the feed (PR 3 stub) – law 09: unresolved is faint, never a colour */
.sct-dw-nyw { color: var(--ink-faint); }

.sct-dw-adapt-row { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
.sct-dw-adapt-checked { color: var(--ink-faint); }
.sct-dw-adapt-note { color: var(--ink-muted); margin-top: var(--s-2); }
/* adaptation check not wired (PR 3 stub) – neutral drawn faint, not a false positive/negative */
.sct-dw-adapt-neutral { color: var(--ink-faint); border-color: var(--rule-soft); }
.sct-dw-rights {
  min-height: 110px; display: grid; place-items: center; text-align: center;
  border: 1px solid var(--rule); border-radius: var(--r-surface); color: var(--ink-faint);
  padding: var(--s-3);
  background: repeating-linear-gradient(45deg, var(--surface-raised) 0 8px, transparent 8px 16px);
}

/* .sct-dw-actions overrides .ks-section--follow's own gutter: the body already pads the panel
   horizontally, so a second side gutter here would double it */
.sct-dw-actions { padding: var(--s-4) 0 0; display: flex; gap: var(--s-2); flex-wrap: wrap; }
.sct-dw-close-action { margin-left: auto; }

/* ── scout: remove-from-list modal (README §9) ── reuses .modal-back / .modal (z 50, already
   ABOVE the drawer's z 40), adding only .sct-dm's own contents */
.sct-dm-body { color: var(--ink-muted); margin-bottom: var(--s-2); }
.sct-dm-title { font-weight: var(--weight-data); color: var(--ink); }
.sct-dm-reasons { display: flex; flex-direction: column; gap: var(--s-1); }
.sct-dm-r {
  display: flex; align-items: center; gap: var(--s-3); padding: var(--pad-cell);
  border-radius: var(--r-control); background: none; border: none; width: 100%;
  text-align: left; color: var(--ink-muted); cursor: pointer;
  transition: color var(--dur-state), background-color var(--dur-state);
}
.sct-dm-r:hover, .sct-dm-r.is-on { background: var(--surface-raised); }
.sct-dm-r.is-on { color: var(--ink); }
.sct-dm-radio {
  width: 12px; height: 12px; border-radius: var(--r-pill); border: 1px solid var(--rule); flex: none;
}
.sct-dm-radio.is-on { border-color: var(--ink); background: var(--ink); }
/* .sct-dm-r-label needs no rule of its own – .sct-dm-r.is-on's color inherits onto its children */

/* ───────────────────────── responsive ───────────────────────── */

/* the Amazon pair needs roughly two 420px cards plus the gutters to stay side by side; below
   that it STACKS and the vertical hairline would be a stray line down one column */
@media (max-width: 1040px) {
  .az-cols { grid-template-columns: minmax(0, 1fr); }
  .az-cols > .ig-sec + .ig-sec { border-left: 0; }
}

@media (max-width: 900px) {
  .app { padding: 0 var(--s-4) var(--s-6); }
  .topbar { flex-direction: column; align-items: flex-start; gap: var(--s-4); padding: var(--s-5) 0 var(--s-4); }
  .tabs { flex-wrap: wrap; }
  .dw-hero { grid-template-columns: 1fr; gap: var(--s-4); padding: var(--pad-card); }
  .dw-cover { width: 132px; height: 198px; }
  .pane, .dw-tabs { padding-left: var(--s-4); padding-right: var(--s-4); }
  .ov-split, .beats, .terr, .sources { grid-template-columns: 1fr; gap: var(--s-5); }
  .crit { grid-template-columns: 1fr 96px; }
  .crit .ev { grid-column: 1 / -1; }
  /* the dropped row STACKS: its title track has a 240px floor, so three columns overflowed the
     card here — and the third is a CONTROL, which put Promote off the right edge */
  .drow { grid-template-columns: minmax(0, 1fr); gap: var(--s-2); }
  .dact { justify-content: flex-start; }
}

[data-rights-research] .avcite { white-space: normal; }

/* ── Memo view (/?memo=<id>) ── memo-mode hides the app chrome; the memo is the page.
   Two tiers: the call and the board are the memo (read in thirty seconds); the title blocks are
   the appendix. Prose sits at the body step because it is read in sentences; the board and the
   scores line stay at data. Rank comes from case, weight and ink, never size (law 1). The faint
   section numeral is the memo's one motif: an ordinal that locates a heading (law 5). */
body.memo-mode .topbar, body.memo-mode .panel:not(#memo-view) { display: none !important; }
body.memo-mode #memo-view { display: flex; }
.memo { max-width: 48rem; margin-inline: auto; width: 100%; display: flex; flex-direction: column; gap: var(--s-6);
        counter-reset: memo-section; font-size: var(--type-data); line-height: 1.6; color: var(--ink); }
/* one label ink for every small-caps line: masthead, stamp, meta, scores, read, table head */
.memo .ks-label { color: var(--ink-faint); }
.memo p { margin: 0; }

/* header: a memo's masthead – label, headline, stamp – under one heavy rule */
.memo-head { display: flex; flex-direction: column; gap: var(--s-2); padding-block: var(--s-5) var(--s-4);
             border-top: 2px solid var(--ink); border-bottom: 1px solid var(--rule); }
.memo-head h1 { font: var(--weight-data) var(--type-numeral)/1.1 var(--face-label); letter-spacing: var(--track-numeral); }

/* sections: the .blk-title shape, led by a faint two-digit ordinal */
.memo-section { display: flex; flex-direction: column; gap: var(--s-4); }
.memo h2 { margin: 0; display: flex; align-items: baseline; gap: var(--s-3);
           font: var(--weight-data) var(--type-section)/1.2 var(--face-label); letter-spacing: var(--track-section); }
.memo h2::before { counter-increment: memo-section; content: counter(memo-section, decimal-leading-zero);
                   color: var(--ink-faint); font-weight: var(--weight-label); font-variant-numeric: tabular-nums; }

/* the call: the memo's thesis, set as a lead paragraph against a hairline in ink */
.memo-call { padding-inline-start: var(--s-4); border-inline-start: 2px solid var(--ink);
             display: flex; flex-direction: column; gap: var(--s-3); }
.memo-checklist, .memo-unverified { margin: 0; padding-inline-start: var(--s-5); display: flex; flex-direction: column; gap: var(--s-2); }
.memo-checklist li::marker, .memo-unverified li::marker { color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.memo-unverified .ks-label { margin-inline-start: var(--s-2); }

/* the board: one ranked slate. group rows carry the section; rank restarts under each. */
.memo-table { overflow-x: auto; }
.memo-table table { width: 100%; border-collapse: collapse; }
.memo-table th, .memo-table td { text-align: left; padding: var(--pad-cell); border-bottom: 1px solid var(--rule-soft);
                                  vertical-align: middle; }
.memo-table thead th { color: var(--ink-faint); font-family: var(--face-label); font-weight: var(--weight-label);
                       font-size: var(--type-micro); letter-spacing: var(--track-micro); text-transform: uppercase;
                       border-bottom: 1px solid var(--rule); }
.memo-table .memo-group th { padding-top: var(--s-4); font-weight: var(--weight-data); border-bottom: 1px solid var(--rule); }
.memo-table td { white-space: nowrap; }
.memo-table .memo-cell-title { font-weight: var(--weight-data); }
.memo-table .memo-rank { width: 1%; color: var(--ink-faint); font-variant-numeric: tabular-nums; text-align: right; }
.memo-table .memo-num { width: 1%; text-align: right; }

/* rights status: one meaning per accent – open is pos, closed is neg, held is second ink */
.memo-rights-open { color: var(--pos); }
.memo-rights-held { color: var(--ink); }
.memo-rights-closed { color: var(--neg); }

/* title blocks: art anchored left in a column that is reserved even when no cover exists, so the
   text stacks align down a section; title / status / meta / logline / scores, then the prose */
.memo-title { display: grid; grid-template-columns: 72px minmax(0, 1fr); gap: var(--s-4); padding-block: var(--s-5);
              border-top: 1px solid var(--rule); }
.memo-title:first-of-type { border-top: 0; padding-top: 0; }
.memo-title-body { display: flex; flex-direction: column; gap: var(--s-3); min-width: 0; }
.memo-art { width: 72px; height: 108px; object-fit: cover; border-radius: var(--r-media); background: var(--surface-raised); }
.memo-title h3 { margin: 0; display: flex; align-items: baseline; gap: var(--s-2) var(--s-3); flex-wrap: wrap;
                 font-size: var(--type-section); font-weight: var(--weight-data); line-height: 1.3; }
.memo-status { font-family: var(--face-label); font-weight: var(--weight-label); }
.memo-scores { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
/* the scores line is a label row, so the rings reset the micro label type their numerals
   would otherwise inherit – the numeral reads at cell size, like the slate's */
.memo-scores .ring { margin-inline-end: var(--s-3); font-size: var(--type-data); letter-spacing: var(--track-data); }
.memo-why strong, .memo-catch strong, .memo-note strong, .memo-rights strong, .memo-call strong { font-weight: var(--weight-data); }
.memo-uncited { color: var(--neg); font-family: var(--face-label); font-size: var(--type-micro);
                letter-spacing: var(--track-micro); text-transform: uppercase; white-space: nowrap; }
.memo-unavailable { color: var(--neg); }
.memo-events { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--s-1); }
.memo-ev-date { display: inline-block; min-width: 96px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }

@media (max-width: 900px) {
  .memo-title { grid-template-columns: 56px minmax(0, 1fr); gap: var(--s-3); }
  .memo-art { width: 56px; height: 84px; }
}

/* Regenerate sweep: an indeterminate bar pinned to the top of the page. The memo call has no
   intermediate signal, so this shows life, not progress. Keyframe duration is a tool-owned
   motion (DO NOT TOUCH item 5's exemption), not a state transition. */
.memo-progress { position: fixed; inset-inline: 0; top: 0; height: 2px; overflow: hidden;
  background: var(--rule); z-index: 1; }
.memo-progress::after { content: ""; position: absolute; inset-block: 0; width: 30%;
  background: var(--ink); animation: memo-sweep 1.4s ease-in-out infinite; }
@keyframes memo-sweep { from { left: -30%; } to { left: 100%; } }
@media (prefers-reduced-motion: reduce) { .memo-progress::after { animation-duration: 3s; } }

/* print: the memo is the whole page. Fills that carry a datum keep their ink; blocks and rows
   never split across a page; the memo tools and citation underlines drop. */
@media print {
  body.memo-mode { background: var(--ground); }
  body.memo-mode .app { padding: 0; }
  body.memo-mode .memo-tools, .memo-progress { display: none; }
  .memo { max-width: none; }
  .memo-head { break-after: avoid; }
  .memo h2 { break-after: avoid; }
  .memo-title, .memo-table tr, .memo-checklist li { break-inside: avoid; }
  .memo-table thead { display: table-header-group; }
  .ring, .memo-rights-open, .memo-rights-held, .memo-rights-closed, .memo-uncited { print-color-adjust: exact; }
  .memo a.cite-link { text-decoration: none; }
}

/* Kismet tab — the Power tab's .crit rows plus a fourth column for the human override */
/* a narrower name column than Power's, a wider value column: the tag beside the score runs to
   MEASURED / DERIVED */
.kis-form .crit { grid-template-columns: 190px 120px 1fr 5.5rem; }
/* after the base rule on purpose: same specificity, so source order decides */
@media (max-width: 900px) { .kis-form .crit { grid-template-columns: 1fr 120px; } }
.kis-form .crit .ev .ecard-note { margin: var(--s-2) 0 0; }
.kis-form .crit .ev > div + div { margin-top: var(--s-2); }
.kis-form .crit textarea { width: 100%; }
/* a heading that carries a score ring or a status chip centers on it; ring-less headings keep the baseline */
.fg-head:has(.ring, .ks-chip) { align-items: center; }
/* still type=number, so only a number can be typed; the native spinner arrows go */
.kis-in { appearance: textfield; }
.kis-in::-webkit-inner-spin-button, .kis-in::-webkit-outer-spin-button { appearance: none; }
