/* The record (paper) environment. Legacy variable names below are aliases
   onto /tokens.css semantics so un-rebuilt page blocks migrate coherently:
   old modeled-teal -> violet, old input-gold -> amber ink, old observed-blue
   -> ink, dark surfaces -> paper. Remove aliases as each page family is
   recomposed (Phases 4-9). */
:root {
  color-scheme: light;
  --ink-950: var(--paper);
  --ink-900: var(--paper-deep);
  --paper-50: var(--ink);
  --paper-100: var(--ink);
  --muted: var(--ink-soft);
  --line: var(--rule);
  --line-strong: #b3a685;
  --teal: var(--ev-modeled);
  --gold: var(--ev-input);
  --coral: var(--ink);
  --blue: var(--ev-observed);
  --violet: var(--ev-modeled);
  --display: var(--serif);
  /* One page-title voice for every route. Six bespoke header rules had grown
     apart — 34px on /news, 36 on /bills and the record pages, 42 on /ask, 48 on
     the dashboards, 84 on /us/accuracy and /us/tracker — so each page announced
     itself at a different scale and the site stopped reading as one product.
     Per-page mobile overrides fought the clamps; this single clamp governs. */
  --page-title: clamp(34px, 4.6vw, 62px);
  /* Serif section headings had seven ad-hoc sizes (20/21/22/23/24/26/30/40/52)
     across fourteen rules — one role, 2.6x spread, no tiering, so a reader
     could not tell which headings were peers. Two deliberate tiers now:
     a statement for real editorial moments, and the standard section heading
     everything else uses. Context variation is fine; seven unrelated sizes is
     not a system. Mono eyebrow labels are a separate, already-consistent
     treatment (11-13px) and are untouched. */
  --title-statement: clamp(26px, 3.2vw, 40px);
  --title-section: 23px;
  --reading: 720px;
  --workspace: 1280px;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html { scroll-behavior: smooth; background: var(--paper); }
body { margin: 0; min-width: 320px; background: var(--paper); color: var(--ink); font: 17px/1.6 var(--sans); }
a { color: inherit; }
button, input, select, textarea { font: inherit; }
button, a { -webkit-tap-highlight-color: transparent; }
/* Baseline interactive-control floor, ported from pheme.css's own equivalent
   ("touchable across every legacy workspace while pages migrate to the
   generated shell") — the generated shell itself never had the matching
   rule. Individual controls with their own explicit sizing (buttons that set
   padding/line-height tall enough already, or deliberately non-target
   elements) are unaffected; this only raises anything shorter.
   Seam: [role="button"]/[role="tab"] added alongside the native elements —
   custom ARIA controls (world-dashboard's .rrow/.rank/.stat rows, etc.) are
   real interactive targets too and were falling through the native-only
   selector list at 29px. */
button, input[type="button"], input[type="submit"], select,
[role="button"], [role="tab"] { min-height: 44px; }
:focus-visible { outline: 2px solid var(--ev-modeled); outline-offset: 2px; }
.skip-link { position: fixed; z-index: 1000; top: 8px; left: 8px; min-height:44px; padding: 10px 16px; display:flex;align-items:center;background: var(--ink); color: var(--paper); font-weight: 700; transform: translateY(-160%); }
.skip-link:focus { transform: none; }

.signal-shell { width: min(calc(100% - 40px), var(--workspace)); margin-inline: auto; }
/* flex-wrap is dormant at normal zoom (the row already fits in ~20px of
   spare width) — it only activates once page zoom (or the CSS zoom
   property) scales the brand/nav/disclosure text past the header's
   viewport-anchored width, letting the row reflow to a second line instead
   of forcing a site-wide horizontal scrollbar. */
.signal-header { position: sticky; z-index: 100; top: 0; min-height: 60px; padding: 0 max(20px, calc((100vw - var(--workspace)) / 2)); display: flex; flex-wrap: wrap; align-items: center; gap: 24px; border-bottom: 2px solid var(--rule-strong); background: var(--paper); }
.signal-brand { display: inline-flex; align-items: baseline; gap: 10px; min-height: 44px; color: var(--ink); text-decoration: none; font: 700 23px var(--serif); letter-spacing: .01em; }
.signal-brand-mark { display: none; }
.signal-nav { margin-left: auto; display: flex; align-items: center; gap: 4px; }
.signal-nav a { min-height: 44px; padding: 0 13px; display: inline-flex; align-items: center; color: var(--ink); text-decoration: none; font-size: 14px; font-weight: 500; }
.signal-nav a:hover { text-decoration: underline; text-underline-offset: 6px; }
.signal-nav a[aria-current="page"] { font-weight: 700; box-shadow: inset 0 -2px 0 0 var(--ink); }
.signal-record-line { border-bottom: 1px solid var(--rule); background: var(--paper); font: 500 11px var(--mono); letter-spacing: .06em; text-transform: uppercase; color: var(--ink-soft); }
/* Full-bleed row whose padding alone forms the gutter — the same pattern as
   .signal-header and .signal-footer. It must NOT also carry max-width +
   margin-inline: this box would then already be inset by the gutter before the
   padding added it a second time, indenting the record line past every other
   element on the page by (100vw - workspace) — 80px at 1440, 640px at 2560. */
.signal-record-line > div { padding: 5px max(20px, calc((100vw - var(--workspace)) / 2)); display: flex; gap: 28px; flex-wrap: wrap; }
.signal-record-line a { min-height: 44px; display: inline-flex; align-items: center; color: inherit; }
/* The record line is a wrap-prone flex row of two mono items; near the phone
   width its 1-vs-2-line wrap flips with the mono advance width (metric fallback
   vs real Plex Mono), shoving the whole page (#main-content) down ~42px on the
   late-font cold load — a ~0.2-0.3 CLS that fires intermittently on slower CI
   runners. Stacking below the phone breakpoint makes the height wrap-independent
   (item heights are metric-matched, not content-width-driven), so font-load
   timing can no longer move the page. */
@media (max-width: 600px) { .signal-record-line > div { flex-direction: column; gap: 4px; } }
.signal-context { position: relative; margin-left: 8px; color: var(--gold); font: 700 11px var(--mono); letter-spacing: .08em; text-transform: uppercase; white-space: nowrap; }
.signal-context summary { min-height: 44px; display: inline-flex; align-items: center; cursor: pointer; list-style: none; }
.signal-context summary::-webkit-details-marker { display: none; }
.signal-context summary::after { content: "＋"; margin-left: 7px; color: var(--teal); font-size: 14px; }
.signal-context[open] summary::after { content: "−"; }
/* <details>'s native content-hiding doesn't reliably suppress an
   absolutely-positioned child when closed (position: absolute escapes the
   layout it would otherwise skip) — the popover's box still gets laid out
   off the edge of the viewport at high zoom, forcing a site-wide horizontal
   scrollbar even though nothing is open. Force it explicitly. */
.signal-context:not([open]) .signal-context-popover { display: none; }
.signal-context-popover { position: absolute; z-index: 120; top: calc(100% + 10px); right: 0; width: min(360px, calc(100vw - 32px)); padding: 18px; border: 1px solid var(--rule-strong); background: var(--paper); color: var(--ink-soft); letter-spacing: normal; text-transform: none; white-space: normal; }
.signal-context-popover strong { display: block; color: var(--paper-50); font: 700 14px var(--sans); }
.signal-context-popover p { margin: 8px 0 12px; font: 14px/1.5 var(--sans); }
.signal-context-popover a { color: var(--teal); font: 700 13px var(--sans); text-underline-offset: 3px; }
.signal-menu-button, .signal-ask { display: none; min-width: 44px; min-height: 44px; }

/* Front-page masthead. Before this, the largest thing above the fold was a
   syndicated news headline at 22px and the only h1 was sr-only, so the front
   door read as a news aggregator and never said what Pheme is at any visible
   size. Uses the same --page-title scale as every other route so the front page
   speaks at the site's voice rather than inventing an eighth one. */
.home-masthead { padding-block: clamp(40px, 6vw, 72px) clamp(28px, 3vw, 40px); border-bottom: 1px solid var(--rule); }
.home-masthead h1 { margin: 0; max-width: 20ch; font: 650 var(--page-title)/1.06 var(--serif); letter-spacing: -.025em; }
.home-masthead-lede { max-width: 62ch; margin: 20px 0 0; color: var(--ink-soft); font: 17px/1.55 var(--sans); }
.home-masthead-lede b { color: var(--ink); font-weight: 650; }
.home-masthead-actions { margin-top: 28px; display: flex; flex-wrap: wrap; align-items: center; gap: 12px 16px; }
/* Scoped so it outranks .signal-button's transparent background, which is
   declared later in this file. The primary action has to look primary. */
.home-masthead .home-masthead-primary { border-color: var(--ink); background: var(--ink); color: var(--paper); }
.home-masthead .home-masthead-primary:hover { background: var(--ink-soft); border-color: var(--ink-soft); }
@media (max-width: 700px) {
  /* Reserve the lede's worst-case height. Measured in the Linux container (the
     platform CI renders on): with Public Sans still loading, the fallback's
     wider metrics wrap this to 6 lines / 149px; once the webfont lands it
     reflows to 5 lines / 124px, and that 25px shrink drags the action row and
     the entire digest below it upward — CLS 0.152 against a 0.05 budget,
     deterministic, and invisible on macOS because its fallback metrics differ.
     Six lines at this size and leading is the ceiling, so the block height is
     wrap-independent and font-load timing can no longer move the page. Same
     reserve-dimensions rule the record line already follows. Desktop measured
     stable at 444px either way and needs no reservation. */
  .home-masthead-lede { font-size: 16px; min-height: calc(6 * 1.55em); }
  .home-masthead-actions { flex-direction: column; align-items: stretch; }
  .home-masthead-actions .text-link { align-self: flex-start; }
}
.signal-eyebrow { margin: 0 0 18px; color: var(--gold); font: 750 12px var(--mono); letter-spacing: .12em; text-transform: uppercase; }
.signal-eyebrow::before { content: ""; display: inline-block; width: 26px; margin-right: 10px; border-top: 1px solid currentColor; vertical-align: middle; opacity: .65; }
h1, h2, h3, p { text-wrap: pretty; }
/* No auto-hyphenation on display type. Newspapers hyphenate narrow justified
   body columns, not headlines — at these sizes an algorithmic break reads as a
   rendering fault, not a typographic choice ("Bills — pending federal
   leg-islation", "United States — 51 juris-dictions" on a 390px phone).
   overflow-wrap stays as the emergency valve for genuinely unbreakable strings
   (a long URL or ID) so nothing can overflow its column. */
h1, h2, h3 { overflow-wrap: break-word; hyphens: none; }
.page-hero h1 { margin: 0; max-width: 820px; font: 650 var(--page-title)/1.06 var(--serif); letter-spacing: -.035em; }
.page-hero > p:not(.signal-eyebrow) { max-width: 680px; margin: 24px 0 0; color: var(--muted); font-size: clamp(17px, 2vw, 21px); line-height: 1.55; }
.signal-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 32px; }
.signal-button { min-height: 48px; padding: 11px 19px; display: inline-flex; align-items: center; justify-content: center; gap: 12px; border: 1px solid var(--ink); border-radius: var(--radius-control); background: transparent; color: var(--ink); text-decoration: none; font: 500 14px var(--sans); cursor: pointer; }
.signal-button:hover { background: var(--paper-deep); }
.signal-button-primary { border-color: var(--ink); background: var(--ink); color: var(--paper); }
.signal-button-primary:hover { background: #3a3328; color: var(--paper); }
.text-link { display: inline-flex; min-height: 44px; align-items: center; color: var(--ink); font-weight: 600; text-decoration: underline; text-underline-offset: 4px; }

.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

.section-intro { max-width: 720px; margin-bottom: 36px; }.section-intro h2, .page-note h2 { margin:0; font:650 var(--title-statement)/1.08 var(--display); letter-spacing:-.025em; }.section-intro > p:last-child, .page-note > p { color:var(--muted); }
.scope-grid span, .link-rail span { color:var(--gold); font:700 11px var(--mono); letter-spacing:.08em; text-transform:uppercase; }
.path-index { border-top: 1px solid var(--line); }
.path-index a { min-height: 92px; display: grid; grid-template-columns: 48px minmax(180px, .8fr) minmax(0, 1.4fr) 96px; gap: 20px; align-items: center; border-bottom: 1px solid var(--line); color: inherit; text-decoration: none; }
.path-index a:hover { background: var(--paper-deep); }
.path-index span { color: var(--gold); font: 700 12px var(--mono); }
.path-index strong { font: 650 25px/1.1 var(--display); }
.path-index em { color: var(--muted); font-size: 14px; font-style: normal; }
.path-index b { justify-self: end; color: var(--teal); font: 700 12px var(--mono); letter-spacing: .08em; text-transform: uppercase; }
.data-badge { display:inline-flex; min-height:28px; align-items:center; padding:4px 9px; border:1px solid currentColor; font:750 11px var(--mono); letter-spacing:.05em; text-transform:uppercase; }.data-badge.observed{color:var(--blue)}.data-badge.modeled{color:var(--gold)}
.news-meta{color:var(--ink-soft);font:700 11px/1.45 var(--mono);text-transform:uppercase}.news-meta{font-size:12px}
.kicker{font-family:var(--mono);font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-soft)}
.chips{display:flex;flex-wrap:wrap;gap:var(--s2)}
.chip{min-height:44px;padding:0 var(--s4);display:inline-flex;align-items:center;border:1px solid var(--rule-strong);background:transparent;color:var(--ink-soft);font:600 13px var(--sans);cursor:pointer}
.chip:hover{border-color:var(--ink)}
.chip[aria-pressed="true"]{border-color:var(--ink);background:var(--paper-deep);color:var(--ink);font-weight:700}
.record-cols{display:grid;grid-template-columns:minmax(0,2fr) minmax(260px,1fr);gap:var(--s8);align-items:start;padding-block:var(--s6) var(--s8)}
/* Seam: bare `1fr` gives the single stacked track an automatic (content-based)
   minimum, so a dense descendant (e.g. accuracy's battleground grid) can blow
   the track past 320-390px; minmax(0,1fr) matches the desktop column's own
   safeguard above and lets content wrap instead of overflowing. */
@media (max-width:1099px){.record-cols{grid-template-columns:minmax(0,1fr);gap:var(--s6)}}

/* ============================================================
   HOME — front page ("today's record"). Phase 4 recompose: the front
   page is a dated index of what changed, not a marketing hero, so this
   block uses the canonical token names directly (not the legacy
   --muted/--line/--teal aliases above) per the design-system's Phase
   4-9 migration note. Shared masthead/footer/record-line come from
   render.mjs (untouched); everything below is main#main-content only.
   ============================================================ */
hr.rule { border: 0; border-top: 1px solid var(--rule); margin-block: 0; height: 0; }
hr.rule.rule-strong { border-top-color: var(--rule-strong); }
.home-lead { display: grid; grid-template-columns: minmax(0, 2fr) minmax(260px, 1fr); gap: var(--s8); padding-block: var(--s7) var(--s6); align-items: start; }
.home-digest > .prov { padding-left: 0; border-left: 0; }
.home-stories { margin-top: var(--s3); border-top: 1px solid var(--rule); }
.home-story { padding: var(--s6) 0; border-bottom: 1px solid var(--rule); }
.home-story .kicker { font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-soft); }
.home-story h3 { margin: var(--s2) 0 0; font: 650 22px/1.25 var(--serif); }
.home-story p { margin: var(--s2) 0 0; max-width: var(--measure); color: var(--ink-soft); font-family: var(--sans); font-size: 14.5px; }
.home-story-actions { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); margin-top: var(--s3); }
.home-story-actions a { min-height: 44px; display: inline-flex; align-items: center; color: var(--ink); font-family: var(--sans); font-size: 13px; font-weight: 700; text-decoration: none; }
.home-story-actions a:hover { text-decoration: underline; text-underline-offset: 3px; }
/* Modelling a reaction to a sourced story is the only thing on this page that
   no other site can do, and it used to render at 13px, third of three, at the
   same weight as the link that sends the reader off to the publisher. Give it
   the weight of the claim: a real bordered control, first in the row, on both
   the front page and the news desk. Everything else in the row stays a quiet
   text link so the hierarchy is unambiguous. */
.home-story-actions .story-action-primary,
.news-actions .story-action-primary {
  padding: 0 15px;
  border: 1px solid var(--ink);
  border-radius: var(--radius-control);
  background: var(--ink);
  color: var(--paper);
  font-size: 14px;
}
.home-story-actions .story-action-primary:hover,
.news-actions .story-action-primary:hover {
  background: var(--ink-soft);
  border-color: var(--ink-soft);
  text-decoration: none;
}
.home-digest-more { margin-top: var(--s4); }
.home-digest-empty { padding: var(--s4) 0; color: var(--ink-soft); font-family: var(--sans); font-size: 14.5px; }
/* Height matches a realistic rendered .home-story (kicker + headline +
   summary + action row) at the desktop digest-column width, not an
   arbitrary placeholder bar height — the previous fixed 108px reserved far
   less than the ~236-340px real stories render at, so the #home-news
   innerHTML swap (home.mjs) collapsed .margin-note/.home-digest-more by
   hundreds of px once real content arrived (docs/PERFORMANCE_BUDGETS.md CLS
   budget failure). See the mobile override below for the narrower column. */
.home-story-skeleton { height: 220px; border-bottom: 1px solid var(--rule); background: linear-gradient(var(--paper-deep) 22px, transparent 0 44px, var(--paper-deep) 0 56px, transparent 0); background-size: 70% 100%; background-repeat: no-repeat; }
.home-story-skeleton + .home-story-skeleton { border-top: 0; }

.margin-note { padding: var(--s5) 0 0 var(--s6); border-left: 1px solid var(--rule); }
.margin-note h2 { margin: 0 0 var(--s3); font: 700 11px var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--ink-soft); }
.margin-note .prov { padding-left: var(--s3); }
.margin-note-lede { margin: var(--s4) 0 0; max-width: 34ch; color: var(--ink-soft); font-family: var(--sans); font-size: 14px; line-height: 1.5; }
.margin-note-limits { margin: var(--s4) 0 0; padding-left: 1.1em; max-width: 34ch; color: var(--ink-soft); font-family: var(--sans); font-size: 14px; line-height: 1.5; }
.margin-note-limits li { margin-bottom: 8px; }
.margin-note .signal-actions { margin-top: var(--s5); flex-direction: column; align-items: stretch; }
.margin-note .signal-button { width: 100%; }

.home-atlas { padding-block: var(--s7); }
.home-atlas-heading { display: flex; align-items: baseline; gap: var(--s5); flex-wrap: wrap; }
.home-atlas-heading h2 { margin: 0; font: 650 var(--title-section) var(--serif); }
.home-atlas-heading p { margin: 0; color: var(--ink-soft); font-family: var(--sans); font-size: 14px; }
.mini-plate { display: block; margin-top: var(--s5); border: 1px solid var(--rule); }
/* aspect-ratio matches the build-time viewBox (VIEW_WIDTH/VIEW_HEIGHT =
   640/400 in scripts/build-home-plate.mjs, same constant for both the US and
   world plates) so the box is reserved at first paint instead of resolving
   from the SVG's intrinsic size once layout settles — an unreserved inline
   <svg width:100%;height:auto> here was the home-page CLS budget failure's
   dominant source (home-atlas collapsing 288px -> 104px, docs/PERFORMANCE_BUDGETS.md). */
.mini-plate-svg { width: 100%; height: auto; aspect-ratio: 640 / 400; display: block; }
.mini-plate-svg .r0 { fill: var(--ev-unknown); }
.mini-plate-svg .r1 { fill: var(--ramp-ink-1); }
.mini-plate-svg .r2 { fill: var(--ramp-ink-2); }
.mini-plate-svg .r3 { fill: var(--ramp-ink-3); }
.mini-plate-svg .r4 { fill: var(--ramp-ink-4); }
.mini-plate-svg .r5 { fill: var(--ramp-ink-5); }
.mini-plate-svg .r6 { fill: var(--ramp-ink-6); }
.mini-plate-svg path { stroke: var(--paper); stroke-width: 0.6; }
.home-atlas-table { margin-top: var(--s3); }

.home-miss { padding-block: var(--s7); border-top: 1px solid var(--rule-strong); }
.home-miss h2 { margin: 0; font: 650 var(--title-statement)/1.1 var(--serif); letter-spacing: -.015em; }
.home-miss-lede { margin: var(--s3) 0 0; max-width: var(--measure); color: var(--ink-soft); font-family: var(--sans); font-size: 14.5px; }
.home-miss-blocks { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s5); margin-top: var(--s4); max-width: 560px; }
.home-miss-value { margin: var(--s2) 0 0; font-size: 34px; font-weight: 600; font-variant-numeric: tabular-nums; }
.home-miss-source { margin: 0; color: var(--ink-soft); font-family: var(--sans); font-size: 13px; }
.home-miss-copy { margin-top: var(--s4); font-family: var(--sans); font-size: 14.5px; }
.home-miss-protocol { margin-top: var(--s2); color: var(--ink-soft); font-family: var(--mono); font-size: 11.5px; line-height: 1.6; }
.home-miss-protocol a { color: var(--ink); }

.home-index { border-top: 1px solid var(--rule); }

.home-method-strip { margin-block: var(--s7); padding: var(--s6); display: grid; grid-template-columns: repeat(3, 1fr) auto; gap: var(--s5); align-items: start; border: 1px solid var(--rule); }
.home-method-strip > div { display: grid; grid-template-columns: 28px 1fr; column-gap: var(--s2); }
.home-method-strip span { grid-row: 1 / 3; color: var(--ink-soft); font: 700 12px var(--mono); }
.home-method-strip strong { font-family: var(--serif); }
.home-method-strip p { grid-column: 2; color: var(--ink-soft); font-size: 13px; }
.home-method-strip > a { min-height: 44px; display: flex; align-items: center; color: var(--ink); font-weight: 700; text-decoration: none; }
.home-method-strip > a:hover { text-decoration: underline; }

@media (max-width: 1099px) {
  .home-lead { grid-template-columns: 1fr; gap: var(--s6); }
  .margin-note { padding-left: 0; border-left: 0; padding-top: var(--s5); border-top: 1px solid var(--rule); }
  .home-miss-blocks { max-width: none; }
  .home-method-strip { grid-template-columns: 1fr 1fr; }
  /* The single-column digest is a narrower measure than the desktop 2fr
     column, so the same story text wraps to more lines — real stories run
     ~300-370px here versus ~236-260px at the desktop width above. */
  .home-story-skeleton { height: 300px; }
}

/* /explore — the Maps front. Two real mini plates instead of a slogan hero
   (see docs/design-intents/explorer-atlas.json): each card carries the same
   build-time-generated static SVG + legend the home page's US plate uses
   (js/atlas engine, not a screenshot), so the object on this page IS the
   map, not a card standing in for one. */
.explore-hero { padding-block: var(--s6) var(--s5); }
/* The Maps hub declared no font at all and fell through to the browser
   default, which is how it stayed the one route off the shared scale after the
   page-title unification — an eighth treatment hiding as an absence rather
   than a value. Caught by auditing every route's rendered h1 instead of the
   rules I had already touched. */
.explore-hero h1 { margin: 0; max-width: 900px; font: 650 var(--page-title)/1.06 var(--serif); letter-spacing: -.025em; }
.explore-plates { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s5); margin-top: var(--s5); }
.explore-plate-card { padding: var(--s5); border: 1px solid var(--rule); }
.explore-plate-card h2 { margin: 0; font: 650 var(--title-section) var(--serif); }
.explore-plate-card .mini-plate { margin-top: var(--s4); }
.explore-plate-card .legend-bar { margin-top: var(--s3); }
.explore-plate-actions { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s4); }
.explore-plate-actions a { flex: 1 1 auto; text-align: center; }
.explore-table-links { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s6); margin-top: var(--s6); padding-top: var(--s5); border-top: 1px solid var(--rule); }
.explore-table-links a { min-height: 44px; display: inline-flex; align-items: center; color: var(--ink); font-family: var(--sans); font-size: 14px; font-weight: 700; text-decoration: none; }
.explore-table-links a:hover { text-decoration: underline; text-underline-offset: 3px; }

@media (max-width: 720px) {
  .explore-plates { grid-template-columns: 1fr; }
}

.signal-page { padding-bottom:96px; }.page-hero { padding-block:clamp(72px,10vw,140px) 64px; border-bottom:1px solid var(--line); }.page-hero h1 { max-width:1000px; }.page-hero .data-badge { margin-top:26px; }.scope-grid { display:grid; grid-template-columns:1fr 1fr; margin-top:64px; border:1px solid var(--line); }.scope-grid > a { min-height:380px; padding:clamp(28px,5vw,64px); display:flex; flex-direction:column; color:inherit; text-decoration:none; }.scope-grid > a:first-child{border-right:1px solid var(--line)}.scope-grid>a:hover{background:var(--paper-deep)}.scope-grid h2{margin:50px 0 14px;font:650 var(--title-statement)/1.08 var(--display)}.scope-grid p{max-width:520px;color:var(--muted)}.scope-grid b{margin-top:auto;color:var(--teal)}
.page-note { margin-top:64px; padding:clamp(28px,5vw,56px); border:1px solid var(--line); background:var(--ink-900); }.three-column { display:grid; grid-template-columns:repeat(3,1fr); gap:32px; margin-top:32px; }.three-column p{color:var(--muted)}.three-column b{color:var(--paper-50)}
.link-rail { margin-top:32px; display:grid; grid-template-columns:repeat(4,1fr); border:1px solid var(--line); }.link-rail a{min-height:120px;padding:22px;display:flex;flex-direction:column;justify-content:space-between;border-right:1px solid var(--line);text-decoration:none;font-weight:800}.link-rail a:last-child{border:0}
.reading-page .page-hero { max-width:var(--workspace); }.prose-grid { display:grid;grid-template-columns:minmax(0,720px) minmax(260px,1fr);gap:80px;padding-top:72px}.prose-grid article h2,.method-chapters h2,.transparency-grid h2,.terms-list h2{font:650 var(--title-section) var(--display)}.prose-grid article p,.method-chapters p,.transparency-grid p,.terms-list p{color:var(--muted)}.prose-grid aside{padding:26px;border-left:1px solid var(--line)}.prose-grid aside a{min-height:48px;display:flex;align-items:center;border-bottom:1px solid var(--line);color:var(--teal);font-weight:800}
.method-chapters,.terms-list { display:grid;grid-template-columns:1fr 1fr;gap:0;margin-top:64px;border:1px solid var(--line)}.method-chapters article,.terms-list article{min-height:300px;padding:36px;border-right:1px solid var(--line);border-bottom:1px solid var(--line)}.method-chapters article:nth-child(even),.terms-list article:nth-child(even){border-right:0}.method-chapters article:nth-last-child(-n+2),.terms-list article:nth-last-child(-n+2){border-bottom:0}.method-chapters article>span,.terms-list article>span{color:var(--gold);font:700 12px var(--mono)}
.transparency-grid { display:grid;grid-template-columns:repeat(2,1fr);margin-top:64px;border:1px solid var(--line)}.transparency-grid article{min-height:260px;padding:36px;border-right:1px solid var(--line);border-bottom:1px solid var(--line)}.transparency-grid article:nth-child(even){border-right:0}.transparency-grid article:nth-last-child(-n+2){border-bottom:0}

/* Observatory indexes: route and trust pages read as ledgers, not collections
   of interchangeable product cards. */
.scope-grid { display: block; border-inline: 0; border-bottom: 0; }
.scope-grid > a { min-height: 210px; padding: 38px 0; display: grid; grid-template-columns: 160px minmax(280px, .9fr) minmax(260px, 1fr); column-gap: 38px; align-items: start; border-right: 0 !important; border-bottom: 1px solid var(--line); }
.scope-grid > a > span { grid-row: 1 / 3; }
.scope-grid h2 { margin: 0; }
.scope-grid p { margin: 0; }
.scope-grid b { grid-column: 3; align-self: end; }
.method-chapters, .terms-list { display: block; border-inline: 0; border-bottom: 0; }
.method-chapters article, .terms-list article { min-height: 190px; padding: 34px 0; display: grid; grid-template-columns: 72px minmax(240px, .7fr) minmax(0, 1.3fr); gap: 28px; align-items: start; border-right: 0 !important; border-bottom: 1px solid var(--line) !important; }
.method-chapters article > span, .terms-list article > span { grid-row: 1 / 3; }
.method-chapters article h2, .terms-list article h2 { margin: 0; }
.method-chapters article p, .terms-list article p { margin: 0; }
.transparency-grid { display: block; border-inline: 0; border-bottom: 0; }
.transparency-grid article { min-height: 180px; padding: 34px 0; display: grid; grid-template-columns: minmax(240px, .7fr) minmax(0, 1.3fr); gap: 44px; border-right: 0 !important; border-bottom: 1px solid var(--line) !important; }
.transparency-grid article h2, .transparency-grid article p { margin: 0; }
.privacy-table { margin-top:64px;border:1px solid var(--line)}.privacy-table>div{min-height:86px;padding:18px 22px;display:grid;grid-template-columns:220px 1fr 180px;gap:24px;align-items:center;border-bottom:1px solid var(--line)}.privacy-table>div:last-child{border:0}.privacy-table span{color:var(--muted)}.privacy-table strong{color:var(--gold);font-size:14px}

/* ============================================================
   THE RECORD OFFICE — Phase 9 recompose (about, method, transparency,
   privacy, terms). Retires the page-hero + signal-eyebrow "slogan" opening
   on these five pages for the same compact record-naming header news/bills/
   ask already use. table.record + .table-scroll are the canonical data-
   table component named in design-system-2026-07.md ("mono uppercase
   headers, hairline rows, tabular numerals, own horizontal scroll
   container"); privacy's retention table is their first caller. .ev-note is
   a spacing modifier for the standalone .ev-block sections these pages use
   in place of the old dark-background .page-note boxes — kept only where a
   box isn't evidence-classed (method's Canonical sources reference stays a
   plain .page-note).
   ============================================================ */
.record-top { padding-block: var(--s7) var(--s5); border-bottom: 1px solid var(--rule); }
.record-top h1 { margin: 0; font: 650 var(--page-title)/1.06 var(--serif); letter-spacing: -.015em; }
.record-top > p { margin: var(--s3) 0 0; max-width: var(--measure); color: var(--ink-soft); font: 500 15px/1.55 var(--sans); }

.ev-note { margin-top: var(--s6); padding: var(--s5); }
.ev-note h2 { margin: 0 0 var(--s2); font: 650 var(--title-section) var(--serif); }
.ev-note > p:not(.prov) { margin: 0; max-width: var(--measure); color: var(--ink-soft); }
.ev-note .signal-actions { margin-top: var(--s4); }
.ev-note #privacy-status { margin-top: var(--s3); color: var(--ink-soft); font-size: 13px; }

.table-scroll { margin-top: var(--s6); border: 1px solid var(--rule); overflow-x: auto; }
table.record { width: 100%; min-width: 700px; border-collapse: collapse; }
table.record th, table.record td { padding: var(--s3) var(--s4); text-align: left; vertical-align: top; }
table.record thead th { border-bottom: 1px solid var(--rule-strong); background: var(--paper-deep); color: var(--ink-soft); font: 700 11px var(--mono); text-transform: uppercase; letter-spacing: .05em; white-space: nowrap; }
table.record tbody th { color: var(--ink); font: 650 14px var(--sans); white-space: nowrap; }
table.record tbody td { border-bottom: 1px solid var(--rule); color: var(--ink-soft); font-size: 13.5px; line-height: 1.5; }
table.record tbody td:last-child { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
table.record tbody tr:last-child td, table.record tbody tr:last-child th { border-bottom: 0; }

.method-chapters article > .prov { grid-column: 3; margin-top: var(--s2); }

@media (max-width: 700px) {
  .record-top { padding-block: var(--s5) var(--s4); }
}

/* Feedback: the report form is the primary object on this route, so it sits
   directly under the hero with no card chrome of its own — the page shell
   already frames it. Category buttons reuse the .chip toggle recipe
   (border+background swap on the active state) already established on the
   News page instead of inventing a second toggle-button language. */
.feedback-section{max-width:640px;padding-block:8px 96px}
.feedback-types{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:22px}
.feedback-types .type-btn{min-height:44px;padding:0 18px;border:1px solid var(--line);background:transparent;color:var(--ink-soft);font:600 13px var(--sans);cursor:pointer}
.feedback-types .type-btn:hover{border-color:var(--line-strong);color:var(--ink)}
.feedback-types .type-btn.on{border-color:var(--ink);background:var(--ink);color:var(--paper);font-weight:700}
.feedback-hint{margin:0 0 18px;padding:11px 14px;border-left:3px solid var(--gold);background:var(--paper-deep);color:var(--ink-soft);font-size:13px}
.feedback-field{margin-bottom:24px}
.feedback-field label{display:block;margin-bottom:8px;color:var(--ink-soft);font:700 12px var(--mono);letter-spacing:.06em;text-transform:uppercase}
.feedback-optional{text-transform:none;font-weight:500;letter-spacing:normal}
.feedback-field textarea,.feedback-field input[type="email"]{width:100%;min-height:48px;padding:12px 14px;border:1px solid var(--line-strong);background:var(--paper-deep);color:var(--ink);font:15px/1.5 var(--sans)}
.feedback-field textarea{min-height:160px;resize:vertical}
.feedback-field textarea:focus,.feedback-field input[type="email"]:focus{outline:2px solid var(--ev-modeled);outline-offset:1px}
.feedback-count{margin-top:6px;color:var(--ink-soft);font:600 11px var(--mono);text-align:right}
.feedback-note{margin-top:10px;color:var(--muted);font-size:13px;line-height:1.5}
.feedback-submit-row{display:flex;align-items:center;gap:16px;flex-wrap:wrap}
.feedback-submit-row .status{color:var(--ink-soft);font:600 13px var(--sans)}
.feedback-submit-row .status.err{color:var(--ev-input)}
.feedback-success{margin-top:28px;max-width:520px}
.feedback-success h2{margin:0 0 6px;font:650 var(--title-section) var(--display)}
.feedback-success p{margin:0;color:var(--muted)}
/* ============================================================
   NEWS + STORY — Phase 5 recompose (evidence desk + claim view). Retired
   blocks: .news-hero, the old grid .news-toolbar/.topic-filters, the
   3-column .news-list article, .news-actions column-spanning hack,
   .story-article's marketing-hero h1, .evidence-warning, and the dark
   --ink-900-background .maat-* cards. This block uses canonical token
   names directly (not the legacy --muted/--line/--teal aliases above),
   matching the Phase 4 home recompose migration note.
   ============================================================ */
.news-top{padding-block:var(--s7) var(--s5)}
.news-top h1{margin:0;font:650 var(--page-title)/1.06 var(--serif);letter-spacing:-.015em}
.news-top .ev-block{margin-top:var(--s5);max-width:var(--measure)}
.news-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s4);padding-bottom:var(--s5);border-bottom:1px solid var(--rule)}
.news-toolbar label{flex-basis:100%;font:700 11px var(--mono);letter-spacing:.06em;text-transform:uppercase;color:var(--ink-soft)}
.news-toolbar input{flex:1;min-width:220px;min-height:48px;padding:10px 14px;border:1px solid var(--rule);background:var(--paper);color:var(--ink);font:15px var(--sans)}
.news-toolbar input:focus-visible{outline:2px solid var(--ev-modeled);outline-offset:1px}
/* #news-topics (the #q chips row) starts empty — loadNews() (news.mjs)
   populates one chip per distinct topic once the feed resolves. A real
   feed's topic count wraps to 2+ rows at the mobile width; reserve for that
   so the toolbar doesn't grow under .news-state/#news-list once populated. */
#news-topics:empty{min-height:44px}
.news-state{min-height:44px;padding-block:var(--s4);display:flex;align-items:center;gap:var(--s4)}
.news-state #news-status{flex:1;min-height:28px;color:var(--ink-soft);font-family:var(--sans);font-size:14px}
.news-state #news-retry{flex:0 0 auto}
.news-clear-row{margin-top:var(--s2)}
.news-list{border-top:1px solid var(--rule-strong)}
.news-list article{padding:var(--s5) 0;display:grid;grid-template-columns:160px minmax(0,1fr);gap:var(--s5);border-bottom:1px solid var(--rule-strong)}
.news-list h2{margin:var(--s2) 0 0;font:650 var(--title-section)/1.28 var(--serif)}
.news-list article>p:not(.news-meta){margin:var(--s2) 0 0;max-width:var(--measure);color:var(--ink-soft);font-family:var(--sans);font-size:14.5px}
.news-actions{display:flex;flex-wrap:wrap;gap:var(--s2) var(--s5);margin-top:var(--s3)}
.news-actions a{min-height:44px;display:inline-flex;align-items:center;color:var(--ink);font-family:var(--sans);font-size:13px;font-weight:700;text-decoration:none}
.news-actions a:hover{text-decoration:underline;text-underline-offset:3px}
/* Defined but never rendered until now — #news-list shipped as a bare empty
   div with zero reserved height, so the first render() swap (news.mjs) grew
   it from ~1px to its real height in one frame, collapsing .margin-note and
   dragging the rest of the page with it (docs/PERFORMANCE_BUDGETS.md CLS
   budget failure). Height approximates a real .news-list article at the
   desktop two-column width; see the mobile override below for the
   single-column article layout's taller wrap. */
.news-story-skeleton{height:230px;border-bottom:1px solid var(--rule);background:linear-gradient(var(--paper-deep) 22px,transparent 0 48px,var(--paper-deep) 0 62px,transparent 0);background-size:70% 100%;background-repeat:no-repeat}
.news-story-skeleton+.news-story-skeleton{border-top:0}

.story-article{max-width:var(--workspace)}
.story-article>header{padding-bottom:var(--s6);border-bottom:1px solid var(--rule)}
.story-article h1{margin:var(--s2) 0 0;font:650 var(--page-title)/1.06 var(--serif);letter-spacing:-.02em}
.story-source-action{margin-top:var(--s5)}
.story-observed{margin-top:var(--s4)}
.story-summary,.story-claims,.story-respond{padding:var(--s6) 0;border-bottom:1px solid var(--rule)}
.story-article h2{margin:var(--s2) 0 0;font:650 var(--title-section) var(--serif)}
.story-summary>p{margin-top:var(--s3);max-width:var(--measure);color:var(--ink-soft)}
.story-claims>p:not(.prov){margin-top:var(--s3);max-width:var(--measure);color:var(--ink-soft);font-family:var(--sans);font-size:13.5px}
.story-claims #evidence-run{margin-top:var(--s4)}
.story-respond .ev-block p:not(.prov){margin-top:var(--s3);max-width:var(--measure);color:var(--ink-soft);font-family:var(--sans);font-size:14px}
.story-respond .signal-actions{margin-top:var(--s4)}
.story-correction{padding-top:var(--s5);color:var(--ink-soft)}
.story-correction a{color:var(--ink)}
.story-expired{padding-block:var(--s7) var(--s8);max-width:var(--measure)}
.story-expired h1{margin:var(--s3) 0 0;font:650 30px/1.25 var(--serif)}
.story-expired p{margin-top:var(--s3);color:var(--ink-soft);font-family:var(--sans);font-size:15px}
.story-expired .signal-button{margin-top:var(--s5)}

.maat-output{margin-top:var(--s5)}
.maat-summary,.maat-claim{margin-top:var(--s4)}
/* The claim's kind now leads each block. It was already computed by
   lib/maat.js and thrown away by the renderer, which meant an opinion and a
   verifiable fact looked identical and carried the same supports/contradicts
   label — a truth frame on something with no truth value. The badge is the
   first thing read because it decides what the rest of the block can mean. */
.maat-kind{margin:0 0 var(--s2);display:flex;flex-wrap:wrap;align-items:baseline;gap:8px}
.maat-kind-badge{padding:2px 8px;border:1px solid var(--rule);border-radius:var(--radius-control);background:var(--paper-deep);color:var(--ink);font:700 11px var(--mono);letter-spacing:.06em;text-transform:uppercase;white-space:nowrap}
.maat-kind-what{max-width:62ch;color:var(--ink-soft);font:13px/1.5 var(--sans)}
.maat-claim-text{font-weight:650;font-family:var(--serif);font-size:17px}
.maat-relationship{display:block;margin-top:var(--s2);color:var(--ink-soft);font:600 12px/1.45 var(--mono)}
.maat-explain{margin-top:var(--s2);color:var(--ink-soft);font-family:var(--sans);font-size:13px}
.maat-where{margin-top:var(--s3);color:var(--ink-soft);font:700 11px var(--mono);letter-spacing:.05em;text-transform:uppercase}
.maat-sources{display:flex;flex-wrap:wrap;gap:var(--s3);margin-top:var(--s2)}
.maat-sources>span{display:grid;gap:2px}
.maat-sources small{color:var(--ink-soft);font:600 11px/1.4 var(--mono)}
.maat-sources a{min-height:44px;display:inline-flex;align-items:center;color:var(--ink);font-family:var(--sans);font-size:13px;font-weight:700}

.signal-footer { padding:56px max(20px,calc((100vw - var(--workspace))/2)); display:grid;grid-template-columns:1.5fr repeat(3,1fr);gap:48px;border-top:1px solid var(--line);background:var(--paper)}.signal-footer-brand{display:flex;gap:14px}.signal-footer-brand strong{font:650 24px var(--display)}.signal-footer-brand p{max-width:300px;margin:4px 0;color:var(--muted);font-size:13px}.signal-footer>div:not(.signal-footer-brand){display:flex;flex-direction:column}.signal-footer>div>strong{margin-bottom:12px;color:var(--paper-50);font:700 12px var(--mono);text-transform:uppercase;letter-spacing:.08em}.signal-footer a{min-height:44px;display:flex;align-items:center;color:var(--muted);font-size:13px;text-decoration:none}.signal-footer a:hover{color:var(--teal)}
.offline-banner{position:fixed;z-index:500;left:50%;bottom:16px;max-width:min(620px,calc(100% - 32px));padding:12px 16px;transform:translateX(-50%);border:1px solid var(--gold);background:var(--ink-900);color:var(--paper-50);font-size:13px;border-width:2px}

@media (max-width: 1099px) {
  .signal-header{gap:12px}.signal-ask{display:inline-flex;margin-left:auto;padding:0 14px;align-items:center;justify-content:center;border:1px solid var(--teal);color:var(--teal);text-decoration:none;font-weight:800}.signal-menu-button{display:inline-flex;align-items:center;gap:8px;border:0;background:transparent;color:var(--paper-50);font-weight:750;cursor:pointer}.signal-menu-button>span:first-child{position:relative;width:18px;height:14px;border-block:2px solid currentColor}.signal-menu-button>span:first-child::after{content:"";position:absolute;left:0;top:50%;width:100%;border-top:2px solid currentColor;transform:translateY(-50%)}.signal-context{display:none}.signal-nav{display:none;position:absolute;top:64px;left:0;right:0;padding:18px 20px 28px;grid-template-columns:1fr 1fr;background:var(--ink-900);border-bottom:1px solid var(--line)}.menu-open .signal-nav{display:grid}.signal-nav a{min-height:52px;border-bottom:1px solid var(--line)}
  .path-index a{grid-template-columns:38px minmax(0,1fr) 90px;gap:12px}.path-index em{grid-column:2;font-size:13px}.path-index b{grid-column:3;grid-row:1/3}.home-method-strip{grid-template-columns:1fr 1fr}.link-rail{grid-template-columns:1fr 1fr}.link-rail a:nth-child(2){border-right:0}.link-rail a:nth-child(-n+2){border-bottom:1px solid var(--line)}
  .signal-footer{grid-template-columns:1.5fr 1fr 1fr}.signal-footer>div:last-child{grid-column:2}
}

@media (max-width: 1099px) and (min-width: 701px) {
  .scope-grid > a { grid-template-columns: 120px minmax(0, 1fr); row-gap: 14px; }
  .scope-grid > a > span { grid-row: 1 / 4; }
  .scope-grid p, .scope-grid b { grid-column: 2; }
  .scope-grid b { align-self: start; }
}

@media (max-width: 700px) {
  body{font-size:16px}.signal-shell{width:min(calc(100% - 32px),var(--workspace))}.signal-header{min-height:56px;padding-inline:16px}.signal-record-line>div{padding-inline:16px}.signal-nav{top:56px;grid-template-columns:1fr}.signal-menu-label{display:none}.signal-brand{font-size:18px}.signal-context{display:block;margin-left:0}.signal-context summary{font-size:0}.signal-context summary::before{content:"ⓘ";font-size:17px;color:var(--teal)}.signal-context summary::after{display:none}.signal-context-popover{right:-8px;top:calc(100% + 8px)}.page-hero>p:not(.signal-eyebrow){font-size:17px}.signal-actions{flex-direction:column;align-items:stretch}.signal-button{width:100%}.path-index a{grid-template-columns:32px minmax(0,1fr);gap:10px;padding-block:16px}.path-index em{grid-column:2}.path-index b{grid-column:2;grid-row:auto;justify-self:start}.home-method-strip{grid-template-columns:1fr;padding:22px}.page-hero{padding-block:64px 42px}.scope-grid{grid-template-columns:1fr}.scope-grid>a{min-height:300px}.scope-grid>a:first-child{border-right:0;border-bottom:1px solid var(--line)}.three-column,.link-rail,.method-chapters,.transparency-grid,.terms-list{grid-template-columns:1fr}.link-rail a{border-right:0;border-bottom:1px solid var(--line)}.method-chapters article,.transparency-grid article,.terms-list article{min-height:auto;border-right:0;border-bottom:1px solid var(--line)!important}.method-chapters article:last-child,.transparency-grid article:last-child,.terms-list article:last-child{border-bottom:0!important}.prose-grid{grid-template-columns:1fr;gap:36px}.prose-grid aside{border-left:0;border-top:1px solid var(--line)}.privacy-table>div{grid-template-columns:1fr;gap:4px}.news-list article{grid-template-columns:1fr;gap:8px}.signal-footer{grid-template-columns:1fr 1fr;gap:32px}.signal-footer-brand{grid-column:1/-1}.signal-footer>div:last-child{grid-column:auto}
  /* The single-column article layout below wraps meta/headline/summary/
     actions taller than the two-column desktop layout above reserves for. */
  .news-story-skeleton{height:340px}
}

@media (max-width: 700px) {
  .scope-grid > a { min-height: auto; padding: 28px 0; display: block; }
  .scope-grid h2 { margin: 18px 0 10px; }
  .scope-grid p { margin-bottom: 20px; }
  .method-chapters article, .terms-list article, .transparency-grid article { min-height: auto; padding: 28px 0; display: block; }
  .method-chapters article h2, .terms-list article h2, .transparency-grid article h2 { margin: 12px 0; }
}

@media (prefers-reduced-motion: reduce) {
  html{scroll-behavior:auto}*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
}

@media (max-width: 700px) {
  .news-top{padding-block:var(--s5) var(--s4)}.news-toolbar{padding-bottom:var(--s4)}.news-toolbar input{min-width:0}.news-state{align-items:flex-start;flex-direction:column;gap:6px;padding-block:var(--s2)}.news-state #news-status{margin:0}.news-state #news-retry{width:100%}
  /* Narrower row fits fewer chips per line than the desktop width above, so
     a real topic list is more likely to wrap past one row. */
  #news-topics:empty{min-height:96px}
  .story-article>header{padding-bottom:var(--s5)}.story-source-action{margin-top:var(--s4)}.story-summary,.story-claims,.story-respond{padding:var(--s5) 0}
  .story-expired{padding-block:var(--s6) var(--s7)}.story-expired h1{font-size:26px}
}

/* ============================================================
   BILLS — the docket (Phase 6 recompose). Two view modes (Real Bills /
   Illustrative Lab) share one master-detail frame: the list scrolls
   independently on the left, the detail column stays sticky on the right
   (the pre-recompose page never pinned the detail side, so a long federal
   list left "Simulate reaction" thousands of px below an empty panel — see
   docs/design-intents/bills-docket.json's rejectedDefaults). Illustrative
   Lab's dashboard/interventions/demographics/state-explorer nest inside the
   sticky detail column exactly as before. Canonical token names used
   directly, matching the Phase 4+ recompose note above (no legacy --teal/
   --gold/--line aliases in new bills rules). Class names below are load-
   bearing: js/bills.mjs and bills-render.mjs generate markup with these
   exact class strings (bill-card, active, scope-btn, int-btn, real-pill,
   metric-box, support-badge…) — renaming any would silently unstyle
   dynamically-rendered content without breaking a single test.
   ============================================================ */
.bills-top{padding-block:var(--s7) var(--s5);border-bottom:1px solid var(--rule)}
.bills-top h1{margin:0;font:650 var(--page-title)/1.06 var(--serif);letter-spacing:-.015em}
.bills-cache-age{margin-left:2px;color:var(--ink-soft);font:600 13px var(--mono);letter-spacing:.01em}
.bills-top>p{margin:var(--s3) 0 0;max-width:var(--measure);color:var(--ink-soft);font-family:var(--sans);font-size:14.5px}
.bills-top .ev-block{margin-top:var(--s5);max-width:var(--measure)}
.bill-breadcrumb{margin:var(--s4) 0 0;font:600 11px var(--mono);color:var(--ink-soft)}
.bill-breadcrumb b{color:var(--ink)}
.bill-breadcrumb .bc-now{color:var(--ink);font-weight:700}
.mode-bar{display:flex;flex-wrap:wrap;gap:var(--s3);align-items:center;margin-top:var(--s4)}
.mode-bar-label{font:700 10px var(--mono);text-transform:uppercase;letter-spacing:.06em;color:var(--ink-soft)}

.scope-toggle-container{display:flex;flex-direction:column;gap:var(--s2);margin-bottom:var(--s4)}
.scope-toggle{display:flex;border:1px solid var(--rule);padding:2px;background:var(--paper-deep)}
/* 44px is the floor everywhere else on this page (.state-select, .guide-collapse
   summary, .mobile-bills-back all set it) and these were the exception at 40px,
   with a ≤600px override that raised them to 44 — i.e. the phone case was
   already right and the desktop one was not. Measured at 1280x900: all four
   scope buttons 40px. Target size is not a phone-only concern; a trackpad, a
   tremor and a stylus all exist at 1280 too. */
.scope-btn{flex:1;min-height:44px;padding:0 var(--s3);border:none;background:transparent;color:var(--ink-soft);font:700 11px var(--mono);cursor:pointer}
.scope-btn:hover{color:var(--ink)}
.scope-btn.active{background:var(--ink);color:var(--paper);font-weight:700}
.state-select{display:none;width:100%;min-height:44px;margin-top:var(--s2);padding:7px 9px;border:1px solid var(--rule);background:var(--paper);color:var(--ink);font:600 13px var(--sans)}
.state-scope-note{display:none;margin:var(--s2) 0 0;font-size:11px;color:var(--ink-soft)}
.state-scope-note.show{display:block}

.guide-collapse{margin:var(--s5) 0 0;border:1px solid var(--rule)}
.guide-collapse summary{min-height:44px;display:flex;align-items:center;padding:0 var(--s4);cursor:pointer;font:700 12px var(--mono);color:var(--ink);list-style:none;text-transform:uppercase;letter-spacing:.05em}
.guide-collapse summary::-webkit-details-marker{display:none}
.guide-collapse summary::after{content:"＋";margin-left:auto;color:var(--ink-soft)}
.guide-collapse[open] summary::after{content:"−"}
.guide-panel{margin:0;display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:var(--s5);padding:var(--s5);border-top:1px solid var(--rule)}
.guide-card .eyebrow{margin:0 0 var(--s2);font:700 11px var(--mono);text-transform:uppercase;letter-spacing:.05em;color:var(--ink-soft)}
.guide-text{margin:0;font-size:13px;line-height:1.55;color:var(--ink-soft)}
.section-title{font:700 13px var(--mono);color:var(--ink-soft);text-transform:uppercase;letter-spacing:.05em;margin:0 0 4px}
#bills-sidebar-title{margin-bottom:var(--s3)}

/* .progressive — the mobile-collapsible <details> wrapper reused by the
   demographic breakdown and state explorer; js/bills.mjs strips their
   `open` attribute on narrow phones via the data-mobile-collapsed hook. */
.progressive{border:1px solid var(--rule);margin-top:var(--s3)}
.progressive summary{min-height:44px;display:flex;align-items:center;padding:0 var(--s4);cursor:pointer;font:700 12px var(--mono);color:var(--ink);list-style:none;text-transform:uppercase;letter-spacing:.05em}
.progressive summary::-webkit-details-marker{display:none}
.progressive summary::after{content:"＋";margin-left:auto;color:var(--ink-soft)}
.progressive[open] summary::after{content:"−"}
.progressive-body{padding:var(--s4)}
.bills-breakdown>.progressive-body{padding-top:4px}
.bills-skeleton-row{height:64px;margin-bottom:var(--s3);border:1px solid var(--rule);background:linear-gradient(var(--paper-deep) 18px,transparent 0 40px,var(--paper-deep) 0 50px,transparent 0);background-size:70% 100%;background-repeat:no-repeat}

.layout-grid{display:grid;grid-template-columns:minmax(280px,1fr) minmax(0,1.6fr);gap:var(--s6);align-items:start;margin-top:var(--s6)}
.layout-grid .panel{min-width:0;border:1px solid var(--rule);padding:var(--s5)}
.layout-grid .panel:first-child{max-height:calc(100vh - 148px);overflow-y:auto}
.layout-grid .panel:last-child{position:sticky;top:92px}
.mobile-bills-back{display:none}
.bills-list{display:flex;flex-direction:column;gap:var(--s3)}
.bill-card{border:1px solid var(--rule);padding:var(--s4);cursor:pointer;display:flex;flex-direction:column;gap:var(--s2);background:var(--paper)}
.bill-card:hover{border-color:var(--rule-strong)}
.bill-card.active{border-color:var(--ink);border-left:3px solid var(--ink);background:var(--paper-deep)}
.bill-card:focus-visible{outline:2px solid var(--ev-modeled);outline-offset:2px}
.bill-header{display:flex;align-items:center;gap:var(--s3)}
/* Phase 10 emoji sweep: was a per-category emoji glyph (font-size sized for
   emoji metrics); now a typographic 2-letter mono category mark (EN/RV/VR/
   BD/HC/AI — see the BILLS/STATE_BILLS icon fields in js/bills.mjs), so it
   needs bold+letter-spacing instead of a bare large font-size. */
.bill-icon{width:32px;height:32px;flex-shrink:0;display:flex;align-items:center;justify-content:center;font:800 11px var(--mono);letter-spacing:.02em;color:var(--ink-soft);border:1px solid var(--rule)}
.bill-title{margin:0;flex:1;font:650 15px var(--serif);color:var(--ink);line-height:1.3}
/* C6 fix — every Illustrative Lab title carries this badge so a
   parenthetical suffix baked into bill.title (LAB-01, or an unlabeled
   realistic-looking "S. 1"/"S.B. 101") can never be mistaken for an
   official designation. Every bill inside #illustrative-view is a Lab
   item by construction, so the badge applies unconditionally there —
   see illustrativeTitleHtml()/illustrativeTitleLabel() in bills-render.mjs. */
.lab-badge{display:inline-flex;margin-right:6px;padding:1px 6px;border:1px dashed var(--ev-input);color:var(--ev-input);font:750 10px var(--mono);letter-spacing:.05em;text-transform:uppercase;vertical-align:middle;white-space:nowrap}
.bill-short-desc{margin:0;font-size:13px;color:var(--ink-soft);line-height:1.4}
.bill-progress-container{display:flex;align-items:center;gap:var(--s2);font:600 11px var(--mono);color:var(--ink-soft)}
.bill-progress-bar{flex:1;height:6px;background:var(--rule)}
.bill-progress-fill{height:100%;width:0%;background:var(--ramp-violet-3)}
.bill-progress-fill.pass{background:var(--ev-modeled)}
.bill-progress-fill.fail{background:var(--ramp-violet-2)}

.bill-details{display:flex;flex-direction:column;gap:var(--s5)}
.detail-header{display:flex;align-items:flex-start;gap:var(--s4);padding-bottom:var(--s4);border-bottom:1px solid var(--rule)}
.detail-header .bill-icon{width:56px;height:56px;font-size:16px}
.detail-title-block{flex:1;min-width:0}
.detail-title-block h2{margin:0 0 4px;font:650 var(--title-section) var(--serif);color:var(--ink)}
.detail-meta{display:flex;flex-wrap:wrap;gap:var(--s3);font:600 12px var(--mono);color:var(--ink-soft)}
.detail-meta span{padding:2px 8px;border:1px solid var(--rule)}
.detail-summary{margin:0;font-size:13.5px;line-height:1.5;color:var(--ink-soft)}

.pass-dashboard{display:grid;grid-template-columns:minmax(200px,1.4fr) minmax(160px,1fr) minmax(160px,1fr);gap:var(--s4);align-items:stretch}
.metric-box{padding:var(--s4);border:1px solid var(--rule);display:flex;flex-direction:column;justify-content:center;gap:4px;text-align:center}
.metric-box.featured{border-color:var(--ev-modeled)}
.metric-box.featured.pass{border-color:var(--ev-modeled);background:color-mix(in srgb,var(--ev-modeled) 6%,var(--paper))}
.metric-box.featured.fail{border-color:var(--ink-soft)}
.metric-label{font:700 11px var(--mono);color:var(--ink-soft);text-transform:uppercase;letter-spacing:.05em}
.metric-value{font:650 27px var(--serif);color:var(--ink)}
.metric-box.featured.pass .metric-value{color:var(--ev-modeled)}
.metric-subtext{font:600 12px var(--mono);color:var(--ink-soft)}

.interventions-section{display:flex;flex-direction:column;gap:var(--s3)}
.panel-note{max-width:72ch;margin:6px 0 var(--s4);color:var(--ink-soft);font:500 13px/1.55 var(--sans)}
.interventions-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:var(--s3)}
.int-btn{min-height:40px;padding:10px;display:flex;align-items:center;gap:var(--s2);border:1px solid var(--rule);background:var(--paper);color:var(--ink-soft);font:600 11px var(--mono);text-align:left;cursor:pointer}
.int-btn:hover{border-color:var(--rule-strong);color:var(--ink)}
.int-btn.active{border-color:var(--ev-modeled);background:color-mix(in srgb,var(--ev-modeled) 8%,var(--paper));color:var(--ink)}
.int-btn-cell{display:flex;align-items:center;gap:4px}
.int-btn-cell .int-btn{flex:1}

.demo-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:var(--s4)}
.demo-card{border:1px solid var(--rule);padding:var(--s4)}
.demo-card h3{margin:0 0 var(--s3);padding-bottom:6px;font:700 11px var(--mono);color:var(--ink-soft);text-transform:uppercase;letter-spacing:.03em;border-bottom:1px solid var(--rule)}
.bar-row{display:flex;align-items:center;gap:var(--s2);margin-bottom:var(--s2);font-size:11px}
.bar-row:last-child{margin-bottom:0}
.bar-label{width:80px;color:var(--ink-soft);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bar-track{flex:1;height:10px;background:var(--rule)}
.bar-fill{height:100%;width:0%;background:var(--ev-modeled)}
.bar-val{width:32px;text-align:right;font:600 10px var(--mono);color:var(--ink)}

.state-explorer{display:flex;flex-direction:column;gap:var(--s2)}
.table-wrapper{max-height:250px;overflow-y:auto;border:1px solid var(--rule)}
.bills-page table{width:100%;border-collapse:collapse;font-size:11px}
.bills-page th{position:sticky;top:0;background:var(--paper-deep);border-bottom:1px solid var(--rule);color:var(--ink-soft);font:700 10px var(--mono);text-transform:uppercase;letter-spacing:.03em;text-align:left;padding:8px 10px}
.bills-page td{padding:7px 10px;border-bottom:1px solid var(--rule);color:var(--ink-soft)}
.bills-page tr:last-child td{border-bottom:none}
.bills-page tr:hover td{background:var(--paper-deep);color:var(--ink)}
.support-badge{font:600 10px var(--mono);padding:2px 6px;display:inline-block;border:1px solid currentColor}
.support-badge.high{color:var(--ev-modeled)}
.support-badge.mid{color:var(--ink-soft)}
.support-badge.low{color:var(--ink-faint)}

.senators-panel{display:flex;flex-direction:column;gap:var(--s2);margin-bottom:var(--s4);padding-bottom:var(--s4);border-bottom:1px solid var(--rule)}
.senators-title{font:700 11px var(--mono);letter-spacing:.03em;text-transform:uppercase;color:var(--ink-soft)}
.senator-cards{display:flex;flex-wrap:wrap;gap:var(--s3)}
.senator-card{display:flex;align-items:center;gap:var(--s3);border:1px solid var(--rule);padding:8px 10px;min-width:200px;flex:1 1 220px}
.senator-avatar{width:36px;height:36px;border-radius:50%;border:1px solid var(--rule);flex-shrink:0;display:grid;place-items:center;background:var(--paper-deep);color:var(--ev-modeled);font:800 12px var(--mono);letter-spacing:.05em}
.senator-info{display:flex;flex-direction:column;gap:2px;min-width:0}
.senator-name-row{display:flex;align-items:center;gap:var(--s2)}
.senator-name{font:700 13px var(--sans);color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.senator-sponsored{font:500 11px var(--sans);color:var(--ink-soft);line-height:1.4}

.real-bills-meta{margin:8px 0 var(--s3);font:600 12px var(--mono);color:var(--ink-soft);min-height:16px}
.real-bill-detail{display:flex;flex-direction:column;gap:var(--s4)}
.real-bill-header{display:flex;flex-direction:column;gap:var(--s3);padding-bottom:var(--s4);border-bottom:1px solid var(--rule)}
.real-bill-kicker{display:flex;flex-wrap:wrap;gap:var(--s2);align-items:center}
.real-pill{display:inline-flex;align-items:center;gap:6px;max-width:100%;border:1px solid var(--rule);color:var(--ink-soft);padding:4px 8px;font:700 11px var(--mono);letter-spacing:.02em;text-transform:uppercase;white-space:normal;overflow-wrap:anywhere}
.real-pill.source{color:var(--ev-observed);border-color:var(--ev-observed)}
.real-pill.live{color:var(--ev-modeled)}
.real-pill.stale{color:var(--ev-input);border-color:var(--ev-input)}
.real-bill-title{margin:0;font:750 var(--title-section) var(--serif);letter-spacing:-.01em;color:var(--ink)}
.real-bill-subtitle{margin:0;font:600 14px var(--sans);color:var(--ink);line-height:1.45}
.real-bill-chips{display:flex;flex-wrap:wrap;gap:var(--s2)}
.real-bill-section{display:flex;flex-direction:column;gap:var(--s2);border:1px solid var(--rule);padding:12px 14px}
.real-bill-section .label{font:700 10px var(--mono);letter-spacing:.05em;text-transform:uppercase;color:var(--ink-soft)}
.real-bill-section p{margin:0;font-size:13px;line-height:1.55;color:var(--ink)}
.real-reaction-shell{display:flex;flex-direction:column;gap:var(--s4)}
.real-shares{display:grid;gap:var(--s2)}
.real-share-row{display:grid;grid-template-columns:92px 1fr auto;gap:var(--s2);align-items:center;font:600 12px var(--sans)}
.real-share-bar{height:8px;background:var(--rule)}
.real-share-fill{height:100%;background:var(--ramp-violet-1)}
.real-share-fill.support{background:var(--ev-modeled)}
.real-share-fill.unsure{background:var(--ramp-violet-3)}
.real-share-fill.oppose{background:var(--ramp-violet-1)}
.real-quotes{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:var(--s3)}
.real-quote{border:1px solid var(--rule);padding:12px}
.real-quote-top{display:flex;align-items:center;justify-content:space-between;gap:var(--s2);margin-bottom:var(--s2)}
.real-quote-top strong{font:700 12px var(--sans);color:var(--ink)}
.real-quote-top span{font:600 10px var(--mono);color:var(--ink-soft);text-transform:uppercase;letter-spacing:.02em}
.real-quote p{margin:0;color:var(--ink-soft);font-size:12px;line-height:1.55}
.real-reaction-note{margin:0;color:var(--ink-soft);font-size:12px;line-height:1.45}

/* .btn — bills-render.mjs and js/bills.mjs both bake class="btn" into
   generated markup (retry/simulate/senator-retry buttons); this is the only
   place that class name is styled now that generated pages no longer load
   pheme.css. Teal accent retired in favor of the ev-modeled violet, this
   page's one recurring "do the modeled thing" action color. */
.bills-page .btn{min-height:44px;padding:10px 16px;border:1px solid var(--ev-modeled);background:color-mix(in srgb,var(--ev-modeled) 10%,var(--paper));color:var(--ev-modeled);font:700 13px var(--sans);cursor:pointer}
.bills-page .btn:hover{background:color-mix(in srgb,var(--ev-modeled) 18%,var(--paper))}
.bills-page .btn:disabled{opacity:.6;cursor:default}
#real-simulate-btn{background:var(--ev-modeled);border-color:var(--ev-modeled);color:var(--paper)}
#real-simulate-btn:hover:not(:disabled){background:color-mix(in srgb,var(--ev-modeled) 88%,#000)}

.hint{font:500 13px var(--sans);color:var(--ink-soft);line-height:1.5}

/* .help / #help-pop — ⓘ affordance + shared tooltip, wired by
   UI.initHelpBubbles(); ported from pheme.css's shared block since
   generated pages no longer load that file. */
.help{all:unset;display:inline-flex;align-items:center;justify-content:center;min-width:44px;min-height:44px;width:44px;height:44px;margin-left:2px;border-radius:50%;border:1px solid var(--rule);color:var(--ink-soft);font:700 11px var(--sans);cursor:pointer;vertical-align:middle}
.help:hover,.help:focus-visible{color:var(--paper);background:var(--ev-input);border-color:var(--ev-input);outline:none}
#help-pop{position:fixed;z-index:120;width:240px;background:var(--paper);border:1px solid var(--ev-input);padding:10px 12px;font:500 12px var(--sans);color:var(--ink);line-height:1.45;box-shadow:0 8px 26px -10px rgba(0,0,0,.35);pointer-events:none;opacity:0;transform:translateY(4px);transition:opacity .14s,transform .14s}
#help-pop.show{opacity:1;transform:translateY(0)}

.model-loading{position:fixed;inset:0;background:color-mix(in srgb,var(--paper) 92%,transparent);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:100;gap:var(--s4)}
.spinner{width:40px;height:40px;border:3px solid var(--rule);border-top-color:var(--ev-modeled);border-radius:50%;animation:bills-spin 1s infinite linear}
@keyframes bills-spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}
.load-title{margin:0;font:650 16px var(--serif);color:var(--ink)}
.load-sub{margin:0;font:500 12px var(--mono);color:var(--ink-soft)}
.load-error{color:var(--ev-input)}
.loader-retry{display:none;min-height:44px;padding:8px 18px;border:none;background:var(--ev-input);color:var(--paper);font:700 12px var(--mono);letter-spacing:.02em;cursor:pointer}
.loader-retry:hover{background:color-mix(in srgb,var(--ev-input) 85%,#000)}

@media (prefers-reduced-motion: reduce) {
  .spinner{animation:none;border-top-color:var(--ev-modeled)}
}

@media (max-width: 900px) {
  .layout-grid:not(.mobile-detail-open)>.panel:nth-child(2){display:none!important}
  .layout-grid.mobile-detail-open>.panel:first-child{display:none}
  .layout-grid{grid-template-columns:1fr;gap:var(--s4)}
  .layout-grid .panel:first-child{max-height:none;overflow-y:visible}
  .layout-grid .panel:last-child{position:static;top:auto}
  .mobile-bills-back{display:inline-flex;align-items:center;align-self:flex-start;min-height:44px;padding:8px 12px;border:1px solid var(--rule);background:var(--paper-deep);color:var(--ev-modeled);font:700 13px var(--sans);cursor:pointer}
}

@media (max-width: 600px) {
  .interventions-grid{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .int-btn{min-height:40px;white-space:normal}
  .int-btn-cell .int-btn{min-width:0}
  .table-wrapper{overflow-x:auto;-webkit-overflow-scrolling:touch}
  .bills-page table{font-size:12px;white-space:nowrap}
  .demo-grid{grid-template-columns:1fr}
  .pass-dashboard{grid-template-columns:1fr}
  .metric-box.featured{order:-1}
}

/* ============================================================
   ASK — the question desk (Phase 7 recompose). Retired blocks: the
   hand-authored ask.html's whole inline <style> (dark ink/panel theme,
   numbered compose-step circles, teal "run" button, coral/teal split-bar
   coloring) and its placeholder site-header--reserve-simple header/empty
   footer, both now server-rendered like every other migrated page. Class
   names below are load-bearing: js/ask.mjs generates result markup with
   these exact strings (rcard, result-hero, coal, quote, run-record…) — see
   that file's own comments for the Support/Oppose color seam. Canonical
   token names used directly, matching the Phase 4+ recompose convention
   (no legacy --teal/--gold/--line aliases in new rules below).
   ============================================================ */
.ask-top{padding-block:var(--s7) var(--s4);border-bottom:1px solid var(--rule)}
.ask-top h1{margin:0;font:650 var(--page-title)/1.06 var(--serif);letter-spacing:-.02em}
.ask-dek{margin:var(--s3) 0 0;max-width:var(--measure);color:var(--ink-soft);font:500 16px/1.5 var(--sans)}
.ask-top .data-note{margin:var(--s2) 0 0;color:var(--ink-soft);font:500 13px/1.5 var(--sans)}
.ask-top .data-note a{color:var(--ink);font-weight:600}

.ask-desk{align-items:start}
.ask-desk .margin-note h2{margin-top:0}

.console{border:1px solid var(--rule);padding:var(--s5)}
.console-body{display:flex;flex-direction:column;gap:var(--s6)}
.console-left{display:flex;flex-direction:column;gap:var(--s3)}
.console .ev-block.input{margin:0}
.console .q-label{display:block;margin:var(--s3) 0 4px;font:700 12px var(--sans);text-transform:uppercase;letter-spacing:.05em;color:var(--ink)}
.console .field-note{margin:0 0 var(--s2);max-width:560px;color:var(--ink-soft);font:500 13px/1.5 var(--sans)}
.ask-page textarea{width:100%;min-height:96px;resize:vertical;padding:12px 14px;border:1px solid var(--rule-strong);background:var(--paper);color:var(--ink);font:500 16px/1.5 var(--sans)}
.ask-page textarea:focus-visible{outline:2px solid var(--ev-modeled);outline-offset:1px}
#q{min-height:104px}

.examples{margin-top:var(--s3)}
.examples>summary{min-height:44px;display:inline-flex;align-items:center;cursor:pointer;color:var(--ink);font:700 13px var(--sans);text-decoration:underline;text-underline-offset:3px}
.preset-head{margin:var(--s3) 0 var(--s2);color:var(--ink-soft);font:500 13px/1.5 var(--sans)}
.presets{display:flex;flex-wrap:wrap;gap:var(--s2);margin-bottom:var(--s2)}
.preset{min-height:44px;padding:0 var(--s4);border:1px solid var(--rule);background:transparent;color:var(--ink-soft);font:600 13px var(--sans);cursor:pointer}
.preset:hover:not(.on){border-color:var(--rule-strong);color:var(--ink)}
.preset.on{border-color:var(--ink);background:var(--ink);color:var(--paper);font-weight:700}
.preset:focus-visible{outline:2px solid var(--ev-modeled);outline-offset:2px}
.preset-why{position:relative;margin:0;padding-left:var(--s4);min-height:1.45em;max-width:560px;color:var(--ink);font:500 13px/1.5 var(--sans)}
.preset-why::before{content:"\21B3";position:absolute;left:0;top:0;color:var(--ink-soft)}

.refine{padding-top:var(--s5);border-top:1px solid var(--rule)}
.refine-lead{min-height:44px;display:grid;grid-template-columns:auto minmax(220px,1fr) auto;align-items:center;gap:var(--s2) var(--s3);cursor:pointer;list-style:none}
.refine-lead::-webkit-details-marker{display:none}
.refine[open] .refine-lead{margin-bottom:var(--s4)}
.refine-title{color:var(--ink);font:700 16px var(--sans)}
.refine-lead .preset-sub{justify-self:end;color:var(--ink-soft);font:500 13px/1.4 var(--sans)}
.refine>summary::after{content:"Change";grid-column:3;color:var(--ink);font:700 12px var(--sans);text-decoration:underline;text-underline-offset:3px}
.row{display:grid;grid-template-columns:minmax(220px,1fr) minmax(260px,1.4fr);gap:var(--s5);margin-top:0}
.field{display:flex;flex-direction:column;gap:6px}
.field-note{margin:4px 0 0;max-width:400px;color:var(--ink-soft);font:500 13px/1.5 var(--sans)}
label.l{font:700 12px var(--sans);text-transform:uppercase;letter-spacing:.05em;color:var(--ink)}
.field-label{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s3)}
.field-label>span:last-child{color:var(--ink-soft);font:500 13px var(--sans)}
#scope{width:100%;min-height:44px;padding:8px 10px;border:1px solid var(--rule-strong);background:var(--paper);color:var(--ink);font:600 13px var(--sans)}
#scope:focus-visible{outline:2px solid var(--ev-modeled);outline-offset:1px}
#scopeNotice{margin-top:6px;font:500 13px/1.5 var(--sans)}

.opts{display:flex;flex-direction:column;gap:6px}
.opt{display:flex;align-items:center;gap:6px;border:1px solid var(--rule);padding:4px}
.opt.off{border-color:var(--rule);opacity:.6}
.opt-dot{flex:none;width:44px;height:44px;min-width:44px;min-height:44px;border:1px solid var(--rule-strong);background:color-mix(in srgb,var(--ev-modeled) 10%,var(--paper));color:var(--ev-modeled);cursor:pointer;padding:0}
.opt-dot::after{content:"✓";font:900 15px var(--sans)}
.opt.off .opt-dot{background:transparent;color:var(--ink-soft)}
.opt.off .opt-dot::after{content:"+"}
.opt-dot:focus-visible{outline:2px solid var(--ev-modeled);outline-offset:2px}
.opt-text{min-height:44px;flex:1 1 auto;min-width:0;background:transparent;border:0;padding:2px 8px;color:var(--ink);font:600 13px var(--sans)}
.opt-text[readonly]{cursor:default}
.opt.off .opt-text{color:var(--ink-soft);text-decoration:line-through;text-decoration-color:var(--rule)}
.opt-text:focus-visible{outline:2px solid var(--ev-modeled);outline-offset:2px}
.opt-edit{flex:none;height:44px;padding:0 10px;border:0;background:transparent;color:var(--ink-soft);font:700 11px var(--sans);cursor:pointer}
.opt-edit:hover,.opt-edit:focus-visible{color:var(--ink)}
.opt-actions{margin-top:var(--s2);display:flex;gap:var(--s2)}
.notice-retry{min-height:44px;padding:4px 12px;border:1px solid var(--rule);background:transparent;color:var(--ink-soft);font:700 11px var(--mono);cursor:pointer}
.notice-retry:hover{color:var(--ink);border-color:var(--rule-strong)}

.backdrop{margin-top:var(--s4)}
.backdrop-summary{min-height:44px;display:inline-flex;align-items:center;gap:var(--s2);cursor:pointer;list-style:none;padding:6px 12px;border:1px solid var(--rule);color:var(--ink-soft);font:700 10px var(--mono);text-transform:uppercase;letter-spacing:.05em}
.backdrop-summary::-webkit-details-marker{display:none}
.backdrop-summary:hover,.backdrop[open] .backdrop-summary{border-color:var(--rule-strong);color:var(--ink)}
.backdrop-chev{color:var(--ink-soft);font-size:10px;transition:transform .15s}
.backdrop[open] .backdrop-chev{transform:rotate(90deg)}
.backdrop-opt{text-transform:none;letter-spacing:0;font-weight:500}
.backdrop-why{margin:var(--s2) 0 0;max-width:560px;color:var(--ink-soft);font:500 13px/1.5 var(--sans)}
.backdrop textarea{margin-top:var(--s2);min-height:56px}

.console-foot{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--s4) var(--s5);margin-top:var(--s5);padding-top:var(--s5);border-top:1px solid var(--rule)}
.console-meta{flex:1 1 300px;min-width:0;display:flex;flex-direction:column;gap:6px}
.run-note{margin:0;max-width:560px;color:var(--ink-soft);font:500 13px/1.5 var(--sans)}
.quota{display:inline-flex;align-self:flex-start;align-items:center;gap:7px;padding:4px 10px;border:1px solid var(--rule);font:600 11px var(--mono);color:var(--ink-soft)}
.quota:empty{display:none}
.quota::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
.capacity-note{margin:0;max-width:560px;color:var(--ink-soft);font:500 14px/1.5 var(--sans)}
.run-status{margin:0;color:var(--ink);font:700 13px/1.45 var(--sans)}
.run-status::before{content:"";display:inline-block;width:9px;height:9px;margin-right:8px;border:2px solid var(--rule);border-top-color:var(--ev-modeled);border-radius:50%;animation:bills-spin .8s linear infinite}
.run{flex:none;min-width:230px;min-height:46px;padding:12px 22px;border:1px solid var(--ink);background:var(--ink);color:var(--paper);font:700 14px var(--sans);cursor:pointer}
.run:hover{background:#3a3328}
.run:focus-visible{outline:2px solid var(--ev-modeled);outline-offset:2px}
.run:disabled{opacity:.5;cursor:default}
.run.is-cancel{background:transparent;border-color:var(--ink);color:var(--ink)}
.run.is-cancel:hover{background:var(--paper-deep)}

@media (prefers-reduced-motion: reduce){
  .run-status::before{animation:none;border-top-color:var(--rule)}
}

@media (max-width: 700px){
  .ask-top{padding-block:var(--s5) var(--s4)}
  .row{grid-template-columns:1fr}
  .refine-lead{grid-template-columns:minmax(0,1fr) auto;row-gap:4px}
  .refine-lead .preset-sub{grid-column:1/-1;justify-self:start}
  .refine>summary::after{grid-row:1;grid-column:2}
  .console-foot{flex-direction:column;align-items:stretch}
  .run{width:100%;order:-1}
}

/* result — rendered into #out by js/ask.mjs's render()/renderUnavailable() */
#out{margin-top:var(--s6)}
.rcard{border:1px solid var(--rule);padding:var(--s5);margin-top:var(--s4)}
.rcard>.prov{padding-left:var(--s3);margin-bottom:var(--s4)}
.result-hero{margin:0 0 var(--s5);padding:var(--s4) 0 var(--s4) var(--s4)}
.rhead{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:var(--s3) var(--s4)}
.rhead h2{margin:4px 0 0;font:650 var(--title-statement)/1.15 var(--serif);letter-spacing:-.01em;color:var(--ink)}
.rhead h2:focus-visible{outline:none;box-shadow:-3px 0 0 var(--ev-modeled);padding-left:9px}
.result-focus:focus-visible{outline:2px solid var(--ev-modeled);outline-offset:4px}
.result-mode{display:flex;align-items:center;gap:6px}
.mode{font:700 10px var(--mono);text-transform:uppercase;letter-spacing:.05em;padding:3px 8px;border:1px solid var(--rule-strong)}
.mode.live{color:var(--ev-modeled);border-color:var(--ev-modeled)}
.mode.partial{color:var(--ev-input);border-color:var(--ev-input)}
.lead-stat{margin:var(--s4) 0 0;font:650 22px var(--sans);color:var(--ink)}
.lead-stat strong{font:650 clamp(32px,4vw,44px)/1 var(--serif);letter-spacing:-.02em}
.sensitivity-note{margin:var(--s2) 0 0;max-width:640px;color:var(--ink-soft);font:500 14px/1.55 var(--sans)}
.distribution{margin-top:var(--s4)}
.nbar{height:26px;display:flex;border:1px solid var(--rule-strong)}
.nbar i{display:flex;align-items:center;justify-content:center;font:700 11px var(--mono);color:var(--paper);min-width:0;white-space:nowrap}
.nlegend{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s3);margin-top:var(--s2);font:600 11px var(--mono);color:var(--ink-soft)}
.sw{width:10px;height:10px;display:inline-block;margin-right:5px;vertical-align:middle;forced-color-adjust:none}
.nlegend-note{color:var(--ink-faint);font-style:italic}
.partial-warning{margin-top:var(--s4);padding:12px 14px;border-left:3px dashed var(--ev-input);background:color-mix(in srgb,var(--ev-input) 8%,var(--paper));color:var(--ink-soft);font-size:14px;line-height:1.55}
.partial-warning strong{color:var(--ink)}

/* .help-inline / .help-label — ask.mjs's own text-labeled "more info" links
   ("About mode", "What does this mean?", "How grouping works", "About
   examples"). These share the base .help class with bills/globe/world's
   icon-only "ⓘ" tooltip trigger, but were always meant to read as inline
   text, not squeeze multi-word labels into a 44px circle — override the
   shared circular treatment back to a plain underlined link, matching every
   other inline "read more" affordance on the page (.text-link). */
.help-inline,.help-label{all:unset;display:inline-flex;align-items:center;min-width:0;min-height:44px;width:auto;height:auto;margin-left:4px;border-radius:0;color:var(--ink);font:700 13px var(--sans);text-decoration:underline;text-underline-offset:2px;cursor:pointer;vertical-align:middle}
.help-inline:hover,.help-inline:focus-visible,.help-label:hover,.help-label:focus-visible{color:var(--ev-input);background:none;outline:2px solid var(--ev-input);outline-offset:2px}

.result-section{padding:var(--s5) 0;border-bottom:1px solid var(--rule)}
.result-section-head{display:grid;grid-template-columns:30px minmax(0,1fr) auto;gap:2px var(--s3);align-items:start;margin-bottom:var(--s4)}
.result-section-head>span{grid-row:1/3;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid var(--rule);color:var(--ink-soft);font:700 10px var(--mono)}
.result-section-head h3{margin:0;font:650 18px/1.2 var(--sans);color:var(--ink)}
.result-section-head p{margin:4px 0 0;color:var(--ink-soft);font:500 13px/1.5 var(--sans)}

.coal-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--s3)}
.coal{border:1px solid var(--rule);padding:12px}
.coal h4{margin:0 0 6px;font:700 11px var(--mono);text-transform:uppercase;letter-spacing:.05em;color:var(--ink-soft)}
.coal table{width:100%;border-collapse:collapse;font:500 13px var(--sans)}
.coal td{padding:4px 0;border-bottom:1px solid var(--rule);color:var(--ink-soft)}
.coal tr:last-child td{border-bottom:0}
.coal td:last-child{text-align:right;font-weight:650;color:var(--ink);font-variant-numeric:tabular-nums}

.quotes{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:var(--s3)}
.quote{margin:0;padding:var(--s4)}
.quote .said{margin:var(--s2) 0;color:var(--ink);font:500 15px/1.5 var(--serif)}
.quote .pick{margin:0;font:700 11px var(--mono);text-transform:uppercase;letter-spacing:.03em}
.quote .conf{margin-left:7px;color:var(--ink-soft);font:600 10px var(--mono);text-transform:uppercase;letter-spacing:.04em}

.result-disclosure{margin-top:var(--s4);border:1px solid var(--rule)}
.result-disclosure>summary{min-height:52px;display:flex;align-items:center;justify-content:space-between;gap:var(--s3);padding:10px 14px;cursor:pointer;list-style:none;color:var(--ink);font:650 14px var(--sans)}
.result-disclosure>summary::-webkit-details-marker{display:none}
.result-disclosure>summary small{margin-left:auto;color:var(--ink-soft);font:500 12px var(--sans)}
.result-disclosure-body{padding:0 14px var(--s4)}
.spin-note{margin:0 0 var(--s3);color:var(--ink-soft);font:500 13px/1.5 var(--sans)}
.media{display:grid;gap:var(--s2)}
.spin-item{border:1px solid var(--rule);border-left-width:3px;padding:12px 14px}
.outlet{font:700 10px var(--mono);text-transform:uppercase;letter-spacing:.05em}
.headline{margin:6px 0;font:700 16px var(--sans);color:var(--ink);letter-spacing:-.01em}
.technique{color:var(--ink-soft);font:500 12.5px/1.45 var(--sans)}
.spin-method{margin:var(--s3) 0 0;color:var(--ink-faint);font:500 12px/1.5 var(--sans)}

.run-record{margin-top:var(--s4);padding:var(--s4);border:1px solid var(--rule)}
.run-record h3{margin:0 0 var(--s2);font:700 11px var(--mono);text-transform:uppercase;letter-spacing:.05em;color:var(--ink-soft)}
.run-record .prov{padding-left:var(--s3)}

.calc{margin-top:var(--s4);border-top:1px solid var(--rule);padding-top:var(--s3)}
.calc-summary{display:inline-flex;align-items:center;min-height:44px;gap:var(--s2);cursor:pointer;list-style:none;padding:8px 12px;border:1px solid var(--rule);font:700 11px var(--mono);letter-spacing:.03em;color:var(--ink-soft)}
.calc-summary::-webkit-details-marker{display:none}
.calc-summary:hover,.calc[open] .calc-summary{border-color:var(--rule-strong);color:var(--ink)}
.calc-summary:focus-visible{outline:2px solid var(--ev-modeled);outline-offset:2px}
.calc-chev{color:var(--ink-soft);font-size:10px;transition:transform .15s}
.calc[open] .calc-chev{transform:rotate(90deg)}
.calc-body{margin-top:var(--s3);border:1px solid var(--rule);padding:var(--s4);max-width:760px}
.calc-lead{margin:0 0 var(--s3);color:var(--ink-soft);font:500 13px/1.5 var(--sans)}
.calc-list{margin:0}
.calc-list dt{margin:0 0 5px;font:700 13.5px var(--sans);color:var(--ink)}
.calc-list dd{margin:0 0 var(--s4);padding-left:var(--s3);border-left:1px solid var(--rule);color:var(--ink-soft);font:500 13px/1.6 var(--sans)}
.calc-list dd:last-of-type{margin-bottom:0}
.calc-list dd b{color:var(--ink);font-weight:700}
.calc-list dd a,.calc-foot a{color:var(--ink);text-decoration:underline;text-underline-offset:2px}
.calc-foot{margin:var(--s3) 0 0;padding-top:var(--s3);border-top:1px solid var(--rule);color:var(--ink-faint);font:500 12px var(--mono)}

.tools{display:flex;gap:var(--s2);margin-top:var(--s4)}
.tools button{min-height:44px;padding:8px 14px;border:1px solid var(--rule);background:transparent;color:var(--ink-soft);font:700 12px var(--mono);cursor:pointer}
.tools button:hover{border-color:var(--rule-strong);color:var(--ink)}
.link-u{color:var(--ink);font-weight:700;text-decoration:underline;text-underline-offset:3px}
.button-link{min-height:44px;display:inline-flex;align-items:center;padding:0 14px;border:1px solid var(--rule-strong);color:var(--ink);font:700 12px var(--mono);text-decoration:none}

.ask-eyebrow{margin:0 0 var(--s2);color:var(--ev-input);font:750 11px var(--mono);letter-spacing:.08em;text-transform:uppercase}
.unavailable-card>p:not(.ask-eyebrow){max-width:68ch}
.unavailable-boundary{margin-top:var(--s3);padding:12px 14px;border-left:3px dashed var(--ev-input);background:color-mix(in srgb,var(--ev-input) 8%,var(--paper));color:var(--ink-soft);font-size:14px;line-height:1.55}
.unavailable-boundary strong{color:var(--ink)}
.err{color:var(--ev-input);font:600 13px var(--mono)}

@media (max-width: 700px){
  .rcard{padding:var(--s4)}
  .result-hero{padding-left:var(--s3)}
  .quotes,.coal-grid{grid-template-columns:1fr}
}

/* ============================================================
   THE LAB — /us/accuracy and /us/tracker (Phase 8a recompose). Both pages
   still delegate their whole dynamic body (KPI strip, cards, legends,
   tables, tracking charts, the KPI/help-bubble modal) to
   pheme-backend/public/us/dashboard-shared.mjs — the SAME runtime that
   still serves the not-yet-migrated /us/explore and /us/forecast on
   pheme.css + us/dashboard.css. Rather than fork that ~2,200-line module
   (or its class names) to un-style it for paper, this block gives the
   SAME class strings a fresh paper-register definition here — invisible to
   explore/forecast since neither of those pages loads signal-room.css.
   Class names below are load-bearing: dashboard-shared.mjs generates
   markup with these exact strings (card, chapter, strip, stat, legend,
   evbar, limits-callout, dashboard-reserved…) — renaming any would
   silently unstyle dynamically-rendered content without breaking a test.
   ============================================================ */

/* .lab-legacy scopes the handful of custom-property names
   dashboard-shared.mjs's generated markup still references by their old
   dark-instrument meaning (--dim/--dim2/--fog, read inline via style="" in
   scRow()/lineChart()) onto the paper equivalents already established
   above (--muted et al.) — scoped to <main>, not :root, so it can never
   leak onto another page that happens to load signal-room.css. */
.lab-legacy{--dim:var(--ink-soft);--dim2:var(--ink-faint);--fog:var(--ink)}

.lab-nav{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2) var(--s4);padding-block:var(--s4);border-bottom:1px solid var(--rule)}
.lab-nav a{min-height:44px;display:inline-flex;align-items:center;color:var(--ink);font:600 13px var(--sans);text-decoration:none}
.lab-nav a:hover{text-decoration:underline;text-underline-offset:3px}
.lab-nav-sep{color:var(--rule-strong)}

/* Definitions-first (accuracy only): Observed / Modeled / Miss, read before
   any number on the page. minmax(min(220px,100%),1fr) — not a bare
   minmax(220px,1fr) — for the same reason us/dashboard.css's own .grid
   uses it: below ~440px a bare 220px floor forces the single remaining
   column to exactly that floor width even when the container itself is
   narrower, guaranteeing horizontal overflow at 320-390px. min() caps the
   floor at whatever space is actually available. */
.defs{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(220px,100%),1fr));gap:var(--s4);margin-top:var(--s3)}
/* min() alone caps the track's SPECIFIED floor, but a grid item's default
   min-width:auto still asks for its own content-based minimum on top of
   that — .ev-block's .prov + .def-copy text was wide enough to win, still
   forcing 323px on a 288px container. min-width:0 here is the second half
   of the fix: it tells the item to accept the track's resolved size
   instead of imposing its own. */
.defs>.ev-block{min-width:0}
.def-copy{margin:6px 0 0;color:var(--ink-soft);font:500 13.5px/1.5 var(--sans)}

/* KPI strip — five summary tiles under the hero dek. */
#kpi-strip:empty{min-height:96px}
#kpi-strip .strip{margin-top:var(--s5)}
.strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(150px,100%),1fr));gap:var(--s3)}
.stat{position:relative;padding:13px 14px;border:1px solid var(--rule-strong);background:var(--paper-deep)}
.stat .v{font:800 24px var(--sans);color:var(--ink);line-height:1.1}
.stat .l{margin-top:5px;color:var(--ink-soft);font:600 12px var(--mono);line-height:1.35}
.stat--link{cursor:pointer;transition:transform .2s ease,border-color .2s ease}
.stat--link:hover{transform:translateY(-2px);border-color:var(--ev-modeled)}

/* Chapter rule — live.html's static "01 Tracking over time" head. */
.chapter{display:flex;align-items:baseline;gap:13px;margin:var(--s7) 0 var(--s4);padding-top:var(--s5);border-top:1px solid var(--rule-strong)}
.chapter .cn{color:var(--ink-soft);font:800 13px var(--mono)}
.chapter h2{margin:0;color:var(--ink);font:650 var(--title-section) var(--serif);letter-spacing:-.01em}
.chapter .ck{margin-left:auto;max-width:360px;text-align:right;color:var(--ink-faint);font:500 13px/1.45 var(--sans)}

/* Cards — the tracking charts (live) and KPI-style stat rows
   (loadScorecard's forecastAccuracyHtml stat() helper, shared markup). */
.card{border:1px solid var(--rule);padding:var(--s4);background:var(--paper)}
.card h3{margin:0 0 6px;font:650 16px var(--serif);color:var(--ink)}
.card .sub,.card>.sub{color:var(--ink-soft);font:500 13px/1.5 var(--sans)}
.card--click{cursor:pointer}
.card--click:hover,.card--click:focus-visible{border-color:var(--ev-modeled)}
.card-more{margin-left:auto;color:var(--ev-modeled);font:700 12px var(--mono)}
.card--click h3{display:flex;align-items:center;gap:var(--s2)}

.sub{color:var(--ink-soft);font:500 13px/1.5 var(--sans)}
.muted{color:var(--ink-soft)}
.trust-copy{margin-top:var(--s3);color:var(--ink-soft);font-size:13.5px;line-height:1.55}
.trust-copy code{font-family:var(--mono);font-size:12.5px}

.legend{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s3);margin-top:var(--s3);font:600 12px var(--mono);color:var(--ink-soft)}

.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(280px,100%),1fr));gap:var(--s4)}

/* 2024 electoral baseline bar (accuracy only) — accent colors come from
   PARTISAN.dem/PARTISAN.rep inline (site tokens --dem/--rep); the bar text
   only needs to clear contrast against those two specific fills, which are
   themselves tuned as TEXT-on-paper colors, so pure white/paper text over
   them (rather than the old dark-on-dark #0b0f17) is the fix.
   Scoped to .lab-legacy (us/accuracy.html, us/tracker.html — the two pages that
   still render dashboard-shared.mjs's evbar/sbar markup outside the
   .us-dashboard-scoped rules below at "us-dashboard .evbar/.mid/.midlab").
   Was bare .evbar/.mid/.midlab until CP9: an unscoped .mid class selector
   collided with ANY unrelated element using class="mid" as a semantic tier
   name elsewhere on the site (e.g. Bills' .support-badge.mid), inheriting
   this rule's position:absolute;top:0;bottom:0 and stretching that element
   edge-to-edge on whatever page it appeared on. Scoping is the fix, not a
   change to the colliding component. */
.lab-legacy .evbar{position:relative;height:34px;display:flex;border:1px solid var(--rule-strong)}
.lab-legacy .evbar i{height:100%;display:flex;align-items:center;font:800 13px var(--sans);color:var(--paper);padding:0 10px;width:0;transition:width 1s cubic-bezier(.22,1,.36,1)}
.lab-legacy .midlab{position:absolute;left:50%;top:-18px;transform:translateX(-50%);color:var(--ink-soft);font:700 10px var(--mono)}
.lab-legacy .mid{position:absolute;left:50%;top:0;bottom:0;width:1px;background:var(--rule-strong)}
.bgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(96px,100%),1fr));gap:var(--s2);margin-top:var(--s3)}
.bg{padding:8px 10px;border:1px solid var(--rule)}
.bg .ab{font:700 12px var(--mono);color:var(--ink)}
.bg .m{display:block;margin-top:2px;font:700 12px var(--sans)}

/* "Where this model is weakest" — an ev-input (hypothetical-boundary)
   callout, not a footnote: dashed amber left rule, same recipe as
   .unavailable-boundary above. */
.limits-callout{margin-top:var(--s5);padding:12px 14px;border-left:3px dashed var(--ev-input);background:color-mix(in srgb,var(--ev-input) 8%,var(--paper))}
.limits-callout .eyebrow{margin:0 0 6px;color:var(--ev-input);font:700 12px var(--mono);text-transform:uppercase;letter-spacing:.05em}
.limits-callout ul{margin:0;padding-left:1.1em}
.limits-callout li{margin-top:6px;color:var(--ink-soft);font-size:14px;line-height:1.55}
.limits-callout li:first-child{margin-top:0}
.limits-callout b{color:var(--ink)}

/* live.html's "looking for current reporting?" handoff to /news. */
.dashboard-handoff{margin-top:var(--s5);padding:var(--s4);display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2) var(--s4);border:1px solid var(--rule)}
.dashboard-handoff b{color:var(--ink)}
.dashboard-handoff span{color:var(--ink-soft);font-size:13px}
.dashboard-handoff a{min-height:44px;margin-left:auto;display:inline-flex;align-items:center;color:var(--ev-modeled);font-weight:700;text-decoration:none}
.dashboard-handoff a:hover{text-decoration:underline}

/* Tracking's empty/error state + its "preview with sample data" / retry
   actions — reuses .hint (already shared) and a lab-scoped .btn (bills'
   own .btn is deliberately scoped .bills-page .btn for the same reason). */
.empty{padding:var(--s5) 0;color:var(--ink-soft)}
.empty p{margin:0;max-width:60ch;font-size:14px;line-height:1.55}
.lab-legacy .btn{min-height:44px;margin-top:var(--s3);padding:10px 16px;border:1px solid var(--ev-modeled);background:color-mix(in srgb,var(--ev-modeled) 10%,var(--paper));color:var(--ev-modeled);font:700 13px var(--sans);cursor:pointer}
.lab-legacy .btn:hover{background:color-mix(in srgb,var(--ev-modeled) 18%,var(--paper))}

/* First-paint CLS reservations — same footprint concept as
   us/dashboard.css's own .dashboard-reserved, re-measured for this
   composition's actual paper-register layout. */
.dashboard-reserved{min-height:540px;color:var(--ink-soft);font:600 12px var(--mono)}
.dashboard-reserved--accuracy{min-height:1600px}
.dashboard-reserved--tracker{min-height:640px}
/* A reservation is for the wait, not for the result. Every number above is
   measured against the state the content is in when it succeeds, and nothing
   released them afterwards — so #tracking's one-line load error rendered 56px
   of text into 640px here and into 1360px at phone widths, and #live on
   /us/explore drops to 1300px inside 1920px the moment a reader uses the "Show
   state rankings" control the page gives them. Stamped by settleTracking() and
   the #live mount in us/dashboard-shared.mjs once anything terminal has landed.
   Both selectors are spelled out because source order will not save this one:
   the ≤720px override, the --explore/--forecast modifiers and .us-dashboard's
   own base all come later in this file, so each arm has to out-specify its
   counterpart rather than sit after it. */
.dashboard-reserved[data-settled],.us-dashboard .dashboard-reserved[data-settled]{min-height:0}
.dashboard-skeleton{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;margin-bottom:14px}
.dashboard-skeleton i{display:block;min-height:180px;border:1px solid var(--rule);background:linear-gradient(110deg,var(--paper-deep) 30%,var(--paper) 45%,var(--paper-deep) 60%);background-size:240% 100%;animation:lab-pulse 1.6s ease-in-out infinite}
@keyframes lab-pulse{to{background-position:-140% 0}}
@media (prefers-reduced-motion: reduce){.dashboard-skeleton i{animation:none}}
@media (max-width: 720px){
  .dashboard-reserved{min-height:760px}
  .dashboard-reserved--accuracy{min-height:2600px}
  .dashboard-reserved--tracker{min-height:1360px}
  /* #kpi-strip:empty's base 96px above only reserves enough for the single
     row its six .strip cells fit into at wide viewports; below 720px the
     auto-fit grid drops to two columns (three rows, one taller for the
     wrapping "pop-weighted 2024 Dem lean" label), measured at ~315px real —
     the unreserved gap is what let strip()'s async populate (dashboard-
     shared.mjs) drop .lab-nav/.record-cols by the same amount
     (docs/PERFORMANCE_BUDGETS.md CLS budget failure on /us/accuracy). */
  #kpi-strip:empty{min-height:320px}
}

/* Chart chrome via CSS only: SVG presentation attributes (the inline
   fill/stroke lineChart() sets for gridlines and axis labels) sit at the
   bottom of the cascade, so a plain author rule here already wins without
   touching the shared runtime — the data lines/dots (party colors,
   category colors) keep their own inline color untouched. */
#live svg text,#tracking svg text{font-family:var(--mono);font-size:12px;fill:var(--ink-faint)}
#live svg line,#tracking svg line{stroke:var(--rule)}

/* KPI / help-bubble detail modal — shared by every dashboard-shared.mjs page.
   Phase 10 dedupe: this used to be a second, near-identical .kpi-backdrop/
   .kpi-modal block (ported from pheme.css's dark version for pages that no
   longer load that file), which the real Phase 8b rebuild further down this
   file (search "KPI DETAIL MODAL") already shadowed for every property they
   both set — because they're unscoped class selectors at equal specificity,
   the later block in source order always won the properties in common, while
   this block's now-deleted box-shadow/transform-entrance declarations kept
   leaking through on properties the later block never touched. One canonical
   block now — the Phase 8b version below, kept as-is, height/overflow fix
   included. */

@media (max-width: 700px){
  .chapter{margin-top:var(--s6);flex-wrap:wrap}
  .chapter .ck{margin-left:0;text-align:left;max-width:none}
  .lab-nav{padding-block:var(--s3)}
}

/* ============================================================
   US ATLAS TABLE VIEWS — /us/explore + /us/forecast (Phase 8b recompose).
   dashboard-shared.mjs's runtime (population model, swingometer, compare,
   rankings, sortable table) is untouched beyond the metric-registry rebase
   and the explore-mode headline fold — only this stylesheet and the two
   page shells changed. Retired: the dark ink/panel dashboard.css theme, the
   #kpi-strip stat-tile grid on /us/explore (headline numbers fold into the
   table header line instead, per the blueprint), and ui.mjs's runtime
   header/FAB/onboarding injection (server-rendered chrome now, same as
   ask/bills/news). Class names below are load-bearing: dashboard-shared.mjs,
   us-explore-hero.mjs, and us-forecast.mjs generate markup with these exact
   strings (chapter, card, seg, keys, evbar, swing-*, cmp-*, mrank, msub…);
   renaming any would silently unstyle dynamically-rendered content.
   The 3D cartogram's own floating instrument chrome (hero-seg/hero-tip/
   hero-legend) stays a self-contained dark "instrument" surface — it reads
   as a distinct panel over its own canvas, not the paper page — so it uses
   the night tokens rather than paper ones. No translucent-blur chrome
   (that CSS property's anti-slop ratchet baseline requires 0 across
   pheme-backend/site).
   ============================================================ */
.us-dashboard { padding-block: var(--s7) 0; }
.us-dashboard .eyebrow{font:700 11px var(--mono);color:var(--ink-soft);text-transform:uppercase;letter-spacing:.05em;display:flex;align-items:center;gap:8px;margin:0 0 var(--s3);flex-wrap:wrap}
.us-dashboard .eyebrow .k{color:var(--ink-faint);text-transform:none;letter-spacing:0;font-weight:500}
.us-dashboard .metric-cell{display:inline-flex;align-items:center;gap:3px}
.us-dashboard .metric-cell .help{margin-left:0}

.us-dashboard .dhero{padding-block:0 var(--s3)}
.us-dashboard .dhero h1{margin:0;max-width:820px;font:650 var(--page-title)/1.06 var(--serif);letter-spacing:-.02em;color:var(--ink)}
.us-dashboard .dhero h1 em{font-style:normal;color:var(--ink)}
.us-dashboard .dhero .sub{margin:var(--s3) 0 0;max-width:660px;color:var(--ink-soft);font:500 16px/1.55 var(--sans)}
.us-dashboard .dhero .sub b{color:var(--ink);font-weight:650}
.us-dashboard .contextual-ask{display:inline-flex;min-height:44px;align-items:center;margin-top:var(--s4);color:var(--ink);font:700 13px var(--sans);text-decoration:underline;text-underline-offset:3px}

/* the compact plate strip — current-selection echo + link back to the
   interactive atlas plate, and (explore only) the US<->World scope switch
   the retired .georow used to carry */
.us-plate-strip{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2) var(--s5);margin-top:var(--s4);padding:var(--s3) 0;border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);font:500 13.5px var(--sans);color:var(--ink-soft)}
.us-plate-strip b{color:var(--ink);font-weight:700}
/* A flex container's direct-child <a> is "blockified" per spec (computed
   display becomes block, not inline), which forfeits the WCAG target-size
   exception for links embedded in prose — give it real tap-target sizing
   instead of leaving it a ~16px-tall block link. */
.us-plate-strip a{display:inline-flex;align-items:center;min-height:44px;color:var(--ink);font-weight:700;text-decoration:underline;text-underline-offset:3px}
.us-plate-strip .scope-switch{margin-left:auto}

/* cross-page nav across the US dashboard family (Explore/2028/Accuracy/
   Model tracker/Simulate) — ported from pheme.css's dark .us-subnav (only
   ever rendered on /us/explore, by ui.mjs's retired runtime chrome
   injection); kept here as static server-rendered markup so the family nav
   survives the migration. */
.us-subnav{display:flex;flex-wrap:wrap;align-items:center;gap:4px;margin-top:var(--s3);padding-left:var(--s3);border-left:1px solid var(--rule)}
.us-subnav a{min-height:44px;display:flex;align-items:center;font:600 11px var(--mono);color:var(--ink-faint);text-decoration:none;padding:4px 8px}
.us-subnav a:hover{color:var(--ink)}
.us-subnav a[aria-current="page"]{color:var(--ink);background:var(--paper-deep);font-weight:700}

/* anchor sub-nav — wrapping in-page jump list, replaces any clipped tab row */
.us-subnav-anchor{display:flex;flex-wrap:wrap;gap:var(--s2) var(--s4);margin-top:var(--s4);padding-top:var(--s3);border-top:1px solid var(--rule)}
.us-subnav-anchor a{min-height:44px;display:inline-flex;align-items:center;color:var(--ink-soft);font:700 12px var(--mono);text-transform:uppercase;letter-spacing:.04em;text-decoration:none}
.us-subnav-anchor a:hover{color:var(--ink);text-decoration:underline;text-underline-offset:3px}

/* forecast-only KPI strip (headline numbers) — explore folds these into the
   table header line instead (no #kpi-strip element on that page at all) */
.us-dashboard #kpi-strip .strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:var(--s5)}
.us-dashboard #kpi-strip:empty{min-height:93px}
@media (min-width:481px) and (max-width:800px){.us-dashboard #kpi-strip:empty{min-height:211px}}
@media (max-width:480px){.us-dashboard #kpi-strip:empty{min-height:322px}}
.us-dashboard .stat{position:relative;border:1px solid var(--rule-strong);padding:13px 14px;background:var(--paper-deep)}
.us-dashboard .stat .v{font:650 24px var(--sans);letter-spacing:-.02em;color:var(--ink);line-height:1.1}
.us-dashboard .stat .l{font:600 11px var(--mono);color:var(--ink-faint);margin-top:5px;line-height:1.35}
.us-dashboard .stat--link{cursor:pointer}
.us-dashboard .stat--link:hover{border-color:var(--rule-strong)}

.us-dashboard .dashboard-reserved{min-height:540px;color:var(--ink-faint);font:600 12px var(--mono)}
.us-dashboard .dashboard-reserved--forecast{min-height:904px}
/* explore's #live mounts exploreLive() (dashboard-shared.mjs) — the state
   explorer table chapter + the synthetic-population/rankings chapter — with
   no async data dependency (same static engine.mjs model every load), so
   this is measured directly rather than estimated: real rendered height is
   ~1908.75px at 1280 wide and ~1924.625px at 390 wide (Playwright
   getBoundingClientRect on #live, cold load, networkidle). Reserved slightly
   above both so the placeholder never undershoots the real content and
   pushes the 3D-cartogram chapter/section below it down after paint — the
   exact regression this rule exists to close (docs/PERFORMANCE_BUDGETS.md
   CLS budget; #ch-map/#hero3d-sec's SECTION.signal-shell and DIV.chapter
   rects collapsing to 0 as #live grew from empty to ~1.9-2.6k px tall). */
.us-dashboard .dashboard-reserved--explore{min-height:1920px}
.us-dashboard .dashboard-skeleton{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;margin-bottom:var(--s4)}
.us-dashboard .dashboard-skeleton i{display:block;min-height:180px;border:1px solid var(--rule);background:linear-gradient(110deg,var(--paper) 30%,var(--paper-deep) 45%,var(--paper) 60%);background-size:240% 100%;animation:us-dashboard-pulse 1.6s ease-in-out infinite}
@keyframes us-dashboard-pulse{to{background-position:-140% 0}}
@media (max-width:720px){
  .us-dashboard .dashboard-reserved{min-height:760px}
  .us-dashboard .dashboard-reserved--forecast{min-height:1800px}
  .us-dashboard .dashboard-reserved--explore{min-height:1940px}
  .us-dashboard .dashboard-skeleton{grid-template-columns:1fr}
  .us-dashboard .dashboard-skeleton i{min-height:150px}
}
@media (prefers-reduced-motion:reduce){.us-dashboard .dashboard-skeleton i{animation:none}}

.us-dashboard .chapter{display:flex;align-items:baseline;gap:13px;margin:var(--s7) 0 var(--s5);padding-top:var(--s6);border-top:1px solid var(--rule-strong);scroll-margin-top:64px}
.us-dashboard .chapter .cn{font:700 13px var(--mono);color:var(--ink-faint)}
.us-dashboard .chapter h2{margin:0;font:650 var(--title-section) var(--serif);letter-spacing:-.01em;color:var(--ink)}
.us-dashboard .chapter .ck{margin-left:auto;max-width:360px;text-align:right;color:var(--ink-soft);font:500 13px/1.45 var(--sans)}
@media (max-width:720px){.us-dashboard .chapter .ck{max-width:100%;text-align:left;margin:4px 0 0}}

.us-dashboard .dashboard-progressive{margin-top:var(--s4)}
.us-dashboard .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(300px,100%),1fr));gap:var(--s4)}
.us-dashboard .card{border:1px solid var(--rule);padding:var(--s4)}
.us-dashboard .card h3{margin:0 0 2px;font:700 14px var(--sans);color:var(--ink);line-height:1.3}
.us-dashboard .card .sub{font:500 11px var(--mono);color:var(--ink-faint);margin-bottom:var(--s3)}
.us-dashboard .card--click{cursor:pointer}
.us-dashboard .card--click:hover,.us-dashboard .card--click:focus-visible{border-color:var(--rule-strong);outline:none}
.us-dashboard .card--click .card-more{float:right;font:700 10px var(--mono);color:var(--ink-faint);letter-spacing:.04em}
.us-dashboard .card--click:hover .card-more,.us-dashboard .card--click:focus-visible .card-more{color:var(--ink)}
.us-dashboard .limits-callout{border-left:3px solid var(--ev-input);background:var(--paper-deep);padding:12px 14px;margin-top:var(--s4)}
.us-dashboard .limits-callout ul{margin:0;padding-left:18px;line-height:1.6;font:500 13px var(--sans);color:var(--ink)}
.us-dashboard .limits-callout li+li{margin-top:6px}

.us-dashboard .compbar{margin-bottom:var(--s4)}
.us-dashboard .compbar .top{display:flex;justify-content:space-between;font:600 12px var(--sans);margin-bottom:5px}
.us-dashboard .compbar .top b{color:var(--ink)}
.us-dashboard .compbar .top span{color:var(--ink-faint);font:500 11px var(--mono)}
.us-dashboard .seg{display:flex;height:18px;overflow:hidden;background:var(--paper-deep)}
.us-dashboard .seg i{display:block;height:100%;width:0;transition:width .9s cubic-bezier(.22,1,.36,1)}
.us-dashboard .keys{display:flex;flex-wrap:wrap;gap:8px 12px;margin-top:6px}
.us-dashboard .keys span{display:inline-flex;align-items:center;gap:5px;font:500 11px var(--mono);color:var(--ink-soft)}
.us-dashboard .sw{width:9px;height:9px;display:inline-block;flex:none}

.us-dashboard .evbar,.us-dashboard .sbar{position:relative;height:36px;overflow:hidden;display:flex;background:var(--paper-deep);margin:20px 0 4px}
/* Paper (light) text on the dem/rep fills. dashboard-shared.mjs's PARTISAN
   constant resolves to var(--dem)/var(--rep) for this migrated mode — the
   tokens.css pair verified 5.61:1/5.70:1 against paper as TEXT, which by
   contrast symmetry is also 5.61:1/5.70:1 for paper text ON these fills.
   The original dark-theme #4b8fe8/#e35b5b (still used unchanged on the
   unmigrated accuracy/live pages via dashboard.css, not this rule) measured
   only 2.91:1/3.15:1 here — the whole reason this pair is conditional. */
.us-dashboard .evbar i,.us-dashboard .sbar i{height:100%;display:flex;align-items:center;font:700 13px var(--sans);color:var(--paper);padding:0 10px;width:0;transition:width 1s cubic-bezier(.22,1,.36,1);forced-color-adjust:none}
.us-dashboard .evbar .mid,.us-dashboard .sbar .mid{position:absolute;left:50%;top:-4px;bottom:-4px;width:2px;background:var(--ink);transform:translateX(-50%);z-index:2}
.us-dashboard .evbar .midlab,.us-dashboard .sbar .midlab{position:absolute;left:50%;top:-18px;transform:translateX(-50%);font:700 10px var(--mono);color:var(--ink-soft);z-index:2}

.us-dashboard .bgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(116px,1fr));gap:6px;margin-top:var(--s4)}
.us-dashboard .bg{background:var(--paper-deep);border:1px solid var(--rule);padding:7px 9px;font:600 12px var(--sans)}
.us-dashboard .bg .ab{color:var(--ink);font-weight:700}
.us-dashboard .bg .m{font:600 11px var(--mono)}

.us-dashboard .rank{display:flex;justify-content:space-between;padding:4px 0;border-bottom:1px solid var(--rule);font:600 12px var(--mono)}
.us-dashboard .rank:last-child{border-bottom:none}
.us-dashboard .rank .n{color:var(--ink-soft);font-family:var(--sans);font-weight:500}
.us-dashboard .rank b{color:var(--ink)}

.us-dashboard input.search{flex:1;min-width:160px;min-height:44px;background:var(--paper);color:var(--ink);border:1px solid var(--rule-strong);padding:9px 12px;font:600 13px var(--sans);outline:none}
.us-dashboard input.search:focus-visible{outline:2px solid var(--ev-modeled);outline-offset:1px}
.us-dashboard .btn{min-height:44px;padding:8px 14px;border:1px solid var(--rule-strong);background:transparent;color:var(--ink-soft);font:700 12px var(--mono);cursor:pointer}
.us-dashboard .btn:hover{border-color:var(--ink);color:var(--ink)}
.us-dashboard .btn.ghost{background:transparent;color:var(--ink-soft)}
.us-dashboard .tblwrap{overflow-x:auto;border:1px solid var(--rule);max-height:520px}
.us-dashboard table{border-collapse:collapse;width:100%;font:600 12px var(--mono)}
.us-dashboard thead th{position:sticky;top:0;background:var(--paper-deep);color:var(--ink-soft);text-align:right;padding:9px 10px;font:700 11px var(--mono);white-space:nowrap;cursor:pointer;user-select:none;border-bottom:1px solid var(--rule-strong)}
.us-dashboard thead th:first-child,.us-dashboard tbody td:first-child{text-align:left}
.us-dashboard thead th:hover{color:var(--ink)}
.us-dashboard thead th .ar{color:var(--ink)}
.us-dashboard tbody td{padding:8px 10px;text-align:right;border-bottom:1px solid var(--rule);color:var(--ink-soft);white-space:nowrap}
.us-dashboard tbody tr{height:44px;cursor:pointer}
.us-dashboard tbody tr:nth-child(even) td{background:var(--paper-deep)}
.us-dashboard tbody tr:hover td{background:color-mix(in srgb,var(--ev-modeled) 10%,var(--paper))}
.us-dashboard tbody tr.hot td{background:color-mix(in srgb,var(--ev-input) 12%,var(--paper));box-shadow:inset 3px 0 0 var(--ev-input)}
.us-dashboard tbody tr.hot td:first-child{color:var(--ink);font-weight:700}
.us-dashboard tbody td:first-child{color:var(--ink);font-weight:700;font-family:var(--sans)}
/* Partisan lean column — real --dem/--rep text, not the generic accent
   colors the dark theme used here (was --orange/--teal; fixed in this
   migration, see the ledger note in the migration report). */
.us-dashboard .lean.d{color:var(--dem)}
.us-dashboard .lean.r{color:var(--rep)}
.us-dashboard .muted{color:var(--ink-faint)}
.us-dashboard svg{width:100%;height:auto;display:block;overflow:visible}
.us-dashboard .empty{border:1px solid var(--rule);padding:28px 20px;text-align:center}
.us-dashboard .empty p{color:var(--ink-soft);font:500 13px var(--sans);max-width:560px;margin:0 auto var(--s4);line-height:1.5}
.us-dashboard .legend{display:flex;flex-wrap:wrap;gap:10px;margin-top:var(--s3)}
.us-dashboard .legend span{display:inline-flex;align-items:center;gap:5px;font:600 11px var(--mono);color:var(--ink-soft)}
.us-dashboard .method{color:var(--ink-soft);font:500 13px/1.6 var(--sans);max-width:780px}
.us-dashboard .method b{color:var(--ink)}
.us-dashboard .method code{font:600 12px var(--mono);color:var(--ink)}

.us-dashboard .sd-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
.us-dashboard .sd-name{font:700 22px var(--serif);color:var(--ink);letter-spacing:-.01em}
.us-dashboard .sd-meta{font:600 12px var(--mono);color:var(--ink-soft);margin-top:3px}
.us-dashboard .sd-close{border:none;background:transparent;color:var(--ink-faint);font-size:18px;cursor:pointer;line-height:1}
.us-dashboard .sd-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(118px,1fr));gap:8px;margin:13px 0}
.us-dashboard .sd-stat{background:var(--paper-deep);border:1px solid var(--rule);padding:8px 10px}
.us-dashboard .sd-stat .v{font:700 17px var(--sans);color:var(--ink)}
.us-dashboard .sd-stat .l{font:600 10px var(--mono);color:var(--ink-faint);margin-top:3px;line-height:1.3}
.us-dashboard .sd-stat .l b{color:var(--ink)}
.us-dashboard .sd-stat .l b.lo{color:var(--ink-soft)}

.us-dashboard .swing-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px 18px;margin-bottom:4px}
.us-dashboard .swing-top{display:flex;justify-content:space-between;font:600 12px var(--sans);margin-bottom:4px}
.us-dashboard .swing-val{font:700 11px var(--mono);color:var(--ink-soft)}
.us-dashboard .swing-slider{width:100%;min-height:44px;accent-color:var(--ink);cursor:pointer}
.us-dashboard .swing-base-toggle{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:var(--s3)}
.us-dashboard .swing-base-toggle button{cursor:pointer;border:1px solid var(--rule-strong);background:transparent;color:var(--ink-soft);padding:6px 10px;font:700 11px var(--sans)}
.us-dashboard .swing-base-toggle button.on{background:var(--ink);color:var(--paper);border-color:var(--ink)}
.us-dashboard .swing-base-toggle button:disabled{cursor:default;opacity:.5}
.us-dashboard .swing-base-note{font:500 10px var(--mono);color:var(--ink-faint)}
/* Why "Live 2028" is disabled, in the one place a reader looking at that
   disabled button will see it. /us/forecast renders the swingometer without
   #tracking, so the load-error and Retry that /us/tracker shows in that section
   have nowhere to go on this page — this line is the control's own. Sized
   against the note above it rather than the 10px mono: it carries a real
   sentence and a 44px button, not a caption. */
.us-dashboard .swing-base-status{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s3);margin:var(--s3) 0 0;font-size:13px}

.us-dashboard .cmp-pick{display:flex;align-items:center;gap:10px;margin-bottom:var(--s4);flex-wrap:wrap}
.us-dashboard .cmp-pick select{flex:1;min-width:130px;background:var(--paper);color:var(--ink);border:1px solid var(--rule-strong);padding:9px 11px;font:600 13px var(--sans);outline:none;min-height:44px}
.us-dashboard .cmp-pick select:focus-visible{border-color:var(--ev-modeled)}
.us-dashboard .cmp-vs{font:700 11px var(--mono);color:var(--ink-faint)}
.us-dashboard .cmp-grid{display:grid;grid-template-columns:1.4fr 1fr 1fr;gap:10px;align-items:center;padding:7px 0;border-bottom:1px solid var(--rule);font:600 13px var(--mono)}
.us-dashboard .cmp-grid.cmp-head{font:700 14px var(--sans);color:var(--ink);border-bottom:1px solid var(--rule-strong)}
.us-dashboard .cmp-head .cmp-sub{display:block;font:500 10px var(--mono);color:var(--ink-faint);margin-top:2px}
.us-dashboard .cmp-lab{color:var(--ink-soft);font:600 12px var(--sans)}
.us-dashboard .cmp-win{color:var(--ink);font-weight:700}

.us-dashboard .scn-cols{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin:var(--s3) 0}
.us-dashboard .scn-h{font:700 13px var(--sans);color:var(--ink);line-height:1.3;min-height:34px}
.us-dashboard .scn-meta{font:600 10px var(--mono);color:var(--ink-faint);text-transform:capitalize;margin:3px 0 7px}
.us-dashboard .scn-stat{font:600 12px var(--mono);color:var(--ink-soft);margin-top:6px}

.us-dashboard .mrank-scroll{margin-top:2px}
.us-dashboard .mrank{display:grid;grid-template-columns:22px 1fr auto;align-items:center;gap:10px;padding:5px 0;border-bottom:1px solid var(--rule);font:600 12px var(--sans)}
.us-dashboard .mrank:last-child{border-bottom:none}
.us-dashboard .mrank .rk{font:700 11px var(--mono);color:var(--ink-faint);text-align:right}
.us-dashboard .mrank .nm{color:var(--ink);display:flex;align-items:center;gap:8px;min-width:0}
.us-dashboard .mrank .nm .track{flex:1;height:6px;background:var(--paper-deep);overflow:hidden;min-width:40px}
.us-dashboard .mrank .nm .track>i{display:block;height:100%;background:var(--ink)}
.us-dashboard .mrank .vv{font:700 12px var(--mono);color:var(--ink);white-space:nowrap}
.us-dashboard .mbreak .seg{margin:4px 0 8px}
.us-dashboard .mnote{font:500 12px var(--sans);color:var(--ink-soft);line-height:1.5}
.us-dashboard .msub{font:700 10px var(--mono);color:var(--ink-faint);text-transform:uppercase;letter-spacing:.05em;margin:4px 0 2px}

/* Richer popouts reuse the shared .kpi-modal shell (ui.mjs UI.showKPIModal) */
.us-dashboard .kpi-links a{color:var(--ink)}

/* 3D cartogram — a self-contained dark instrument surface (see file note) */
.us-dashboard #hero3d-sec{margin-top:var(--s6)}
.us-dashboard #hero3d{position:relative;height:min(58vh,520px);min-height:340px;border:1px solid var(--rule-strong);overflow:hidden;background:var(--night)}
.us-dashboard #hc{position:absolute;inset:0;width:100%;height:100%;display:block}
.us-dashboard .hero-seg{position:absolute;left:12px;top:12px;display:flex;flex-wrap:wrap;gap:5px;max-width:calc(100% - 130px);z-index:3}
.us-dashboard .hero-seg button{cursor:pointer;border:1px solid var(--night-rule);background:var(--night-panel);color:var(--bone-soft);padding:6px 10px;font:700 11px var(--sans)}
.us-dashboard .hero-seg button.on{background:var(--bone);color:var(--night);border-color:var(--bone)}
.us-dashboard .hero-seg .metric-cell{gap:1px}
.us-dashboard .hero-seg .metric-cell .help{min-width:44px;min-height:44px;width:44px;height:44px;margin-left:-1px;border:none;background:transparent;color:var(--bone-soft);font-size:10px;opacity:.6}
.us-dashboard .hero-seg .metric-cell:hover .help,.us-dashboard .hero-seg .metric-cell:focus-within .help{opacity:1;color:var(--bone)}
.us-dashboard .hero-modebtn{min-height:44px;position:absolute;right:12px;top:12px;z-index:3;cursor:pointer;border:1px solid var(--night-rule);background:var(--night-panel);color:var(--bone-soft);padding:6px 11px;font:700 11px var(--sans)}
.us-dashboard .hero-modebtn:hover,.us-dashboard .hero-modebtn:focus-visible{color:var(--bone);border-color:var(--bone-soft);outline:none}
.us-dashboard .hero-tip{position:absolute;z-index:4;pointer-events:none;transform:translate(-50%,-118%);background:var(--night-panel);border:1px solid var(--night-rule);padding:6px 10px;font:700 12px var(--sans);color:var(--bone);white-space:nowrap;display:none}
.us-dashboard .hero-tip .v{color:var(--bone);font-family:var(--mono)}
.us-dashboard .hero-legend{position:absolute;right:12px;bottom:12px;z-index:3;background:var(--night-panel);border:1px solid var(--night-rule);padding:8px 10px;font:600 11px var(--mono);color:var(--bone-soft);max-width:200px}
.us-dashboard .hero-legend .ramp{height:8px;margin:6px 0 4px}
.us-dashboard .hero-legend .ends{display:flex;justify-content:space-between;color:var(--bone-soft)}
.us-dashboard .hero-loading{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;z-index:5;background:var(--night);color:var(--bone-soft);font:600 13px var(--mono);text-align:center;padding:0 24px}
.us-dashboard .hero-hint{position:absolute;left:12px;bottom:12px;z-index:3;font:500 10px var(--mono);color:var(--bone-soft);pointer-events:none}
.us-dashboard .cartogram-gate{border:1px solid var(--rule);padding:var(--s5);text-align:center}
.us-dashboard .cartogram-gate p{margin:0 0 var(--s4);max-width:52ch;margin-inline:auto;color:var(--ink-soft);font:500 14px/1.55 var(--sans)}
@media (max-width:760px){.us-dashboard .hero-seg{max-width:calc(100% - 24px)}.us-dashboard .hero-legend{display:none}}

@media (prefers-reduced-motion:reduce){
  .us-dashboard .seg i,.us-dashboard .evbar i,.us-dashboard .sbar i{transition:none!important}
}
@media (max-width:720px){
  .us-dashboard{font-size:15px}
  .us-dashboard .dhero .sub,.us-dashboard .card .method,.us-dashboard .card p,.us-dashboard .empty p{font-size:15px;line-height:1.55}
  /* Authoritative phone type floor — ported from dashboard.css's own
     bottom-of-file rule (the same one this pins in tests/e2e/edge-cases.spec.mjs).
     Dense/support labels stay compact but readable; kept last in this block
     so a later addition here cannot silently undo it. */
  .us-dashboard .help,
  .us-dashboard .eyebrow,
  .us-dashboard .stat .l,
  .us-dashboard .card .sub:not(.trust-copy),
  .us-dashboard .compbar .top span,
  .us-dashboard .keys span,
  .us-dashboard .evbar .midlab,
  .us-dashboard .sbar .midlab,
  .us-dashboard .bg .m,
  .us-dashboard thead th,
  .us-dashboard .legend span,
  .us-dashboard .sd-stat .l,
  .us-dashboard .swing-val,
  .us-dashboard .swing-base-toggle button,
  .us-dashboard .swing-base-note,
  .us-dashboard .cmp-vs,
  .us-dashboard .cmp-head .cmp-sub,
  .us-dashboard .scn-meta,
  .us-dashboard .hero-seg button,
  .us-dashboard .hero-seg .metric-cell .help,
  .us-dashboard .hero-modebtn,
  .us-dashboard .hero-legend,
  .us-dashboard .hero-hint,
  .us-dashboard .card--click .card-more,
  .us-dashboard .mrank .rk,
  .us-dashboard .msub {
    font-size: 12px;
  }
  .us-dashboard svg text { font-size: 12px; }
  .us-dashboard .card .sub.trust-copy,
  .us-dashboard .muted.trust-copy,
  .us-dashboard .mnote {
    font-size: 13px;
    line-height: 1.55;
  }
}
@media (max-width:560px){
  .us-dashboard .scn-cols{grid-template-columns:1fr}
  .us-dashboard .swing-grid{grid-template-columns:1fr}
  .us-dashboard .sd-stats{grid-template-columns:1fr 1fr}
}
/* ============================================================
   WORLD EXPLORE — /world/explore (Phase 8b recompose, output stays
   world-dashboard.html). Retired: the page's own inline dark <style>
   block, the #secnav ARIA-tabs hide/show mechanism (replaced by a plain
   wrapping in-page anchor nav — every section stays rendered, none hidden,
   so a 320px reader never hits a clipped tab row), and ui.mjs's runtime
   header/FAB injection. New: the read-only atlas browse map panel mounted
   above rankings (see the atlas-* rules below, shared with /us/simulate
   and /world/simulate's browse mode). world-dashboard.mjs's rankings/
   regions/table/compare runtime is untouched beyond the metric-registry
   rebase. Class names below are load-bearing (world-dashboard.mjs
   generates markup with these exact strings).
   ============================================================ */
.world-explore-page{padding-block:var(--s7) 0}
.world-explore-page .eyebrow{font:700 11px var(--mono);color:var(--ink-soft);text-transform:uppercase;letter-spacing:.05em;display:flex;align-items:center;gap:8px;margin:0 0 var(--s3);flex-wrap:wrap}
.world-explore-page .eyebrow .k{color:var(--ink-faint);text-transform:none;letter-spacing:0;font-weight:500}
.world-explore-page h1{margin:18px 0 6px;font:650 var(--page-title)/1.06 var(--serif);letter-spacing:-.02em;color:var(--ink)}
.world-explore-page h1 em{font-style:normal;color:var(--ink)}
.world-explore-page .lede{margin:0 0 var(--s5);max-width:640px;color:var(--ink-soft);font:500 16px/1.5 var(--sans)}
.world-explore-page .contextual-ask{display:inline-flex;min-height:44px;align-items:center;margin-top:var(--s3);color:var(--ink);font:700 13px var(--sans);text-decoration:underline;text-underline-offset:3px}

/* wrapping in-page anchor nav (replaces the old #secnav ARIA-tabs) */
.world-explore-page #secnav{display:flex;flex-wrap:wrap;gap:var(--s2) var(--s4);margin-top:var(--s4);padding-top:var(--s3);border-top:1px solid var(--rule)}
.world-explore-page #secnav a{min-height:44px;display:inline-flex;align-items:center;color:var(--ink-soft);font:700 12px var(--mono);text-transform:uppercase;letter-spacing:.04em;text-decoration:none}
.world-explore-page #secnav a:hover,.world-explore-page #secnav a.on{color:var(--ink);text-decoration:underline;text-underline-offset:3px}

.world-explore-page .stats{display:grid;grid-template-columns:repeat(5,1fr);gap:14px;margin-top:var(--s5);min-height:112px}
.world-explore-page .stat{border:1px solid var(--rule-strong);padding:16px;cursor:pointer;background:var(--paper-deep)}
.world-explore-page .stat:hover{border-color:var(--rule-strong)}
.world-explore-page .stat b{display:block;font:700 26px var(--sans);color:var(--ink);letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.world-explore-page .stat span{font:600 11px var(--mono);color:var(--ink-faint)}

.world-explore-page section{padding:30px 0;border-top:1px solid var(--rule-strong)}
.world-explore-page section:first-of-type{border-top:none}

.world-explore-page .seg{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:var(--s4)}
.world-explore-page .seg button:not(.help){min-height:44px;cursor:pointer;border:1px solid var(--rule);background:transparent;color:var(--ink-soft);padding:7px 11px;font:700 12px var(--sans)}
.world-explore-page .seg button.on{background:var(--ink);color:var(--paper);border-color:var(--ink)}
.world-explore-page .seg .spacer{flex:1}
.world-explore-page .metric-cell{display:inline-flex;align-items:center;gap:3px}
.world-explore-page .metric-cell .help{margin-left:0}
.world-explore-page .toggle{min-height:44px;cursor:pointer;border:1px solid var(--rule-strong);background:transparent;color:var(--ink-soft);padding:7px 11px;font:700 12px var(--sans)}
.world-explore-page .toggle:hover{color:var(--ink)}

.world-explore-page .rank{display:flex;min-height:44px;align-items:center;gap:10px;padding:4px 0;cursor:pointer}
.world-explore-page .rank .nm{width:150px;flex:none;font:600 12px var(--sans);color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.world-explore-page .rank .track{flex:1;height:18px;background:var(--paper-deep);overflow:hidden}
.world-explore-page .rank .fill{height:100%;transition:width .5s cubic-bezier(.4,0,.2,1)}
.world-explore-page .rank .val{width:96px;flex:none;text-align:right;font:600 12px var(--mono);color:var(--ink-soft)}
.world-explore-page .rank:hover .nm{color:var(--ink)}
.world-explore-page .rank-note{font-size:11px;color:var(--ink-faint);padding:2px 2px 6px}

.world-explore-page .rrow{display:grid;grid-template-columns:160px 1fr 150px;align-items:center;gap:12px;padding:7px 0;border-bottom:1px solid var(--rule-strong);cursor:pointer}
.world-explore-page .rrow .rnm{font:700 12px var(--sans);color:var(--ink)}
.world-explore-page .rrow .rtrack{height:14px;background:var(--paper-deep);overflow:hidden}
.world-explore-page .rrow .rfill{height:100%}
.world-explore-page .rrow .rmeta{text-align:right;font:600 11px var(--mono);color:var(--ink-soft)}
.world-explore-page .rrow:hover .rnm{color:var(--ink)}

.world-explore-page .tbar{display:flex;gap:10px;align-items:center;margin-bottom:var(--s3);flex-wrap:wrap}
.world-explore-page #q{flex:1;min-width:180px;min-height:44px;background:var(--paper);border:1px solid var(--rule-strong);color:var(--ink);padding:9px 12px;font:500 13px var(--sans)}
.world-explore-page #q:focus-visible{outline:2px solid var(--ev-modeled);outline-offset:1px}
.world-explore-page .table-scroll-box{overflow-x:auto;overflow-y:auto;max-height:640px}
.world-explore-page table{width:100%;border-collapse:separate;border-spacing:0;font:500 12.5px var(--sans)}
.world-explore-page thead th{position:sticky;top:0;background:var(--paper);text-align:right;padding:8px 9px;color:var(--ink-soft);font:700 11px var(--mono);cursor:pointer;white-space:nowrap;border-bottom:1px solid var(--rule-strong)}
.world-explore-page thead th:first-child{text-align:left}
.world-explore-page thead th:hover{color:var(--ink)}
.world-explore-page thead th.sort::after{content:" ▾";color:var(--ink)}
.world-explore-page thead th.sort.asc::after{content:" ▴"}
.world-explore-page tbody td{text-align:right;padding:7px 9px;border-bottom:1px solid var(--rule);color:var(--ink-soft);font-variant-numeric:tabular-nums}
.world-explore-page tbody td:first-child{text-align:left;color:var(--ink);font-weight:600}
.world-explore-page tbody tr{cursor:pointer}
.world-explore-page tbody tr:nth-child(even) td{background:var(--paper-deep)}
.world-explore-page tbody tr:hover td{background:color-mix(in srgb,var(--ev-modeled) 10%,var(--paper))}
.world-explore-page #tbody tr.hot td{background:color-mix(in srgb,var(--ev-input) 12%,var(--paper));box-shadow:inset 3px 0 0 var(--ev-input)}
.world-explore-page .reg-dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:7px;vertical-align:middle}
.world-explore-page .table-more{margin-top:var(--s3)}

.world-explore-page .cmp-pick{display:flex;gap:10px;align-items:center;margin-bottom:var(--s4);flex-wrap:wrap}
.world-explore-page .cmp-pick select{flex:1;min-width:0;min-height:44px;background:var(--paper);border:1px solid var(--rule-strong);color:var(--ink);padding:9px 11px;font:600 13px var(--sans)}
.world-explore-page .cmp-vs{font:700 12px var(--mono);color:var(--ink-faint)}
.world-explore-page .cmp-grid{display:grid;grid-template-columns:1fr 90px 90px;gap:8px;padding:7px 0;border-bottom:1px solid var(--rule);font:600 12.5px var(--mono)}
.world-explore-page .cmp-grid.head{color:var(--ink-faint);font:700 10px var(--mono);text-transform:uppercase;letter-spacing:.05em}
.world-explore-page .cmp-grid .lab{font-family:var(--sans);color:var(--ink-soft);font-weight:500}
.world-explore-page .cmp-grid b{text-align:right}
.world-explore-page .cmp-grid .win{color:var(--ink)}

.world-explore-page .legend{display:flex;flex-wrap:wrap;gap:7px 13px;margin-top:var(--s3)}
.world-explore-page .legend span{display:inline-flex;align-items:center;gap:6px;font:600 10px var(--mono);color:var(--ink-soft)}
.world-explore-page .legend .sw{width:9px;height:9px;border-radius:50%}
.world-explore-page .note{font:500 13px/1.6 var(--sans);color:var(--ink-soft);max-width:680px}
.world-explore-page .note b{color:var(--ink)}

/* country / region detail modal */
.world-explore-page .modal-backdrop{position:fixed;inset:0;z-index:100;background:rgba(33,28,20,.5);display:flex;align-items:flex-start;justify-content:center;padding:44px 16px;overflow-y:auto}
.world-explore-page .modal-backdrop[hidden]{display:none}
.world-explore-page .modal{background:var(--paper);border:1px solid var(--rule-strong);width:100%;max-width:620px}
.world-explore-page .modal-hd{display:flex;align-items:flex-start;gap:14px;padding:22px 24px 18px;border-bottom:1px solid var(--rule)}
.world-explore-page .modal-flag{font-size:38px;line-height:1}
.world-explore-page .modal-hd .nm{margin:0;font:700 24px var(--serif);letter-spacing:-.01em;color:var(--ink)}
.world-explore-page .modal-hd .meta{margin-top:7px;display:flex;gap:8px;flex-wrap:wrap;align-items:center;font:600 12px var(--mono);color:var(--ink-soft)}
.world-explore-page .modal-reg{padding:2px 8px;border:1px solid currentColor;font:700 10px var(--mono);text-transform:uppercase;letter-spacing:.05em}
.world-explore-page .modal-close{margin-left:auto;min-width:32px;min-height:32px;background:transparent;border:1px solid var(--rule);color:var(--ink-soft);font-size:17px;line-height:1;cursor:pointer;flex-shrink:0}
.world-explore-page .modal-close:hover{color:var(--ink);border-color:var(--rule-strong)}
.world-explore-page .modal-bd{padding:16px 24px 24px;max-height:70vh;overflow-y:auto}
.world-explore-page .mind{padding:11px 0;border-bottom:1px solid var(--rule)}
.world-explore-page .mind:last-child{border-bottom:none}
.world-explore-page .mind .top{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
.world-explore-page .mind .ml{font:600 13px var(--sans);color:var(--ink-soft)}
.world-explore-page .mind .mv{font:700 15px var(--sans);color:var(--ink)}
.world-explore-page .mind .mbar{height:5px;background:var(--paper-deep);margin-top:8px;overflow:hidden}
.world-explore-page .mind .mbar i{display:block;height:100%}
.world-explore-page .mind .mctx{font:500 11px var(--mono);color:var(--ink-faint);margin-top:6px}
.world-explore-page .modal-rowlink{margin-top:2px}
.world-explore-page .kpi-links a{color:var(--ink)}

@media (max-width:1023px){
  .world-explore-page .rank .nm{width:104px}
  .world-explore-page .rrow{grid-template-columns:110px 1fr 96px}
  .world-explore-page .table-scroll-box{max-height:70vh}
  .world-explore-page .stats{grid-template-columns:repeat(2,1fr);min-height:340px}
}
@media (max-width:560px){
  .world-explore-page .rrow{grid-template-columns:1fr;gap:4px}
  .world-explore-page .cmp-grid{grid-template-columns:1fr}
  .world-explore-page .modal-close{width:40px;height:40px}
}
@media (prefers-reduced-motion:reduce){
  .world-explore-page .rank .fill{transition:none}
}

/* ============================================================
   ATLAS BROWSE MAP PANEL — read-only plate mounted above /world/explore's
   rankings (js/atlas/browse.mjs, same controller /us/simulate and
   /world/simulate use for their live browse mode). Ported from the
   sim-shell's paper-mode instrument CSS (public/simulation-next.css's
   --paper-* variables are the same palette as /tokens.css's --ink/--rule
   family, just an older naming generation); unscoped since every class is
   already atlas-*-prefixed and only this page loads it in the paper
   register today.
   ============================================================ */
.atlas-browse-host{position:relative}
.atlas-browse-error{font-size:14px;color:var(--ink-soft)}
.atlas-browse-error a{color:var(--ink)}
.atlas-grid{display:grid;grid-template-columns:minmax(0,7fr) minmax(280px,3fr);gap:28px;align-items:start}
.atlas-plate-section{min-width:0}
.atlas-plate-wrap{position:relative}
.atlas-plate-wrap svg{width:100%;height:auto;display:block}
.atlas-plate-svg .ap{stroke:var(--paper);stroke-width:.6;cursor:pointer}
.atlas-plate-svg .ap:hover{stroke:var(--ink);stroke-width:1.2}
.atlas-plate-svg .ap.selected{stroke:var(--ink);stroke-width:1.8}
.atlas-plate-svg .ap:focus-visible{outline:2px solid var(--ev-modeled);outline-offset:1px}
.atlas-kbd-note{font-size:12.5px;color:var(--ink-soft);margin:10px 0 0}
.atlas-kbd-note a{color:var(--ink)}
.atlas-rail-heading{font:600 11px var(--mono);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft);margin:0 0 8px}
.layer-list{display:flex;flex-direction:column;gap:6px}
.layer-list .chip{width:100%;justify-content:space-between;text-align:left}
.chip .src{font:400 10px var(--mono);color:var(--ink-faint);white-space:nowrap}
.layer-more{margin-top:2px}
.layer-more>.layer-list{margin-top:6px}
.layer-more>summary.chip::-webkit-details-marker{display:none}
.inspector{border:1px solid var(--rule);border-top:2px solid var(--rule-strong);padding:16px;font:400 14px var(--sans);color:var(--ink)}
.inspector:focus-visible{outline:2px solid var(--ev-modeled);outline-offset:2px}
.inspector h3{margin:8px 0 0;font:600 19px var(--serif)}
.inspector-empty{font-size:13.5px;color:var(--ink-soft);margin:0}
.inspector dl{display:grid;grid-template-columns:1fr auto;gap:4px 16px;margin:12px 0}
.inspector dt{color:var(--ink-soft);font-size:13px}
.inspector dd{margin:0;font-variant-numeric:tabular-nums;text-align:right;font-weight:600}
.inspector-actions{display:flex;gap:8px;flex-wrap:wrap}
.inspector .btn,.inspector-actions .btn{font:500 13px var(--sans);border:1px solid var(--ink);background:transparent;color:var(--ink);padding:8px 12px;min-height:44px;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center}
.inspector .prov{display:flex;gap:8px;align-items:baseline;font:500 11px var(--mono);letter-spacing:.05em;color:var(--ink-soft);border-left:3px solid var(--ev-observed);padding-left:10px;margin:0}
.inspector .prov b{color:var(--ink);text-transform:uppercase;font-weight:600}
/* Same specificity as .inspector .prov b (two classes + element), so without
   this override evidence.css's .prov.derived b never wins the cascade here
   despite loading first — see simulation-next.css's identical fix for why
   the instrument surface needs its own copy of this override too. */
.inspector .prov.derived b{color:var(--ev-modeled)}
@media (max-width:1023px){.atlas-grid{grid-template-columns:minmax(0,1fr)}}
@media (max-width:760px){
  .atlas-browse-host{padding-block:14px}
  .atlas-plate-wrap{max-height:60vh;overflow:hidden}
}

/* ============================================================
   KPI DETAIL MODAL (canonical) — the richer state-profile/full-ranking/
   demographic popouts dashboard-shared.mjs opens via ui.mjs's
   UI.showKPIModal(). Originally ported from pheme.css's dark version; Phase
   10 deleted the earlier near-duplicate block above (search "Phase 10
   dedupe") that this one had already been shadowing property-by-property.
   No translucent-blur chrome, box-shadow, transform entrance, or
   border-radius (the ratchet baseline requires 0 for a blurred-backdrop
   effect and gradients across pheme-backend/site; square corners match the
   rest of the paper register).
   ============================================================ */
.kpi-backdrop{position:fixed;inset:0;background:rgba(33,28,20,.55);z-index:1000;opacity:0;pointer-events:none;transition:opacity .2s ease;display:flex;align-items:center;justify-content:center;padding:16px}
.kpi-backdrop.show{opacity:1;pointer-events:auto}
/* Seam: a state profile's senators/bills-link content can run past a phone
   viewport's height (390x844 measured ~1296px tall); neither this rule nor
   its Phase 8a twin above ever capped the modal's height, so the overflow
   sat below the fold with no way to scroll to it. max-height + overflow-y
   matches .world-explore-page .modal-bd's already-working pattern. */
.kpi-modal{background:var(--paper);border:1px solid var(--rule-strong);padding:24px;width:90%;max-width:580px;max-height:calc(100vh - 32px);overflow-y:auto;color:var(--ink)}
.kpi-head{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:var(--s4);border-bottom:1px solid var(--rule);padding-bottom:var(--s3)}
.kpi-head h2{margin:0;font:650 var(--title-section) var(--serif);color:var(--ink)}
.kpi-head .close-btn{min-width:44px;min-height:44px;background:transparent;border:1px solid var(--rule);color:var(--ink-soft);font-size:20px;cursor:pointer;padding:0 4px;line-height:1}
.kpi-head .close-btn:hover{color:var(--ink);border-color:var(--rule-strong)}
.kpi-body{font-size:13px;line-height:1.6;color:var(--ink-soft);display:flex;flex-direction:column;gap:var(--s3)}
.kpi-body b{color:var(--ink)}
.kpi-body code{font-family:var(--mono);color:var(--ink);background:var(--paper-deep);padding:2px 5px}
.kpi-body .formula{background:var(--paper-deep);border:1px solid var(--rule);padding:10px 14px;font-family:var(--mono);color:var(--ink);text-align:center;margin:6px 0}
.kpi-links{margin-top:var(--s5);border-top:1px solid var(--rule);padding-top:var(--s4);display:flex;flex-direction:column;gap:8px}
.kpi-links a{font:700 12px var(--mono);color:var(--ink);text-decoration:none;display:inline-flex;align-items:center;gap:6px}
.kpi-links a:hover{text-decoration:underline;text-underline-offset:3px}
@media (prefers-reduced-motion:reduce){.kpi-backdrop{transition:none}}

/* ============================================================
   /cost — published price inputs as derived hours of work.
   The graphic here is the DATA, not decoration: a sparkline of the
   time price across every year, with the peak and the latest point
   marked. A two-point then/now comparison hides reversals (the median
   home's time price peaked in 2005 and is below that today), and
   hiding a reversal is how a page like this becomes an argument.
   ============================================================ */
.cost-top { padding-block: var(--s6) var(--s5); }
.cost-top h1 { margin: 0; max-width: 20ch; font: 650 var(--page-title)/1.06 var(--serif); letter-spacing: -.025em; }
.cost-dek { max-width: 62ch; margin: 20px 0 0; color: var(--ink-soft); font: 17px/1.55 var(--sans); }
.cost-lede { max-width: 68ch; margin: 12px 0 var(--s4); color: var(--ink-soft); font: 15px/1.6 var(--sans); }
.cost-note, .cost-caveat { max-width: 72ch; color: var(--ink-soft); font: 14px/1.6 var(--sans); }
.cost-chart-guide,
.cost-definitions { max-width: 78ch; color: var(--ink-soft); font: 14px/1.6 var(--sans); }
.cost-chart-guide { margin: -12px 0 var(--s4); }
.cost-definitions { margin: var(--s3) 0 0; }
.cost-chart-guide b,
.cost-definitions b { color: var(--ink); }
.cost-caveat { margin-top: var(--s4); padding: 14px 16px; border-left: 3px solid var(--rule); background: var(--paper-deep); }
.cost-sources { margin-top: var(--s3); font: 12px var(--mono); color: var(--ink-faint); }
.cost-check { color: var(--ink-soft); text-underline-offset: 3px; }

/* Item marks. Deliberately not one uniform icon set at one stroke weight —
   that grid-of-identical-glyphs look is the current cross-product cliché and
   this repo's own design rules flag it. These sit at kicker size beside the
   name: a mark, not an illustration. */
.cost-glyph { width: 22px; height: 22px; flex: none; fill: none; stroke: var(--ink); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; vertical-align: -3px; }
.cost-goods h3 .cost-glyph { width: 20px; height: 20px; }
.cost-home h2 .cost-glyph { width: 26px; height: 26px; stroke-width: 1.4; }

/* Sparkline: the timeline. Peak and latest are the two points worth marking. */
.cost-spark { display: block; width: 100%; height: auto; margin-top: var(--s3); overflow: visible; }
.cost-spark-line { fill: none; stroke: var(--ink); stroke-width: 1.4; stroke-linejoin: round; stroke-linecap: round; }
.cost-spark-peak { fill: var(--paper); stroke: var(--ink-faint); stroke-width: 1.2; }
.cost-spark-now { fill: var(--ev-modeled); stroke: none; }
.cost-spark-key { display: flex; justify-content: space-between; margin: 4px 0 0; color: var(--ink-faint); font: 10px var(--mono); letter-spacing: .06em; text-transform: uppercase; }
.cost-spark-legend { display: inline-flex; align-items: center; gap: 5px; color: var(--ink-soft); }
.cost-spark-legend i { display: inline-block; width: 7px; height: 7px; border-radius: 50%; }
.cost-spark-legend i.is-peak { border: 1.2px solid var(--ink-faint); background: var(--paper); }
.cost-spark-legend i.is-now { background: var(--ev-modeled); }

/* Housing */
.cost-home { padding-block: var(--s5); }
.cost-home h2, .cost-goods h2, .cost-method h2, .cost-cohort h2, .cost-split h2 { margin: var(--s2) 0 0; display: flex; align-items: center; gap: 10px; font: 650 var(--title-section) var(--serif); }
.cost-home-chart { margin-top: var(--s4); padding: 18px 20px; border: 1px solid var(--rule); background: var(--paper-deep); }
.cost-home-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0; margin-top: var(--s4); border: 1px solid var(--rule); }
.cost-home-col { padding: 20px 22px; }
.cost-home-col + .cost-home-col { border-left: 1px solid var(--rule); }
.cost-home-year { margin: 0; color: var(--gold); font: 700 12px var(--mono); letter-spacing: .1em; }
.cost-home-price { margin: 6px 0 14px; font: 650 clamp(28px, 3.4vw, 40px)/1 var(--serif); letter-spacing: -.02em; }
.cost-home-facts { margin: 0; display: grid; gap: 8px; }
.cost-home-facts > div { display: flex; justify-content: space-between; gap: 14px; padding-bottom: 8px; border-bottom: 1px solid var(--rule); }
.cost-home-facts dt { color: var(--ink-soft); font: 13px var(--sans); }
.cost-home-facts dd { margin: 0; font: 650 14px var(--mono); }
.cost-home-facts .is-burdened { color: var(--cost-more); }

/* Goods */
.cost-good-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 0; margin-top: var(--s4); border: 1px solid var(--rule); }
.cost-good { padding: 22px; border-right: 1px solid var(--rule); border-bottom: 1px solid var(--rule); }
.cost-good h3 { margin: 0; display: flex; align-items: center; gap: 9px; font: 650 19px var(--serif); }
.cost-good h3 small { color: var(--ink-faint); font: 400 12px var(--mono); }
.cost-good-move { display: flex; align-items: baseline; gap: 10px; margin: 14px 0 0; font: 650 17px var(--mono); }
.cost-good-then { color: var(--ink-faint); }
.cost-good-arrow { color: var(--ink-faint); }
.cost-good-move.is-cheaper .cost-good-now { color: var(--cost-less); }
.cost-good-move.is-dearer .cost-good-now { color: var(--cost-more); }
.cost-good-verdict { margin: 6px 0 0; color: var(--ink-soft); font: 13px var(--sans); }
.cost-good-prices { margin: var(--s3) 0 0; color: var(--ink-faint); font: 12px var(--mono); }

/* Method */
.cost-method { padding-block: var(--s5) var(--s6); }
.cost-method-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0; margin-top: var(--s4); border: 1px solid var(--rule); }
.cost-method-grid article { padding: 22px; border-right: 1px solid var(--rule); border-bottom: 1px solid var(--rule); }
.cost-method-grid span { color: var(--gold); font: 700 11px var(--mono); letter-spacing: .1em; }
.cost-method-grid h3 { margin: 8px 0 8px; font: 650 17px var(--serif); }
.cost-method-grid p { margin: 0; max-width: 46ch; color: var(--ink-soft); font: 14px/1.6 var(--sans); }

/* --- cohort ladder: same age, ten years apart each rung -------------------
   A real table, because this is genuinely tabular: six cohorts against six
   measures. It scrolls inside its own focusable region rather than shrinking
   the type or dropping columns — a narrow screen is not a reason to publish
   fewer of the numbers than a wide one gets. */
.cost-cohort, .cost-split { padding-block: var(--s5); }
.cost-ladder-scroll { margin-top: var(--s4); overflow-x: auto; border: 1px solid var(--rule); }
.cost-ladder-scroll:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.cost-ladder { border-collapse: collapse; width: 100%; min-width: 620px; }
.cost-ladder th, .cost-ladder td { padding: 12px 16px; text-align: left; border-bottom: 1px solid var(--rule); vertical-align: middle; }
.cost-ladder thead th { color: var(--ink-soft); font: 500 11px/1.35 var(--mono); letter-spacing: .06em; text-transform: uppercase; vertical-align: bottom; }
/* Explicit size, not the browser's <small> shrink. Inheriting it compounded on
   top of the 11px header and rendered "then" at 9.17px — the smallest text on
   the page, on the column doing the most explaining. */
.cost-ladder thead th small { display: block; font-size: 11px; font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--ink-faint); }
.cost-ladder tbody th { font: 650 15px var(--mono); }
.cost-ladder tbody th small { display: block; margin-top: 2px; color: var(--ink-faint); font: 400 11px var(--mono); }
.cost-ladder td { font: 14px var(--mono); }
.cost-ladder .num { text-align: right; font-variant-numeric: tabular-nums; }
.cost-ladder tbody tr:last-child th, .cost-ladder tbody tr:last-child td { border-bottom: 0; }
/* The current cohort is the reader's "now" — marked by weight and a rule, not
   by a fill, so it reads as the last row rather than as a highlighted answer. */
.cost-ladder tr.is-now th, .cost-ladder tr.is-now td { background: var(--paper-deep); }
.cost-paid { color: var(--ink-faint); font-style: italic; }
/* The bar IS the years-of-work number, drawn. Same ink as the text, no scale
   of its own, so it can only ever say what the digits beside it say.
   The digits come FIRST and the track is fixed-width: with the bar leading, a
   long bar pushed its own number onto a second line on wide screens and clean
   off the scrolled edge on a phone, so the rows that mattered most were the
   ones whose value you could not read. */
/* The flex box is an inner span, never the <td> itself: making a table cell a
   flex container takes it out of table layout, so its border-bottom stops
   sharing the row's baseline and the column draws a staircase down the table. */
.cost-ladder-measure { display: flex; align-items: center; gap: 10px; }
.cost-ladder-val { flex: none; min-width: 2.5ch; text-align: right; font-variant-numeric: tabular-nums; }
.cost-ladder-track { flex: none; width: 110px; }
.cost-ladder-fill { display: block; height: 9px; background: var(--ink); opacity: .8; }
/* Only shown at widths where the table actually outruns its container. */
/* Say a table runs off the side before the reader has to discover it by
   swiping. /cost has done this since the rebuild; three tables elsewhere
   scrolled silently — /privacy hid 392px of its retention table at 390px wide,
   /us/explore 324px of the state explorer, /world/explore 872px of the country
   rankings. Sharing one declaration rather than copying the look: the class
   names stay separate because cost generates its copy per table from a real
   column count, which is the part worth keeping page-local. */
.cost-ladder-hint, .table-hint { display: none; margin: var(--s4) 0 -8px; color: var(--ink-faint); font: 12px var(--mono); }

/* --- the split: two people, one year ------------------------------------- */
.cost-split-grid { display: grid; grid-template-columns: 1fr 1fr; margin-top: var(--s4); border: 1px solid var(--rule); }
.cost-split-col { padding: 20px 22px; }
.cost-split-col + .cost-split-col { border-left: 1px solid var(--rule); }
.cost-split-kicker { margin: 0; color: var(--gold); font: 700 12px var(--mono); letter-spacing: .1em; text-transform: uppercase; }
.cost-split-hours { margin: 10px 0 16px; display: flex; align-items: baseline; gap: 9px; }
.cost-split-hours b { font: 650 clamp(34px, 4.2vw, 48px)/1 var(--serif); letter-spacing: -.02em; }
.cost-split-hours span { color: var(--ink-soft); font: 13px var(--sans); }
.cost-split-note { margin: 14px 0 0; max-width: 42ch; color: var(--ink-soft); font: 14px/1.6 var(--sans); }
.cost-verdict { margin: var(--s4) 0 0; max-width: 68ch; font: 17px/1.5 var(--sans); }
.cost-verdict b { font-family: var(--mono); font-weight: 650; }

/* --- Contents -------------------------------------------------------------
   The page carries twenty-seven priced things across seven sections. Before
   this there was no way to see what was on it or skip to the part you came
   for, which on a phone meant scrolling blind. Numbered because the order is
   an argument: what changed, then the things themselves, then housing. */
.cost-contents { margin-top: var(--s5); }
.cost-contents ol { margin: 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: 0; border: 1px solid var(--rule); counter-reset: toc; }
.cost-contents li { flex: 1 1 auto; counter-increment: toc; border-right: 1px solid var(--rule); }
.cost-contents li:last-child { border-right: 0; }
.cost-contents a { display: flex; align-items: baseline; gap: 8px; min-height: 44px; padding: 11px 15px; color: var(--ink); text-decoration: none; font: 500 14px/1.3 var(--sans); }
.cost-contents a::before { content: counter(toc, decimal-leading-zero); color: var(--gold); font: 600 11px var(--mono); letter-spacing: .06em; }
.cost-contents a:hover { background: var(--paper-deep); text-decoration: underline; text-underline-offset: 4px; }

/* --- The wage panel -------------------------------------------------------
   min-height is load-bearing, not cosmetic. Without JavaScript this box holds a
   sentence; with it, /js/cost.mjs replaces the contents with the live control.
   Reserving the height means that swap moves nothing, which is what keeps the
   page inside its 0.05 CLS budget no matter when the module executes. Change
   the contents and re-measure the budget, or don't change them. */
.cost-wage-shell { margin-top: var(--s5); }
.cost-wage { display: flex; flex-direction: column; justify-content: center; gap: 10px; min-height: 96px; padding: 16px 18px; border: 1px solid var(--rule); background: var(--paper-deep); }
.cost-wage-static, .cost-wage-state { margin: 0; max-width: 78ch; color: var(--ink-soft); font: 14px/1.6 var(--sans); }
.cost-wage-static b, .cost-wage-state b { color: var(--ink); font-family: var(--mono); font-weight: 650; }
.cost-wage-row { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; }
.cost-wage-field { display: flex; align-items: center; gap: 8px; font: 500 15px var(--sans); }
.cost-wage-money { display: inline-flex; align-items: center; gap: 2px; font: 650 15px var(--mono); }
.cost-wage input { width: 7.5ch; min-height: 44px; padding: 4px 6px; border: 1px solid var(--rule-strong); border-radius: var(--radius-control); background: var(--paper); color: var(--ink); font: 650 16px var(--mono); }
.cost-wage select { min-height: 44px; padding: 4px 8px; border: 1px solid var(--rule-strong); border-radius: var(--radius-control); background: var(--paper); color: var(--ink); font: 500 14px var(--sans); }
.cost-wage input:focus-visible, .cost-wage select:focus-visible, .cost-wage-reset:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.cost-wage-reset { min-height: 44px; padding: 6px 14px; border: 1px solid var(--rule-strong); border-radius: var(--radius-control); background: transparent; color: var(--ink); font: 500 14px var(--sans); cursor: pointer; }
.cost-wage-reset:hover { background: var(--paper); }
/* A figure computed from a number the reader typed is not a measurement, and
   the page has to look different when it is showing one. */
.cost-page.is-your-wage .cost-wage { border-color: var(--ev-input); border-left-width: 3px; }

/* --- Ranked table ---------------------------------------------------------
   The one view that answers the headline without holding six sections in your
   head. Bars run from a centre line so more-work and less-work are read as
   direction, not just magnitude, and they are LOG-scaled in the builder: on a
   linear scale everything that got cheaper is squashed into 0–1 and the page
   would look like a doom generator by geometry alone. */
.cost-rank-scroll { margin-top: var(--s4); overflow-x: auto; border: 1px solid var(--rule); }
.cost-rank-scroll:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.cost-rank-table { border-collapse: collapse; width: 100%; min-width: 640px; }
.cost-rank-table th, .cost-rank-table td { padding: 9px 14px; text-align: left; border-bottom: 1px solid var(--rule); vertical-align: middle; }
.cost-rank-table tbody tr:last-child th, .cost-rank-table tbody tr:last-child td { border-bottom: 0; }
.cost-rank-table thead th { color: var(--ink-soft); font: 500 11px var(--mono); letter-spacing: .06em; text-transform: uppercase; }
.cost-rank-table tbody th { display: flex; align-items: center; gap: 8px; font: 600 15px var(--sans); border-bottom: 0; }
.cost-rank-table .num { text-align: right; font: 400 13px var(--mono); color: var(--ink-soft); white-space: nowrap; }
.cost-rank-table .num b { font-weight: 650; font-size: 14px; }
.cost-rank-table tbody tr:hover { background: var(--paper-deep); }
.cost-rank-bar { width: 40%; min-width: 140px; }
.cost-rank-track { position: relative; display: block; height: 12px; background: var(--paper-deep); border-left: 1px solid var(--rule); border-right: 1px solid var(--rule); }
/* The centre line is the only reference the bar has; without it a bar reads as
   a length rather than a departure from "unchanged". */
.cost-rank-track::before { content: ""; position: absolute; left: 50%; top: -2px; bottom: -2px; width: 1px; background: var(--ink-faint); }
.cost-rank-track i { position: absolute; top: 0; bottom: 0; display: block; }
.cost-rank-track i.is-dearer { background: var(--cost-more); }
.cost-rank-track i.is-cheaper { background: var(--cost-less); }
.cost-rank-table b.is-dearer { color: var(--cost-more); }
.cost-rank-table b.is-cheaper { color: var(--cost-less); }

/* --- Category disclosures -------------------------------------------------
   Widening the catalog to twenty-seven items took the page to twenty-six phone
   screens, which is not an improvement no matter how good the data is. The
   ranked table stays open and carries every measured item, so what folds here
   is per-item depth and never the result. Native <details>: no script, keyboard
   focus for free, and it still works with scripting off. */
.cost-cat > summary { display: flex; align-items: baseline; flex-wrap: wrap; gap: 10px; min-height: 44px; padding: 8px 0; cursor: pointer; list-style: none; }
.cost-cat > summary::-webkit-details-marker { display: none; }
.cost-cat > summary::after {
  content: "";
  order: -1;
  flex: none;
  width: 8px; height: 8px;
  margin-right: 2px;
  border-right: 2px solid var(--gold);
  border-bottom: 2px solid var(--gold);
  transform: rotate(-45deg);
  transition: transform 120ms ease;
}
.cost-cat[open] > summary::after { transform: rotate(45deg); }
.cost-cat > summary:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }
.cost-cat > summary h2 { margin: 0; }
.cost-cat-count, .cost-cat-more { color: var(--ink-faint); font: 400 12px var(--mono); letter-spacing: .04em; }
.cost-cat-more { color: var(--ink); font: 600 17px var(--serif); letter-spacing: 0; }
.cost-housing-more > .cost-cat > summary { border-top: 1px solid var(--rule); padding-top: 16px; }
@media (prefers-reduced-motion: reduce) { .cost-cat > summary::after { transition: none; } }

/* --- Reconstructed items --------------------------------------------------
   A double rule for a figure that is derived twice over: arithmetic on an index
   that is itself an estimate. Deliberately not the dotted `modeled` rule —
   nothing here is simulated — and not the plain `derived` rule either, because
   these carry an assumption the others do not. */
.prov.reconstructed { border-left: 4px double var(--ev-observed); }
.prov.reconstructed b { color: var(--ev-modeled); }
/* 12px is a floor, not a preference: the live WCAG gate fails /cost outright if
   any p/li/td computes under it, and shrinking a provenance label is exactly
   the wrong economy — it is the line telling a reader this figure is weaker
   than the ones around it. */
.cost-good.is-reconstructed .prov { margin: 12px 0 0; padding: 3px 0 3px 10px; font-size: 12px; }
.cost-good.is-reconstructed .cost-good-move { flex-wrap: wrap; gap: 6px; }
.cost-good.is-reconstructed .cost-good-then { font: 400 12px/1.4 var(--sans); }
.cost-good-prices b { font-weight: 650; }

@media (max-width: 760px) {
  .cost-home-grid, .cost-method-grid { grid-template-columns: 1fr; }
  .cost-home-col + .cost-home-col { border-left: 0; border-top: 1px solid var(--rule); }
  .cost-good-grid { grid-template-columns: 1fr; }
  .cost-dek { font-size: 16px; }
  .cost-home-facts > div { flex-direction: column; gap: 2px; }
  .cost-split-grid { grid-template-columns: 1fr; }
  .cost-split-col + .cost-split-col { border-left: 0; border-top: 1px solid var(--rule); }
  .cost-contents ol { flex-direction: column; }
  .cost-contents li { border-right: 0; border-bottom: 1px solid var(--rule); }
  .cost-contents li:last-child { border-bottom: 0; }
}

/* The ladder's min-width plus the shell's gutters stop fitting here, which is
   the point at which a reader needs telling that the table pans. */
@media (max-width: 720px) {
  .cost-ladder-hint, .table-hint { display: block; }
}

/* Mobile legibility floor, matching the one simulation-next.css sets for the
   simulate shell. /cost is on the reading stylesheet so it did not inherit that
   floor, and it carries more micro-labelled type than any other page: sparkline
   keys at 10px, table headers and cohort birth-years at 11px, the scrubber at
   11px. Measured at 390px before this, the smallest was 9.17px.
   The scrubber can grow safely because its strip is a wrapping grid — at 12px
   the years reflow to a second row instead of pushing the page sideways. */
@media (max-width: 700px) {
  .cost-spark-key,
  .cost-spark-legend,
  .cost-ladder thead th,
  .cost-ladder thead th small,
  .cost-ladder tbody th small,
  .cost-map-scrub-label,
  .cost-map-tick,
  .cost-map-fig .legend-bar,
  .cost-sources {
    font-size: 12px;
  }
}

/* --- state map + year scrubber -------------------------------------------
   The scrubber is a native radio group, not a slider and not a script: arrow
   keys, roving focus, and the checked state all come from the platform already
   correct, and the first frame is on screen before any JS could have run.
   The per-year fill rules are emitted by build-cost-map.mjs into the section,
   because they follow the artifact's year range rather than this file's. */
.cost-map { padding-block: var(--s5); }
.cost-map-radio { position: absolute; }
.cost-map-scrub { margin: var(--s4) 0 0; padding: 0; border: 0; display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }
.cost-map-scrub-label { color: var(--ink-faint); font: 500 11px var(--mono); letter-spacing: .1em; text-transform: uppercase; }
/* Wraps rather than overflows. A year label cannot shrink below its own text,
   so nine of them set a hard min-content floor on the strip — at 320px with the
   WCAG 1.4.12 text-spacing overrides applied that floor exceeds the viewport and
   pushed the whole document sideways. Wrapping to a second row costs nothing and
   never clips a year.
   Grid rather than a wrapping flex row: with flex, the years that wrap onto a
   second row stretch to fill it, so a lone ninth year became a full-width
   button. Grid columns are defined for the whole strip, so a wrapped year keeps
   the same width as the ones above it. */
.cost-map-ticks { display: grid; grid-template-columns: repeat(auto-fit, minmax(36px, 1fr)); flex: 1 1 320px; border-bottom: 1px solid var(--rule); }
.cost-map-tick {
  text-align: center; padding: 6px 2px 8px; cursor: pointer;
  color: var(--ink-faint); font: 12px var(--mono);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  /* 44px of vertical target on touch without pushing the strip taller. */
  min-height: 44px; display: flex; align-items: flex-end; justify-content: center;
}
.cost-map-tick:hover { color: var(--ink); background: var(--paper-deep); }
.cost-map-fig { margin: var(--s4) 0 0; }
.cost-map-svg { display: block; width: 100%; height: auto; background: var(--paper-deep); border: 1px solid var(--rule); }
/* Every state starts at the lightest band and is repainted by the emitted
   per-year rules. Without a base fill an unmatched year would render invisible
   states rather than obviously-wrong ones. */
/* Mid-tone borders, not paper ones. A paper-coloured stroke separates the dark
   end of the ramp well and vanishes at the light end — the lightest band sits at
   1.08:1 against paper, so the palest states ran together into one shape with no
   readable boundary. --rule contrasts in both directions. */
.cost-map-st { fill: var(--ramp-ink-1); stroke: var(--rule); stroke-width: .6; }
.cost-map-nodata { fill: url(#cost-map-hatch); stroke: var(--rule); stroke-width: .6; }
.cost-map-readouts { margin-top: var(--s3); }
.cost-map-readout { display: none; }
.cost-map-rline { margin: 0; max-width: 72ch; color: var(--ink-soft); font: 15px/1.6 var(--sans); }
.cost-map-rline b { color: var(--ink); }
.cost-map-fig .legend-bar { margin-top: 0; border-top: 0; }
/* Delineate each swatch. The lightest band sits at 1.08:1 against the legend's
   own background, so without an edge the ramp appears to start at the second
   band and the reader loses the bottom of the scale. Scoped to this figure
   rather than the shared .legend-ramp, whose other users carry reviewed visual
   baselines — the same latent issue is worth a separate look there. */
.cost-map-fig .legend-ramp i { box-shadow: inset 0 0 0 1px var(--rule); }
.cost-map-table { margin-top: var(--s4); }
/* min-height, not just padding: this is a real tap target and the phone
   target-size scan holds it to 44px. Kept as list-item so the disclosure
   triangle survives — display:flex would drop the marker. */
.cost-map-table > summary { cursor: pointer; padding: 14px 0; min-height: 44px; box-sizing: border-box; color: var(--ink-soft); font: 13px/1.2 var(--sans); border-bottom: 1px solid var(--rule); }
.cost-map-table > summary:hover { color: var(--ink); }
.cost-map-table[open] > summary { font-weight: 600; color: var(--ink); }
.cost-map-grid { min-width: 640px; }
.cost-map-grid tbody th { font: 500 13px var(--sans); white-space: nowrap; }
.cost-map-state-name { display: block; }
.cost-map-row-sources { display: flex; gap: 8px; margin-top: 3px; font: 11px/1.3 var(--mono); }
.cost-map-grid td { font-size: 13px; }
.cost-map-grid .is-now { font-weight: 650; }
.cost-map-grid tr.is-nodata th, .cost-map-grid tr.is-nodata td { color: var(--ink-faint); font-style: italic; }

@media (max-width: 760px) {
  .cost-map-scrub-label { flex-basis: 100%; }
  .cost-map-row-sources { font-size: 12px; }
  .cost-map-row-sources .cost-check {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
  /* No font-size shrink here. It used to drop the years to 11px to keep the
     strip on one row; the strip is a wrapping grid now, so at 12px the years
     reflow to a second row instead — and 11px put this control below the
     mobile legibility floor the rest of the site keeps. */
}

/* Mobile legibility floor. The kicker, provenance labels and small table text sit at
   11px, which is fine on a desktop where they are dense supporting metadata and tight
   next to the thing they describe. On a phone they are the smallest text on the page
   and were measured at 11px across the home, simulate, news and ask routes. Raise the
   floor to 12px below the established 700px breakpoint without touching desktop
   density. Evidence labels (Observed / Modeled) are included on purpose: they carry
   the disclosure that the whole product depends on.

   .signal-record-line is DELIBERATELY excluded and stays at 11px. It is the only
   sub-12px text left on any route at phone width, and the exclusion is a decision
   rather than an oversight: it is masked in every visual baseline, it carries a
   build hash and a date rather than product content, and it is set in uppercase
   mono where 11px is a deliberate typographic choice. Raising it would shift the
   stacked layout below 600px by about a pixel and newly invalidate the two phone
   baselines (story, 404) that this floor does not otherwise touch. Revisit if the
   line ever carries something a reader has to act on. */
@media (max-width: 700px) {
  .kicker,
  .home-story .kicker,
  .cost-map-scrub-label,
  .state-scope-note,
  .bar-row,
  .world-explore-page .rank-note,
  .prov,
  .browse-status,
  .stage-disclosure,
  .reaction-legend-note,
  .disclosure,
  .home-miss-protocol {
    font-size: 12px;
  }
  .bills-page table {
    font-size: 12px;
  }
}

/* The handoff into the simulation. Styled as a departure, not a source link:
   the source links beside it stay on the evidence side of the boundary, and
   this one deliberately leaves it. */
/* A real 44px target, not an inline afterthought. It sits beside the source
   links but it is not one of them: those stay on the evidence side of the
   boundary and this one deliberately crosses it, so it gets the tap target an
   action deserves rather than the exemption inline text links get. */
.cost-ask { display: inline-flex; align-items: center; min-height: 44px; margin-left: 10px; color: var(--ev-modeled); font: 500 12px var(--mono); letter-spacing: .02em; text-decoration: underline; text-underline-offset: 3px; }
.cost-ask:hover { text-decoration-thickness: 2px; }

/* --- The basket -----------------------------------------------------------
   Rendered complete and totalled at build time; /js/cost.mjs swaps the
   quantity cells for inputs. The totals sit beside the table on desktop and
   below it on a phone, because the totals are the finding and the rows are the
   working. */
.cost-basket-grid { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(220px, .65fr); gap: 0; margin-top: var(--s4); border: 1px solid var(--rule); }
.cost-basket-table { border-collapse: collapse; width: 100%; }
.cost-basket-table th, .cost-basket-table td { padding: 9px 14px; text-align: left; border-bottom: 1px solid var(--rule); vertical-align: middle; }
.cost-basket-table thead th { color: var(--ink-soft); font: 500 11px var(--mono); letter-spacing: .06em; text-transform: uppercase; }
.cost-basket-table tbody th { display: flex; align-items: center; gap: 8px; font: 600 15px var(--sans); border-bottom: 0; }
.cost-basket-table tbody th small { color: var(--ink-faint); font: 400 11px var(--mono); }
.cost-basket-table .num { text-align: right; font: 400 13px var(--mono); color: var(--ink-soft); white-space: nowrap; }
.cost-basket-input { width: 7.5ch; min-height: 44px; padding: 2px 4px; border: 1px solid var(--rule-strong); border-radius: var(--radius-control); background: var(--paper); color: var(--ink); font: 650 14px var(--mono); text-align: right; }
.cost-basket-input:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.cost-basket-totals { display: flex; flex-direction: column; justify-content: flex-start; gap: var(--s4); padding: 20px; border-left: 1px solid var(--rule); background: var(--paper-deep); }
.cost-basket-total { display: flex; flex-direction: column; gap: 3px; }
.cost-basket-year { color: var(--gold); font: 700 11px var(--mono); letter-spacing: .1em; }
.cost-basket-total b { font: 650 clamp(26px, 3vw, 34px)/1 var(--serif); letter-spacing: -.02em; }
.cost-basket-usd { color: var(--ink-soft); font: 12px var(--mono); }

/* --- Sorting the states table --------------------------------------------- */
.cost-sort { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.cost-sort > span { color: var(--ink-soft); font: 500 11px var(--mono); letter-spacing: .06em; text-transform: uppercase; }
.cost-sort button { min-height: 44px; padding: 6px 12px; border: 1px solid var(--rule); border-radius: var(--radius-control); background: transparent; color: var(--ink); font: 500 13px var(--sans); cursor: pointer; }
.cost-sort button[aria-pressed="true"] { border-color: var(--rule-strong); background: var(--paper-deep); font-weight: 650; }
.cost-sort button:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

@media (max-width: 760px) {
  .cost-basket-grid { grid-template-columns: 1fr; }
  .cost-basket-totals { border-left: 0; border-top: 1px solid var(--rule); flex-direction: row; flex-wrap: wrap; gap: var(--s5); }
}
/* Same treatment the ranked table gets: four columns do not fit 320px, and a
   page-level horizontal scroll is a failure the gate catches. */
.cost-basket-scroll { overflow-x: auto; }
.cost-basket-scroll:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }
.cost-basket-table { min-width: 420px; }
/* Stated by the script only when a typed wage is active, because it is only
   true then: some of the section's numbers followed the wage and some could
   not. Without it the section badge would claim more than the figures deliver. */
.cost-scope-note { margin: 6px 0 0; max-width: 76ch; color: var(--ink-soft); font: 13px/1.55 var(--sans); border-left: 3px dashed var(--ev-input); padding-left: 10px; }
