/*
  INFORMANT — surveillance-noir / ticker-tape design system.

  Evidence-room, not dashboard: near-black layered grounds, one hot signal
  color (amber), strict semantic green/red for market moves, everything else
  slate. Data is set in a single mono stack throughout — the contrast comes
  from scale and tracking, not from mixing typefaces. Cards read like case
  files (reticle corner brackets, dashed meta rules); badges read like
  stamps; the tape strip under the masthead is the wire this whole thing is
  named for.
*/

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }

:root {
  /* grounds — each a step lighter, evidence-room lighting */
  --bg0: #0a0c0f;
  --bg1: #12161b;
  --bg2: #171d24;
  --bg3: #1c232c;

  /* lines */
  --line: #262e37;
  --line-soft: #1a2027;

  /* ink */
  --ink: #cdd6de;
  --ink-dim: #7c8a97;
  --ink-faint: #4b5560; /* decorative chrome only (hairlines, ticks) — ~2.4:1, never on readable text */

  /* the one hot accent */
  --accent: #e8b04b;
  --accent-dim: #6b5228;
  --accent-soft: rgba(232, 176, 75, 0.14);
  --accent-glow: rgba(232, 176, 75, 0.55);

  /* semantic — strictly up/down, never decorative */
  --up: #4cae6b;
  --down: #d66a52;

  /* cool slate, used sparingly for neutral/informational stamps (GAP) */
  --info: #7a93a8;

  --font-mono: ui-monospace, "SFMono-Regular", "Berkeley Mono", "JetBrains Mono",
    "Fira Code", Menlo, Consolas, "Liberation Mono", monospace;

  --sp-1: .25rem; --sp-2: .5rem; --sp-3: .75rem; --sp-4: 1rem; --sp-5: 1.5rem; --sp-6: 2.25rem;
}

body {
  background: var(--bg0);
  background-image: repeating-linear-gradient(
    180deg, rgba(255, 255, 255, .015) 0px, rgba(255, 255, 255, .015) 1px,
    transparent 1px, transparent 3px
  );
  color: var(--ink);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  min-height: 100vh;
  line-height: 1.4;
}

a { color: inherit; }
h1, h2, h3, p { margin: 0; }

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

/* ---------------------------------------------------------------- masthead */

.masthead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-4);
  background: var(--bg2);
  border-bottom: 1px solid var(--line);
}

.masthead__brand {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}

.rec-dot {
  width: 8px;
  height: 8px;
  min-width: 8px;
  border-radius: 50%;
  background: var(--down);
  box-shadow: 0 0 0 0 rgba(214, 106, 82, .5);
  animation: rec-pulse 2.2s ease-in-out infinite;
}

@keyframes rec-pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(214, 106, 82, .5); }
  50% { opacity: .5; box-shadow: 0 0 0 5px rgba(214, 106, 82, 0); }
}

.wordmark {
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: .5em;
  color: var(--ink);
}

.status-line {
  color: var(--ink-dim);
  font-size: .68rem;
  letter-spacing: .02em;
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* -------------------------------------------------------------- tape strip */

.tape {
  overflow: hidden;
  background: var(--bg2);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

.tape__track {
  display: inline-flex;
  width: max-content;
  animation: tape-scroll 32s linear infinite;
}

.tape__track--static { animation: none; }

@keyframes tape-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

.tape__group {
  display: inline-flex;
  align-items: center;
}

.tape__cell {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  padding: .5em 1.1em;
  font-size: .72rem;
  letter-spacing: .03em;
  border-right: 1px solid var(--line-soft);
  color: var(--ink-dim);
}

.tape__ticker { color: var(--ink); font-weight: 700; }
.tape__cell--empty { color: var(--ink-dim); }

/* ------------------------------------------------------------------- nav */

.nav {
  display: flex;
  gap: var(--sp-5);
  padding: 0 var(--sp-4);
  background: var(--bg2);
  border-bottom: 1px solid var(--line);
  overflow-x: auto;
}

.nav__tab {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  padding: var(--sp-3) 0;
  color: var(--ink-dim);
  text-decoration: none;
  font-size: .72rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}

.nav__tab:hover { color: var(--ink); }
.nav__tab.is-active { color: var(--accent); border-bottom-color: var(--accent); }

/* MORE + sheet are mobile-only chrome; desktop never shows them. */
.nav__more { display: none; }
.nav-sheet, .nav-sheet__backdrop { display: none; }

/* ------------------------------------------------------------------ view */

.view {
  max-width: 52rem;
  margin: 0 auto;
  padding: var(--sp-5) var(--sp-4) var(--sp-6);
}

.view-stub {
  padding: var(--sp-6) 0;
  border-top: 1px dashed var(--line);
}

.view-stub__title {
  font-size: 1.1rem;
  letter-spacing: .35em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.view-stub__note {
  margin-top: var(--sp-3);
  color: var(--ink-dim);
  font-size: .8rem;
}

.view-stub__meta {
  margin-top: var(--sp-2);
  color: var(--ink-dim);
  font-size: .72rem;
}

.error { color: var(--down); font-size: .8rem; }

/* -------------------------------------------------------- reticle frames */

/* Corner-bracket overlay via exactly two pseudo-elements: ::before paints the
   top-left + top-right brackets, ::after the bottom-left + bottom-right ones.
   Each is four 1px hairlines positioned at the element's own corners — a
   viewfinder/evidence-tag mark, never a full border. */
.reticle {
  position: relative;
}

.reticle::before,
.reticle::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 9px;
  pointer-events: none;
  background-repeat: no-repeat;
  background-image:
    linear-gradient(var(--accent-dim), var(--accent-dim)),
    linear-gradient(var(--accent-dim), var(--accent-dim)),
    linear-gradient(var(--accent-dim), var(--accent-dim)),
    linear-gradient(var(--accent-dim), var(--accent-dim));
  background-size: 9px 1px, 1px 9px, 9px 1px, 1px 9px;
}

.reticle::before {
  top: 0;
  background-position: top left, top left, top right, top right;
}

.reticle::after {
  bottom: 0;
  background-position: bottom left, bottom left, bottom right, bottom right;
}

/* -------------------------------------------------------------------- wire */

.wire { display: flex; flex-direction: column; }

.card {
  background: var(--bg1);
  border: 1px solid var(--line);
  padding: var(--sp-4);
  margin-bottom: var(--sp-3);
}

.card__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-3);
  padding-bottom: var(--sp-2);
  border-bottom: 1px dashed var(--line-soft);
  color: var(--ink-dim);
  font-size: .68rem;
  letter-spacing: .06em;
  text-transform: uppercase;
}

.card__time { text-decoration: none; }
.card__time:hover { color: var(--accent); }

.card__stamps {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .4em;
}

.card__body {
  color: var(--ink);
  font-size: .875rem;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere; /* post text can contain very long unbroken URLs */
}

/* ~6 lines, then the expand button takes over — a webkit-box clamp is still
   the only reliable cross-browser way to do this without measuring layout. */
.card__body--clamp {
  display: -webkit-box;
  -webkit-line-clamp: 6;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.card__expand {
  display: block;
  margin-top: var(--sp-2);
  padding: 0;
  border: none;
  background: none;
  color: var(--accent);
  font-family: inherit;
  font-size: .66rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  cursor: pointer;
}

.card__expand:hover { text-decoration: underline; }

.card__hydrate-note {
  margin-top: var(--sp-2);
  color: var(--ink-faint);
  font-size: .68rem;
}

.card__chips {
  margin-top: var(--sp-2);
  display: flex;
  flex-wrap: wrap;
  gap: .4em;
}

/* ---------------------------------------------------------------- media */

/* Inline media (Phase-7 Task 6): shared across the wire, dossier post panel,
   and burst member cards (see web/lib.js's mediaBlock) — one look for "here
   is the evidence", never confused with post text. A stored image is served
   only from our own /media/<hash>.<ext> route (mediaBlock validates hash+ext
   client-side before ever building that src — never a post.media URL
   directly); failed/unsupported entries fall back to the plain link row. */
.media-list {
  margin-top: var(--sp-3);
  padding-top: var(--sp-2);
  border-top: 1px dashed var(--line-soft);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.media-entry { display: flex; flex-direction: column; gap: .4em; }

.media-frame {
  display: block;
  padding: 0;
  border: 1px solid var(--line);
  background: var(--bg2);
  max-width: 100%;
  line-height: 0; /* kill the inline-image baseline gap under the border */
  cursor: pointer;
  appearance: none;
  border-radius: 0;
}

.media-frame:hover { border-color: var(--accent-dim); }

.media-image {
  display: block;
  max-width: 100%;
  max-height: 360px;
  width: auto;
  height: auto;
  object-fit: contain;
}

.media-video {
  display: block;
  margin-top: 0.5rem;
  max-width: 100%;
  max-height: 420px;
  width: 100%;
  border: 1px solid var(--ink-dim);
  background: #000;
}

/* MACHINE DESCRIPTION stamp — dim + italic + a small, wide-tracked label:
   an honesty requirement, not styling taste. This is model output
   describing an image, and must never be visually confusable with the
   poster's own words (.card__body/.dossier__summary etc. are never
   italicized anywhere else in the app). */
.media-desc {
  display: flex;
  flex-direction: column;
  gap: .2em;
}

.media-desc__label {
  color: var(--ink-faint);
  font-size: .6rem;
  letter-spacing: .2em;
  text-transform: uppercase;
}

.media-desc__text {
  color: var(--ink-dim);
  font-size: .78rem;
  font-style: italic;
  line-height: 1.5;
}

.media-link {
  color: var(--accent);
  font-size: .72rem;
  word-break: break-all;
  text-decoration: none;
}

.media-link:hover { text-decoration: underline; }

/* ------------------------------------------------------------------- llm */

.llm-row {
  margin-top: var(--sp-3);
  padding-top: var(--sp-2);
  border-top: 1px dashed var(--line-soft);
}

.llm-row__meta {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-2);
}

.llm-summary {
  color: var(--ink-dim);
  font-size: .78rem;
  line-height: 1.5;
}

.meter {
  width: 4rem;
  height: .4rem;
  background: var(--bg2);
  border: 1px solid var(--line);
  overflow: hidden;
}

.meter__fill {
  height: 100%;
  background: var(--accent);
}

/* -------------------------------------------------------------- reaction */

.reaction {
  margin-top: var(--sp-3);
  padding-top: var(--sp-2);
  border-top: 1px dashed var(--line-soft);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
}

.reaction__row {
  display: flex;
  align-items: center;
  gap: .4em;
}

.reaction__ticker {
  min-width: 2.6em;
  color: var(--ink);
  font-weight: 700;
  font-size: .72rem;
}

.reaction__spark {
  width: 64px;
  height: 18px;
  display: block;
  flex-shrink: 0;
}

.sparkline__zero { stroke: var(--line-soft); stroke-width: 1; }
.sparkline__line { fill: none; stroke-width: 1.5; }

/* ----------------------------------------------------------------- jury */

/* Phase-8 jury widget — sits below the reaction strip on a solo card or a
   burst-group header (one vote per event, not per member post — see
   wire.js/burst.js/dossier.js call sites). Buttons reuse the same bordered
   chip/tab idiom as .patterns__tab/.archive__load-more; the RIGHT/WRONG/HUNG
   verdict is a plain .stamp (see lib.js's stamp()) so it inherits the same
   dim small-caps bordered-badge treatment as every other stamp in the app —
   only its color is bespoke, per the brief's positive/negative accent rule. */
.jury {
  margin-top: var(--sp-3);
  padding-top: var(--sp-2);
  border-top: 1px dashed var(--line-soft);
  display: flex;
  flex-direction: column;
  gap: .5em;
}

.jury__label {
  color: var(--ink-faint);
  font-size: .6rem;
  letter-spacing: .2em;
  text-transform: uppercase;
}

.jury__buttons { display: flex; flex-wrap: wrap; gap: .6em; }

.jury__btn {
  padding: .35em 1.1em;
  border: 1px solid var(--line);
  background: var(--bg1);
  color: var(--ink-dim);
  font-family: inherit;
  font-size: .72rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  cursor: pointer;
}

.jury__btn:hover:not(:disabled) { color: var(--ink); border-color: var(--accent-dim); }
.jury__btn:disabled { cursor: default; opacity: .6; }

.jury__tally,
.jury__your-call,
.jury__verdict {
  color: var(--ink-dim);
  font-size: .72rem;
  letter-spacing: .02em;
}

.jury__verdict { display: flex; align-items: center; flex-wrap: wrap; gap: .35em; }

.stamp--right { color: var(--up); }
.stamp--wrong { color: var(--down); }

/* ----------------------------------------------------- poll of the week */

/* POLL OF THE WEEK (Task 2) — deliberately NOT a variant of .jury above:
   a standalone card (one per page, sitting above the broadcast feed),
   not a per-burst footer strip; solid accent border instead of .jury's
   dashed line; its own label copy. This visual distance is part of the
   honesty firewall itself (see web/views/broadcast.js's own header
   comment): a glance must tell a reader this is audience entertainment,
   never the measured jury verdict rail underneath a burst. */
.poll {
  margin-bottom: var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--accent-dim);
  border-top: 3px solid var(--accent);
  background: var(--accent-soft);
  display: flex;
  flex-direction: column;
  gap: .55em;
}

.poll__label {
  color: var(--accent);
  font-size: .62rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  font-weight: 600;
}

.poll__archive-link {
  color: var(--ink-faint);
  font-size: .62rem;
  letter-spacing: .12em;
  text-decoration: none;
  align-self: flex-end;
  margin-top: -1.6em;
}
.poll__archive-link:hover { color: var(--accent); }

.poll__disclaimer {
  color: var(--ink-faint);
  font-size: .68rem;
  letter-spacing: .01em;
}

.poll__question {
  color: var(--ink);
  font-size: .95rem;
  font-weight: 600;
}

.poll__buttons { display: flex; flex-wrap: wrap; gap: .6em; }

.poll__phone { color: var(--ink-faint); font-size: .72rem; letter-spacing: .02em; }
.poll__phone a { color: var(--accent); text-decoration: none; }

.poll__btn {
  padding: .35em 1.1em;
  border: 1px solid var(--accent-dim);
  background: var(--bg1);
  color: var(--ink-dim);
  font-family: inherit;
  font-size: .72rem;
  letter-spacing: .04em;
  cursor: pointer;
  text-align: left;
}

.poll__btn:hover:not(:disabled) { color: var(--ink); border-color: var(--accent); }
.poll__btn:disabled { cursor: default; opacity: .6; }

.poll__tally-row { display: flex; align-items: center; gap: .5em; font-size: .78rem; color: var(--ink-dim); }
.poll__tally-label { flex: 0 0 auto; min-width: 6em; }
.poll__tally-bar {
  flex: 1 1 auto;
  height: .5em;
  background: var(--bg1);
  border: 1px solid var(--line-soft);
  position: relative;
  overflow: hidden;
}
.poll__tally-fill { position: absolute; inset: 0; width: 0; background: var(--accent-dim); }
.poll__tally-count { flex: 0 0 auto; color: var(--ink-faint); font-size: .68rem; }

.poll__total { color: var(--ink-faint); font-size: .68rem; }
.poll__your-choice { color: var(--ink-dim); font-size: .68rem; }

/* THE BACKTEST's audience CONFIRMED/BUSTED vote widget (fix round 1,
   controller ruling B) — mirrors .poll's structure above end to end but in
   its OWN namespace with a distinct accent (--info, a cool blue, vs .poll's
   warm --accent amber) — same "visual distance is part of the honesty
   firewall" posture as .poll vs .jury (web/views/broadcast.js's own header
   comment): a glance must tell a reader this is the AUDIENCE's call, never
   the mechanical engine verdict on the case board underneath it. */
.btvote {
  margin-bottom: var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--info);
  border-top: 3px solid var(--info);
  background: rgba(122, 147, 168, 0.1);
  display: flex;
  flex-direction: column;
  gap: .55em;
}

.btvote__label {
  color: var(--info);
  font-size: .62rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  font-weight: 600;
}

.btvote__archive-link {
  color: var(--ink-faint);
  font-size: .62rem;
  letter-spacing: .12em;
  text-decoration: none;
  align-self: flex-end;
  margin-top: -1.6em;
}
.btvote__archive-link:hover { color: var(--info); }

.btvote__disclaimer {
  color: var(--ink-faint);
  font-size: .68rem;
  letter-spacing: .01em;
}

.btvote__question {
  color: var(--ink);
  font-size: .95rem;
  font-weight: 600;
}

.btvote__buttons { display: flex; flex-wrap: wrap; gap: .6em; }

.btvote__btn {
  padding: .35em 1.1em;
  border: 1px solid var(--info);
  background: var(--bg1);
  color: var(--ink-dim);
  font-family: inherit;
  font-size: .72rem;
  letter-spacing: .04em;
  cursor: pointer;
  text-align: left;
}

.btvote__btn:hover:not(:disabled) { color: var(--ink); border-color: var(--info); }
.btvote__btn:disabled { cursor: default; opacity: .6; }

.btvote__tally-row { display: flex; align-items: center; gap: .5em; font-size: .78rem; color: var(--ink-dim); }
.btvote__tally-label { flex: 0 0 auto; min-width: 6em; }
.btvote__tally-bar {
  flex: 1 1 auto;
  height: .5em;
  background: var(--bg1);
  border: 1px solid var(--line-soft);
  position: relative;
  overflow: hidden;
}
.btvote__tally-fill { position: absolute; inset: 0; width: 0; background: var(--info); }
.btvote__tally-count { flex: 0 0 auto; color: var(--ink-faint); font-size: .68rem; }

.btvote__total { color: var(--ink-faint); font-size: .68rem; }
.btvote__your-choice { color: var(--ink-dim); font-size: .68rem; }

/* --------------------------------------------------------- burst chaining */

/* A multi-post burst renders as one evidence folder: this header strip sits
   flush against the group's first card (no gap, no bottom border — the
   card's own top border picks up where the header's leaves off), tagged
   like a case-file divider rather than another card. Member cards below get
   a thin rail down the left edge (.burst-chain) with reticle corners
   suppressed except at the true visual ends (see the __first/__last rules)
   — wire.js's groupPosts()/renderBurstGroup() work out membership and the
   50-post-boundary truncation case. */
.burst-header {
  background: var(--bg2);
  border: 1px solid var(--accent-dim);
  border-bottom: none;
  padding: var(--sp-2) var(--sp-4);
}

.burst-header__row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .3em;
  color: var(--ink-dim);
  font-size: .68rem;
  letter-spacing: .06em;
  text-transform: uppercase;
}

.burst-header__label { color: var(--accent); font-weight: 700; }
.burst-header__sep { color: var(--ink-faint); }

.burst-header__link { color: var(--accent); text-decoration: none; }
.burst-header__link:hover { text-decoration: underline; }

/* Only the top gap needs tightening vs. the base .reaction rule — the header
   strip is already snugger than a full card. */
.burst-header .reaction { margin-top: var(--sp-2); }

.burst-chain { border-left: 2px solid var(--line-soft); }

/* Tight gap only between two consecutive chained cards — the group's last
   rendered member (whether or not it's the burst's true last) keeps the
   normal .card margin so it reads as a clean break from whatever follows. */
.burst-chain:has(+ .burst-chain) { margin-bottom: 2px; }

.burst-chain__first.reticle::after,
.burst-chain__mid.reticle::before,
.burst-chain__mid.reticle::after,
.burst-chain__last.reticle::before {
  content: none;
}

/* ---------------------------------------------------------------- stamps */

.stamp {
  display: inline-block;
  border: 1px solid currentColor;
  padding: .1em .5em;
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .12em;
  line-height: 1.5;
  text-transform: uppercase;
}

.stamp--retruth { color: var(--ink-dim); }
.stamp--developing { color: var(--accent); }
.stamp--gap { color: var(--info); }
.stamp--extended { color: var(--info); }
.stamp--contaminated { color: var(--down); }
.stamp--default { color: var(--ink-dim); }
.stamp--noise { color: var(--ink-faint); margin-left: .5em; }

/* Badge explainers (web/lib.js's EXPLAINERS) — CSS-only tooltip on any
   stamp/pctile badge whose kind has a dictionary entry (those elements pick
   up tabindex="0" + data-explain from lib.js; unknown kinds get neither
   attribute and so never match this selector — no tooltip, exactly the
   pre-existing look). `position: relative` here (not on .stamp/.pctile
   themselves) keeps this scoped to only the badges that actually explain
   themselves. Stamps live in dense chip rows, so the tooltip is an
   absolutely-positioned overlay — it never reflows the row it sits in. */
[data-explain] {
  position: relative;
  cursor: help;
  text-decoration: underline dotted currentColor;
  text-underline-offset: .2em;
}

[data-explain]::after {
  content: attr(data-explain);
  position: absolute;
  left: 50%;
  bottom: calc(100% + .4em);
  transform: translateX(-50%);
  z-index: 40;
  width: max-content;
  max-width: 200px;
  padding: .5em .65em;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--bg1);
  color: var(--ink);
  font-size: .68rem;
  font-weight: 400;
  font-style: normal;
  letter-spacing: normal;
  text-transform: none;
  line-height: 1.35;
  white-space: normal;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .4);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .1s ease;
}

/* Dense stamp/pctile-bearing tables (patterns, archive, dossier windows) sit
   in a horizontally scrollable `overflow-x: auto` wrap (see
   .patterns__table-wrap / .archive__table-wrap / .dossier__windows below) —
   per spec that forces the OTHER axis to clip too, so a badge in the last
   cell of a row would otherwise have its centered tooltip sheared off by
   the wrap's own right edge. Right-anchor the tooltip for last-cell badges
   specifically (the one position that's reliably at the clipping edge)
   rather than trying to guess a table's width in pure CSS. */
tr > :last-child [data-explain]::after {
  left: auto;
  right: 0;
  transform: none;
}

[data-explain]:hover::after,
[data-explain]:focus::after,
[data-explain]:focus-visible::after {
  opacity: 1;
  visibility: visible;
}

/* -------------------------------------------------------------- pctiles */

/* Visual honesty: only a genuinely strong reading gets to look urgent. */
.pctile {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.6em;
  padding: .15em .4em;
  border: 1px solid var(--line);
  background: var(--bg2);
  color: var(--ink-dim);
  font-weight: 700;
  font-size: .72rem;
}

.pctile--muted { color: var(--ink-dim); border-color: var(--line); background: var(--bg2); }
.pctile--amber { color: var(--accent); border-color: var(--accent-dim); background: var(--accent-soft); }
.pctile--hot {
  color: var(--bg0);
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 0 10px var(--accent-glow), 0 0 2px var(--accent-glow);
}
.pctile--none { color: var(--ink-dim); border-style: dashed; background: transparent; }

/* ----------------------------------------------------------------- chips */

.chip {
  display: inline-flex;
  align-items: center;
  gap: .3em;
  padding: .15em .55em;
  border: 1px solid var(--line);
  background: var(--bg2);
  color: var(--ink-dim);
  font-size: .68rem;
  letter-spacing: .04em;
}

.chip--live { color: var(--accent); border-color: var(--accent-dim); }
.reaction__ghead { flex-basis: 100%; color: var(--ink-dim); font-size: 10px; letter-spacing: 1.5px; border-bottom: 1px solid var(--line-soft); padding-bottom: 2px; margin: 6px 0 2px; }
.reaction__row--parked { opacity: 0.55; }
.reaction__pending { color: var(--ink-dim); }

/* PUNDIT BOOTH — display-only AI satire (see pundit-booth spec). Collapsed
   toggle row in the stamp idiom; left/right quote blocks get semantic blue/red
   accent rails. */
.pundit-booth { margin-top: 0.5rem; }
.pundit-booth__toggle {
  background: none; border: none; padding: 0; cursor: pointer;
  font: inherit; font-size: 0.72rem; letter-spacing: 0.08em;
  color: var(--ink-dim);
}
.pundit-booth__toggle:hover { color: var(--accent); }
.pundit-booth__body { margin-top: 0.4rem; display: grid; gap: 0.4rem; }
/* display:grid beats the UA's [hidden]{display:none} — restate it or the
   collapsed booth isn't actually collapsed. */
.pundit-booth__body[hidden] { display: none; }
.pundit { border-left: 2px solid; padding: 0.15rem 0 0.15rem 0.6rem; }
.pundit--left { border-color: #4a7dbd; }
.pundit--right { border-color: #bd4a4a; }
.pundit__label { font-size: 0.68rem; letter-spacing: 0.08em; color: var(--ink-dim); }
.pundit__chip { margin-left: 0.5rem; font-style: italic; text-transform: lowercase; }
.pundit__take { margin: 0.15rem 0 0; font-size: 0.85rem; }

/* ---------------------------------------------------------------- tables */

.table { width: 100%; border-collapse: collapse; font-size: .8rem; }
.table th, .table td { padding: .5em .75em; border-bottom: 1px solid var(--line-soft); text-align: left; }
.table th {
  color: var(--ink-dim);
  font-size: .65rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  font-weight: 600;
}
.table tbody tr:hover { background: var(--bg2); }

/* ------------------------------------------------------------- sparkline */

.sparkline { display: block; width: 100%; height: 2.5rem; }

/* -------------------------------------------------------------- semantic */

.up { color: var(--up); }
.down { color: var(--down); }

/* --------------------------------------------------------------- dossier */

.dossier__head { margin-bottom: var(--sp-4); }

.dossier__panel {
  background: var(--bg1);
  border: 1px solid var(--line);
  padding: var(--sp-4);
  margin-bottom: var(--sp-3);
}

.dossier__panel-title {
  font-size: .72rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: var(--sp-3);
  padding-bottom: var(--sp-2);
  border-bottom: 1px dashed var(--line-soft);
}

.dossier__source-seen {
  margin-top: var(--sp-2);
  color: var(--ink-faint);
  font-size: .68rem;
  letter-spacing: .02em;
}

.dossier__chipset {
  margin-top: var(--sp-2);
  display: flex;
  flex-wrap: wrap;
  gap: .4em;
}

/* LEXICON (l1, rule-based) vs MODEL (llm) topic rows — same chip look, but
   the label chrome keeps the two provenances from ever being confused. */
.dossier__topic-row {
  margin-top: var(--sp-2);
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.dossier__topic-label {
  min-width: 4.5em;
  color: var(--ink-faint);
  font-size: .62rem;
  letter-spacing: .14em;
  text-transform: uppercase;
}

.dossier__fp-grid {
  margin-top: var(--sp-2);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(6rem, 1fr));
  gap: var(--sp-2);
}

.dossier__fp-item {
  display: flex;
  flex-direction: column;
  gap: .15em;
  padding: .4em .5em;
  background: var(--bg2);
  border: 1px solid var(--line);
}

.dossier__fp-label {
  color: var(--ink-faint);
  font-size: .6rem;
  letter-spacing: .08em;
  text-transform: uppercase;
}

.dossier__fp-value { color: var(--ink); font-size: .85rem; font-weight: 700; }

.dossier__meter-row {
  margin-top: var(--sp-2);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: .72rem;
}

.dossier__meter-label {
  min-width: 5.5em;
  color: var(--ink-faint);
  letter-spacing: .08em;
  text-transform: uppercase;
}

.dossier__meter-value { color: var(--ink-dim); }

/* Bipolar (sentiment) meter: a centered zero-tick, fill grows from center
   toward whichever side the sign points — inline background set per-fill
   from --up/--down at render time (see dossier.js), not a CSS modifier. */
.meter--bipolar { position: relative; }
.meter--bipolar .meter__fill { position: absolute; top: 0; bottom: 0; }
.meter__zero { position: absolute; left: 50%; top: 0; bottom: 0; width: 1px; background: var(--line); }

.dossier__summary {
  margin-top: var(--sp-3);
  color: var(--ink-dim);
  font-size: .78rem;
  line-height: 1.5;
}

.dossier__llm-footer {
  margin-top: var(--sp-2);
  color: var(--ink-faint);
  font-size: .62rem;
  letter-spacing: .04em;
}

.dossier__charts { display: flex; flex-direction: column; gap: var(--sp-4); }

.dossier__chart-panel {
  padding-top: var(--sp-3);
  border-top: 1px dashed var(--line-soft);
}

.dossier__chart-panel:first-child { padding-top: 0; border-top: none; }

.dossier__chart-titlerow {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-2);
}

.dossier__chart-title {
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--ink);
}

.dossier__chart { width: 100%; height: 220px; position: relative; }

/* Awaiting-bars banner (Task-7 fix): shown above a chart whose anchor (post
   moment / burst first-member) falls outside current bar coverage — the rules
   and default zoom fail closed in that state, so this stamp+note is the
   honest explanation for an otherwise bare line. */
.dossier__chart-note {
  margin-bottom: var(--sp-2);
  color: var(--ink-dim);
  font-size: .68rem;
}

.dossier__tooltip {
  position: absolute;
  display: none;
  pointer-events: none;
  background: var(--bg2);
  border: 1px solid var(--line);
  padding: .25em .5em;
  font-size: .68rem;
  color: var(--ink);
  white-space: nowrap;
  z-index: 5;
}

.dossier__windows { margin-top: var(--sp-3); }

/* PRE-60M row (Wave 2, Task 1 — Who Moved First): dim, sits above the post
   windows with its own bottom border so it visually reads as "before", not
   just another row in the same series. */
.dossier__pre-row td { color: var(--ink-dim); border-bottom: 1px dashed var(--line); }
.dossier__pre-label { text-transform: uppercase; letter-spacing: .04em; }

.stamp--quarantined { color: var(--down); }
.stamp--final { color: var(--up); }

/* Per-post backlink to the burst dossier (Phase-6 Task 7) — a .stamp is a
   <span> everywhere else in the app; this is the one place it's also an
   <a>, so the link-only chrome (no underline until hover) lives in its own
   modifier rather than touching the shared .stamp rule. */
.stamp-link { text-decoration: none; cursor: pointer; }
.stamp-link:hover { text-decoration: underline; }
.dossier__burst-link { margin-top: var(--sp-2); }

/* -------------------------------------------------------------- patterns */

/* The league table + fingerprint cuts. Controls are tab-style window buttons
   (visual sibling of .nav__tab, but real <button>s — this is a data query,
   not navigation) plus a minN number input. Bars reuse the same honesty-scale
   tiers as .pctile (muted/amber/hot) so a "hot" reading looks identically
   urgent whether it's a badge on the wire or a bar fill here. */
.patterns__controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
}

.patterns__tabs { display: flex; flex-wrap: wrap; gap: .4em; }

.patterns__tab {
  padding: .4em 1em;
  border: 1px solid var(--line);
  background: var(--bg1);
  color: var(--ink-dim);
  font-family: inherit;
  font-size: .72rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  cursor: pointer;
}

.patterns__tab:hover { color: var(--ink); }
.patterns__tab.is-active { color: var(--accent); border-color: var(--accent-dim); background: var(--accent-soft); }

.patterns__minn {
  display: flex;
  align-items: center;
  gap: .5em;
  color: var(--ink-dim);
  font-size: .72rem;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.patterns__minn-input {
  width: 3.5em;
  background: var(--bg1);
  border: 1px solid var(--line);
  color: var(--ink);
  font-family: inherit;
  font-size: .8rem;
  padding: .3em .5em;
}

.patterns__minn-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.patterns__section-title {
  margin-bottom: var(--sp-3);
  color: var(--ink-dim);
  font-size: .72rem;
  letter-spacing: .18em;
  text-transform: uppercase;
}

/* Jury accuracy panel (Phase-8) — sits above the league table, same section
   title chrome as LEAGUE TABLE/FINGERPRINT CUTS; hidden entirely (no DOM node)
   when the payload carries no `jury` block at all (votes feature off). */
.patterns__jury { margin-bottom: var(--sp-5); }
.patterns__jury-note { color: var(--ink-dim); font-size: .8rem; line-height: 1.5; }

.patterns__league { margin-bottom: var(--sp-6); }
.patterns__table-wrap { overflow-x: auto; }

/* Null-band de-emphasis (Wave-1 Task 1) — a row whose observed median falls
   inside the pooled-empirical null band (or is too thin, n<3, to say
   anything about a random draw of that size) reads as noise, not signal:
   reduced opacity plus a small ~CHANCE chip (see .stamp--noise), rather than
   hiding the row outright — still evidence, just weak evidence. */
.patterns__row--noise { opacity: .55; }

/* THE AVERAGE MINUTE (Wave-6 Task 4) — hand-rolled SVG chart, no library.
   .eventstudy__band is the matched-control 5th-95th percentile shading;
   .eventstudy__mean is the real curve on top of it; .eventstudy__zero is the
   dashed zero-return baseline; .eventstudy__t0 marks the post/burst time
   itself. */
.eventstudy__chart-wrap { margin: var(--sp-3) 0; }
.eventstudy__svg { display: block; width: 100%; height: auto; }
.eventstudy__band { fill: var(--accent-soft); stroke: none; }
.eventstudy__mean { fill: none; stroke: var(--accent); stroke-width: 2; }
.eventstudy__zero { stroke: var(--line-soft); stroke-width: 1; stroke-dasharray: 3 3; }
.eventstudy__t0 { stroke: var(--ink-faint); stroke-width: 1; stroke-dasharray: 2 2; }
.eventstudy__tick, .eventstudy__ytick { fill: var(--ink-dim); font-size: 9px; }

/* Wave-6 fix ("the flagship chart names the ticker it won't plot, and why"):
   the SAY-WHY-SPY-IS-MISSING note, sitting directly above the chart (or the
   empty state) whenever the dynamic default had to skip a ruler-flagged
   preferred ticker. --down (the same color .frontrun__flagged's own link
   uses) marks it as a withholding, not an ordinary disclosure line. */
.eventstudy__omitted {
  margin: 0 0 var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-left: 3px solid var(--down);
  background: var(--accent-soft);
  color: var(--ink);
  font-size: .8rem;
  line-height: 1.5;
}

.patterns__bar-cell { display: flex; flex-wrap: wrap; align-items: center; gap: .3em .6em; }

/* Bootstrap 90% CI on a row's own median — small trailing text next to the
   pctile badge, e.g. "P71 [64–77]". */
.patterns__ci { color: var(--ink-faint); font-size: .68rem; letter-spacing: .02em; white-space: nowrap; }

.patterns__bar-track {
  position: relative;
  flex: 1;
  min-width: 5rem;
  height: .55rem;
  background: var(--bg2);
  border: 1px solid var(--line);
}

.patterns__bar-fill { position: absolute; top: 0; left: 0; bottom: 0; }
.patterns__bar-fill--muted { background: var(--ink-faint); }
.patterns__bar-fill--amber { background: var(--accent-dim); }
.patterns__bar-fill--hot { background: var(--accent); box-shadow: 0 0 8px var(--accent-glow); }

.stamp--mapped { color: var(--accent); }

.patterns__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-4);
}

.patterns__cut {
  background: var(--bg1);
  border: 1px solid var(--line);
  padding: var(--sp-3);
  min-width: 0;
}

.patterns__cut-title {
  margin-bottom: var(--sp-2);
  color: var(--ink-dim);
  font-size: .65rem;
  letter-spacing: .12em;
  text-transform: uppercase;
}

.patterns__cut-empty { color: var(--ink-dim); font-size: .72rem; }

.patterns__empty {
  padding: var(--sp-5) 0;
  border-top: 1px dashed var(--line);
  border-bottom: 1px dashed var(--line);
}

.patterns__empty-title {
  color: var(--ink-dim);
  font-size: .9rem;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.patterns__empty-note { margin-top: var(--sp-2); color: var(--ink-dim); font-size: .8rem; line-height: 1.5; }
.patterns__empty-hint { margin-top: var(--sp-2); color: var(--ink-faint); font-size: .72rem; }

/* Sizes summary (Phase-6 Task 7) — a stable, minN-independent header note
   above the league table (see patterns.js's sizesNote()). */
.patterns__sizes-note {
  margin-bottom: var(--sp-4);
  color: var(--ink-dim);
  font-size: .78rem;
  letter-spacing: .02em;
}

.patterns__footer {
  margin-top: var(--sp-5);
  padding-top: var(--sp-3);
  border-top: 1px dashed var(--line-soft);
  color: var(--ink-faint);
  font-size: .66rem;
  line-height: 1.5;
}

/* --------------------------------------------------------- posting rhythm
   THE POSTING RHYTHM — hand-rolled SVG, no chart library, same house rule
   THE AVERAGE MINUTE's own .eventstudy__* rules follow. Wrapped in
   overflow-x: auto (the bar chart can run wide over a multi-year archive);
   the chart's own <svg width>/<svg height> are real pixel dimensions (not
   just a viewBox scaled to fill a container), so the scroll is real. */
/* position: relative so cadenceBarSvg's own .dossier__tooltip (absolutely
   positioned, shared idiom with dossier.js/burst.js) anchors to this wrap
   rather than the page. */
.cadence__chart-wrap { margin: var(--sp-3) 0; overflow-x: auto; position: relative; }
.cadence__bars-svg, .cadence__heatmap-svg { display: block; height: auto; }

.cadence__bar { fill: var(--accent-dim); }
.cadence__bar--max { fill: var(--accent); }
/* NOT var(--down): this section's entire premise is "no market claim" (see
   model/cadence.js's own header) — var(--down) is the site's error/negative
   token (.error, .stamp--wrong, .stamp--contaminated, .frontrun__row--flagged,
   price-down .down), and a reader trained by the rest of the site would read
   this bar as flagged or negative rather than "he posted once that day".
   var(--info) is the token this codebase already reserves for neutral,
   non-market, non-error informational marks (.stamp--gap, .stamp--extended). */
.cadence__bar--min { fill: var(--info); }
/* Today's own bar, if the archive's last materialized day is still in
   progress — dashed outline distinguishes "still counting" from a settled
   day's final count, never a different fill (it is not an extreme). */
.cadence__bar--partial { stroke: var(--ink-dim); stroke-width: 1; stroke-dasharray: 1 1; }

.cadence__callout { fill: var(--ink-dim); font-size: 9px; }

/* Heatmap cells: fill-opacity carries the count (set inline per cell); the
   base fill is constant so opacity alone reads as intensity. Regular-NYSE-
   session cells (Mon-Fri, the hours session.js's own sessionBounds cover)
   get a visible accent outline — the whole point of this grid is to let a
   reader SEE how much output lands where no price can respond, before
   reading a single number below it. */
.cadence__cell { fill: var(--accent); stroke: var(--line-soft); stroke-width: 1; }
.cadence__cell--session { stroke: var(--accent-dim); stroke-width: 1.5; }
.cadence__axis { fill: var(--ink-dim); font-size: 9px; }

/* ----------------------------------------------------------- front-run board
   Wave-3 Task 3: sits above the existing league table, its own section. Rows
   whose q clears the 0.10 significance bar glow with the same accent-glow
   treatment .pctile--hot / .patterns__bar-fill--hot already use — everything
   else stays plain, on purpose (a quiet table is an honest table). */
.frontrun { margin-bottom: var(--sp-6); }

.frontrun__row--sig {
  background: var(--accent-soft);
  box-shadow: inset 3px 0 0 var(--accent), 0 0 8px var(--accent-glow);
}

.stamp--frontrun-sig { color: var(--accent); font-weight: 700; margin-left: .4em; }

/* Wave-3 fix: warn-graded rulers publish no inference. Deliberately styled
   like calib-warn (same --down color) rather than the accent-glow .frontrun__
   row--sig treatment — this is a WARNING about missing evidence, not a
   positive result, and must never be visually confusable with SIG. */
.stamp--frontrun-flagged { color: var(--down); font-weight: 700; }
.frontrun__row--flagged { box-shadow: inset 3px 0 0 var(--down); }
.frontrun__flagged { color: var(--ink-dim); }
.frontrun__flagged a { color: var(--down); }

/* Blocker C1: an UNAUDITED/stale-audit/insufficient-sample row is a
   DIFFERENT claim than a confirmed-bad (flagged) one — "nobody has checked
   this yet" vs "checked, and it's bowed" — so it gets its own visually
   distinct treatment: --info (the same neutral blue-gray gap/extended
   already use), never the --down red reserved for a confirmed finding. Text
   color still lives on .frontrun__flagged (shared with the flagged case);
   only the row's own accent differs. */
.frontrun__row--unaudited { box-shadow: inset 3px 0 0 var(--info); }
.stamp--frontrun-unaudited { color: var(--info); font-weight: 700; }

.frontrun__unmapped { color: var(--ink-faint); font-style: italic; }

.frontrun__insufficient { color: var(--ink-faint); font-style: italic; }

.frontrun__footer {
  margin-top: var(--sp-3);
  padding: var(--sp-3);
  border: 1px dashed var(--line-soft);
  color: var(--ink-dim);
  font-size: .72rem;
  line-height: 1.6;
}

.frontrun__footer-line { margin: 0 0 var(--sp-2); }
.frontrun__footer-line:last-child { margin-bottom: 0; }

.frontrun__disclaimer {
  margin: 0 0 var(--sp-2);
  color: var(--ink);
  font-weight: 700;
}

/* --------------------------------------------------------------- registry
   Wave-4 Task 1: THE REGISTRY, sitting directly below the front-run board.
   In-sample is deliberately muted (grey — "what we saw when we froze");
   out-of-sample is the live, undimmed number — the one column that can't
   have been hand-tuned after the fact. */
.registry { margin-bottom: var(--sp-6); }

.registry__insample { color: var(--ink-faint); }
.registry__outsample { color: var(--ink); font-weight: 600; }

.registry__row--pending { opacity: .7; }
.registry__pending { color: var(--ink-dim); font-style: italic; }

.registry__ruler-ok { color: var(--ink-dim); }

.registry__footer {
  margin-top: var(--sp-3);
  padding: var(--sp-3);
  border: 1px dashed var(--line-soft);
  color: var(--ink-dim);
  font-size: .72rem;
  line-height: 1.6;
}

.registry__footer-line { margin: 0 0 var(--sp-2); }
.registry__footer-line:last-child { margin-bottom: 0; }

/* A broken chain is a hard error, never silently repaired — this banner is
   the loudest thing on the page when it appears, on purpose. */
.registry__broken {
  margin: 0 0 var(--sp-3);
  padding: var(--sp-3);
  border: 2px solid var(--down);
  background: rgba(214, 106, 82, 0.12); /* --down (#d66a52) tinted, same rgba-soft idiom as --accent-soft above */
  color: var(--down);
  font-weight: 700;
  line-height: 1.5;
}

/* ----------------------------------------------------------------- best-of
   Wave-4 Task 3: THE BEST-OF BOARD, the front door for a first-time
   visitor. THE HONESTY CONSTRAINT: hits and misses are EQUAL prominence —
   both columns render through the exact same .best__col/.best__col-title
   classes (see best.js's bestColumn()), so there is nothing here to
   accidentally style one louder than the other. Do not add a
   .best__col-title--hit / --miss modifier pair "just for a splash of
   color" — that is precisely the kind of divergence a reviewer will flag,
   and the shared-function-plus-shared-class design exists so it can't
   happen by accident. */
.best__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
}

.best__col {
  background: var(--bg1);
  border: 1px solid var(--line);
  padding: var(--sp-4);
  min-width: 0;
}

.best__col-title {
  margin-bottom: var(--sp-3);
  color: var(--ink);
  font-size: .95rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  text-align: center;
}

.best__table-wrap { overflow-x: auto; }

.best__row-link { color: var(--accent); text-decoration: none; }
.best__row-link:hover { text-decoration: underline; }

.best__ticker { display: inline-flex; align-items: center; gap: .35em; font-weight: 600; }

.best__chipset { display: flex; flex-wrap: wrap; gap: .3em; }

.best__empty { color: var(--ink-dim); font-size: .8rem; line-height: 1.5; }

.best__footer {
  padding: var(--sp-3);
  border: 1px dashed var(--line-soft);
  color: var(--ink-dim);
  font-size: .72rem;
  line-height: 1.6;
}

.best__footer-line { margin: 0 0 var(--sp-2); }
.best__footer-line:last-child { margin-bottom: 0; }

/* FIX 2 (Wave-4 honesty pass): .best__mobile is the interleaved single-list
   replacement for .best__grid's two stacked columns on narrow screens (see
   best.js's mobileTable()/interleave() for why: a phone visitor stacking
   .best__grid at 1fr used to see every hit row before the first miss row).
   Hidden by default here; shown (and .best__grid hidden) inside the
   `@media (max-width: 720px)` block below — exactly one of the two layouts
   is ever visible at a time, never both. */
.best__mobile { display: none; }

.best__mobile-note {
  margin: 0 0 var(--sp-3);
  color: var(--ink-dim);
  font-size: .72rem;
  line-height: 1.5;
}

/* verdictChip's one shared class (best.js) — same "one class, text is the
   only lever" discipline as .best__col-title, so a hit row and a miss row
   in the interleaved mobile list are never one font-weight/color apart. */
.best__verdict-chip {
  display: inline-block;
  padding: .15em .5em;
  border: 1px solid var(--line);
  background: var(--bg2);
  color: var(--ink-dim);
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .04em;
  white-space: nowrap;
}

/* --------------------------------------------------------------- archive */

/* Search + topic/ticker selects, one flex row that wraps on narrow screens
   (same shape as .patterns__controls — a data-query row, not navigation). */
.archive__controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-5);
}

.archive__search,
.archive__select {
  background: var(--bg1);
  border: 1px solid var(--line);
  color: var(--ink);
  font-family: inherit;
  font-size: .8rem;
  padding: .4em .6em;
}

.archive__search { flex: 1 1 16rem; min-width: 10rem; }
.archive__search:focus-visible,
.archive__select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.archive__table-wrap { overflow-x: auto; }

.archive__snippet { max-width: 28rem; color: var(--ink-dim); overflow-wrap: anywhere; }

.archive__chipset { display: flex; flex-wrap: wrap; gap: .3em; }

.archive__empty {
  padding: var(--sp-5) 0;
  border-top: 1px dashed var(--line);
  border-bottom: 1px dashed var(--line);
}

.archive__empty-title {
  color: var(--ink-dim);
  font-size: .9rem;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.archive__empty-note { margin-top: var(--sp-2); color: var(--ink-dim); font-size: .8rem; line-height: 1.5; }

.archive__load-more {
  display: block;
  margin: var(--sp-5) auto 0;
  padding: .5em 1.4em;
  background: var(--bg1);
  border: 1px solid var(--line);
  color: var(--ink-dim);
  font-family: inherit;
  font-size: .72rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  cursor: pointer;
}

.archive__load-more:hover:not(:disabled) { color: var(--ink); border-color: var(--accent-dim); }
.archive__load-more:disabled { cursor: default; opacity: .6; }
/* display:block above would otherwise defeat the UA's [hidden] rule — restate it. */
.archive__load-more[hidden] { display: none; }

/* B<size> badge (Phase-6 Task 7) — links a row to its burst dossier; sits
   right next to the time link, so it needs its own gap rather than relying
   on the cell's (nonexistent) flex layout. */
.archive__burst-badge { margin-left: .4em; text-decoration: none; color: var(--accent); border-color: var(--accent-dim); }
.archive__burst-badge:hover { text-decoration: underline; }

/* ------------------------------------------------------------ responsive */

/* Mobile pass (Wave 2, Task 3) — ONE consolidated breakpoint. Desktop
   (>720px) must stay pixel-identical to before this task, so every rule
   that touches layout/paint lives in this single block — never a second,
   separate `@media (max-width: 720px)` elsewhere in this file. The only
   non-CSS piece is the wire's chip-row disclosure (`+N MORE`), which gates
   itself on `matchMedia('(max-width: 720px)')` in web/views/wire.js so it
   is a true no-op above this breakpoint (see chipsRow() there); what's
   here is just the chrome that disclosure needs. */
@media (max-width: 720px) {
  /* Belt-and-suspenders against real (if invisible) page-level horizontal
     scroll: [data-explain]::after badge tooltips are absolutely positioned
     at up to 200px wide inside ~28px badges, and nothing between them and
     the document clips horizontally — on a narrow viewport that scrollable
     overflow reaches all the way up to <html>. Everything that's meant to
     scroll sideways already owns its own overflow-x:auto (tables, .tape,
     .nav); this just stops that one pre-existing leak from ever giving the
     whole page a horizontal scrollbar on phones. */
  html, body { overflow-x: hidden; }

  /* -------------------------------------------------- masthead + tape */
  .masthead { flex-direction: column; align-items: flex-start; gap: var(--sp-2); padding: var(--sp-3); }
  .status-line { text-align: left; white-space: normal; cursor: pointer; }
  .wordmark { font-size: .9rem; letter-spacing: .35em; }
  .tape__cell { padding: .4em .8em; font-size: .68rem; }

  /* Not-yet-mirrored media fallback rows: one ellipsized line, not a
     four-line wrapped URL. */
  .media-link {
    display: block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .view { padding: var(--sp-4) var(--sp-3) var(--sp-5); }
  .card__meta { flex-wrap: wrap; }
  .dossier__meter-row { flex-wrap: wrap; }
  .patterns__grid { grid-template-columns: 1fr; }

  /* FIX 2 (Wave-4 honesty pass): below this width, .best__grid's two stacked
     columns (hits fully, THEN misses fully) put the misses column below the
     fold for the phone visitor most subreddit traffic actually is — equal
     CSS weight, unequal scroll position. Swap to the single interleaved
     .best__mobile list instead of merely collapsing the grid to 1fr; see
     best.js's mobileTable()/interleave() and this file's .best__mobile rule
     above. */
  .best__grid { display: none; }
  .best__mobile { display: block; }

  /* --------------------------------------------- fixed bottom nav bar */
  /* Thumb-reachable: the nav (WIRE/PATTERNS/ARCHIVE/BROADCAST) pins to the
     viewport bottom. Body gains matching bottom padding so no content —
     including the site footer, which gets no special-case rule of its own —
     ever renders underneath the fixed bar; it just comes to rest above the
     reserved padding like any other content. */
  body { padding-bottom: 3.5rem; }

  .nav {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 100;
    height: 3.5rem;
    gap: 0;
    padding: 0;
    align-items: stretch;
    justify-content: space-around;
    overflow-x: visible;
    border-bottom: none;
    border-top: 1px solid var(--line);
    box-shadow: 0 -2px 12px rgba(0, 0, 0, .35);
  }

  .nav__tab {
    flex: 1 1 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .35rem;
    padding: 0;
    text-align: center;
    border-bottom: none;
    border-top: 2px solid transparent;
  }

  .nav__tab.is-active { border-bottom-color: transparent; border-top-color: var(--accent); }

  /* 4 primaries + MORE. The three demoted tabs live in the MORE sheet
     (duplicate anchors below the nav in index.html); all seven <a> stay in
     the DOM for desktop and for syncNav()'s is-active bookkeeping. */
  .nav__tab--secondary { display: none; }

  .nav__more {
    display: flex;
    flex: 1 1 0;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    border-top: 2px solid transparent;
    color: var(--ink-dim);
    font: inherit;
    font-size: .72rem;
    letter-spacing: .18em;
    text-transform: uppercase;
    cursor: pointer;
  }
  .nav__more.is-active { color: var(--accent); border-top-color: var(--accent); }
  .nav__more[aria-expanded="true"] { color: var(--ink); }

  .nav__tab, .nav__more {
    font-size: .62rem;
    letter-spacing: .08em;
    padding-left: 2px;
    padding-right: 2px;
  }

  .nav-sheet__backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 99;                       /* below .nav (100), above content */
    background: rgba(0, 0, 0, .55);
  }
  .nav-sheet {
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 3.5rem;                    /* sits on the 3.5rem nav bar */
    z-index: 100;
    background: var(--bg2);
    border-top: 1px solid var(--line);
    box-shadow: 0 -2px 12px rgba(0, 0, 0, .35);
    padding: var(--sp-2) 0;
  }
  .nav-sheet[hidden], .nav-sheet__backdrop[hidden] { display: none; }
  .nav-sheet__link {
    display: block;
    padding: var(--sp-3) var(--sp-4);
    color: var(--ink-dim);
    text-decoration: none;
    font-size: .72rem;
    letter-spacing: .18em;
    text-transform: uppercase;
  }
  .nav-sheet__link.is-active { color: var(--accent); }

  /* ------------------------------------------ wire chip-row disclosure */
  /* The >6-chip collapse logic itself lives in wire.js's chipsRow() (only
     invoked when matchMedia matches this same breakpoint); this is just
     the toggle button + hidden-extra chrome it needs. `display: contents`
     keeps the extra chips as direct flex items of .card__chips once shown,
     so the row still wraps exactly like a flat, uncollapsed row would. */
  .card__chips-extra { display: contents; }
  .card__chips-extra[hidden] { display: none; } /* restate — display:contents above would otherwise beat the UA's [hidden] rule */

  .card__chip-toggle {
    padding: .15em .55em;
    border: 1px dashed var(--line);
    background: none;
    color: var(--accent);
    font-family: inherit;
    font-size: .68rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    cursor: pointer;
  }

  .card__chip-toggle:hover { text-decoration: underline; }

  /* -------------------------------- dense tables: scroll + edge fade */
  /* Horizontal scroll was already the desktop behavior for these four
     (see .patterns__table-wrap/.archive__table-wrap/.best__table-wrap/
     .dossier__windows above) — this only adds a subtle right-edge fade as a
     visible affordance that there's more to scroll to, matching each wrap's
     own surrounding ground color (dossier's panel and best's column are
     both --bg1; the other two sit directly on the view's --bg0). */
  .dossier__windows { display: block; overflow-x: auto; position: relative; }
  .patterns__table-wrap,
  .archive__table-wrap,
  .best__table-wrap {
    position: relative;
  }

  .patterns__table-wrap::after,
  .archive__table-wrap::after,
  .best__table-wrap::after,
  .dossier__windows::after {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 1.5rem;
    pointer-events: none;
  }

  .patterns__table-wrap::after,
  .archive__table-wrap::after { background: linear-gradient(to right, transparent, var(--bg0)); }
  .best__table-wrap::after,
  .dossier__windows::after { background: linear-gradient(to right, transparent, var(--bg1)); }
}

/* --------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {
  .rec-dot { animation: none; opacity: 1; }
  .tape__track { animation: none !important; transform: translateX(0); }
}

/* SITE FOOTER — the self-hosted-AI provenance stamp, on every view. */
.site-footer {
  margin: 2.5rem auto 1.25rem;
  max-width: 44rem;
  padding: 0 1rem;
  text-align: center;
  font-size: 0.72rem;
  line-height: 1.5;
  letter-spacing: 0.04em;
  color: var(--ink-dim);
}
.site-footer__stamp {
  letter-spacing: 0.14em;
  font-weight: 600;
}
.site-footer__sep { margin: 0 0.4em; color: var(--ink-faint); }
.site-footer__link { color: var(--ink-dim); text-decoration: underline; text-underline-offset: 2px; }
.site-footer__link:hover { color: var(--accent); }

/* Subscribe / follow links (Spotify + YouTube) — broadcast tab + footer */
.subscribe { display: inline-flex; gap: 0.6rem; align-items: center; flex-wrap: wrap; vertical-align: middle; }
.subscribe__link {
  display: inline-flex; align-items: center; gap: 0.4rem;
  text-decoration: none; color: var(--ink-dim);
  border: 1px solid var(--ink-dim); border-radius: 4px;
  padding: 0.3rem 0.65rem; font-size: 0.78rem; letter-spacing: 0.06em;
}
.subscribe__link:hover, .subscribe__link:focus-visible { color: var(--accent); border-color: var(--accent); }
.subscribe__link svg { width: 18px; height: 18px; display: block; }
.subscribe__link--compact { border: 0; padding: 0.1rem 0.25rem; }
.broadcast-subscribe {
  display: flex; align-items: center; gap: 0.9rem; flex-wrap: wrap;
  margin-bottom: 1rem; padding-bottom: 0.9rem;
  border-bottom: 1px solid var(--ink-faint);
}
.broadcast-subscribe__label { font-size: 0.68rem; letter-spacing: 0.14em; color: var(--ink-dim); }

/* YOUTUBE — click-to-load facade; no third-party request until clicked. */
.yt-facade {
  margin-top: 0.5rem;
  width: 100%;
  aspect-ratio: 16 / 9;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  border: 1px solid var(--ink-dim);
  cursor: pointer;
  color: var(--ink-dim);
  background: transparent; /* reset native <button> chrome */
  font: inherit;
}
.yt-facade:hover, .yt-facade:focus-visible { color: var(--accent); border-color: var(--accent); }
.yt-facade__play { font-size: 2rem; }
.yt-facade__label { font-size: 0.68rem; letter-spacing: 0.08em; }
.yt-embed {
  margin-top: 0.5rem;
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 0;
}

/* WIRE offline notice — transient banner over a kept-alive stale feed. */
.wire__offline {
  margin: 0 0 0.6rem;
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--down);
  color: var(--down);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
}

/* SILENCE WATCH banner (Task 4) — threshold-gated notice above the wire when
   the current inter-post gap lands at/above its configured percentile.
   Amber accent, not --down: this is an observation, not an outage/alarm —
   same border+soft-fill idiom as .pctile--amber/.patterns__tab.is-active
   elsewhere on the site, just promoted to banner scale. */
.silence-banner {
  margin: 0 0 0.6rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--accent-dim);
  background: var(--accent-soft);
  color: var(--ink);
  font-size: 0.78rem;
  line-height: 1.5;
}

.silence-banner b {
  color: var(--accent);
  letter-spacing: 0.1em;
  margin-right: 0.4em;
}

/* ------------------------------------------------------------- broadcast */

/* BROADCAST tab — 2000s cable-news chyron treatment, adapted to the site's
   own palette (Justin's design direction, Task 6): each segment card opens
   with a banner row bled flush to the card's edges — a solid --down
   "BREAKING" bar in white uppercase text, or a boxed lower-third,
   edition-aware strip in the accent color — "THE CLOSE · <date>" for the
   evening bulletin, "MORNING DESK · <date>" / "MIDDAY DESK · <date>" for
   the intraday editions (MARKET DESK Task 3) — closed off by a
   thin double rule (border-style: double). Below that: a bold headline (the
   script's first sentence) with a right-aligned timestamp block, then the
   player, transcript, satire label, and sources line. Transcript/expand
   reuse the wire's exact .card__body/.card__body--clamp/.card__expand
   idiom (see web/views/wire.js) — this is still "one of the cards", just
   with a news-desk masthead bolted on top; no reticle corners here, since
   the banner already owns the card's top edge. */
.broadcast { display: flex; flex-direction: column; }

.broadcast-card__banner {
  margin: calc(-1 * var(--sp-4)) calc(-1 * var(--sp-4)) var(--sp-3) calc(-1 * var(--sp-4));
  padding: var(--sp-2) var(--sp-4);
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  border-bottom: 3px double var(--line);
}

.broadcast-card__banner--breaking {
  background: var(--down);
  color: #fff;
  border-bottom-color: var(--bg0);
}

.broadcast-card__banner--bulletin {
  background: var(--bg2);
  color: var(--accent);
  border-bottom: 3px double var(--accent-dim);
}

/* MARKET WEATHER (Wave 2, Task 2) — solid accent fill, same treatment as
   BREAKING's solid bar but in the hot accent color instead of --down: it's
   a lower-third stinger, not breaking news, so it must never read as red. */
.broadcast-card__banner--weather {
  background: var(--accent);
  color: var(--bg0);
  border-bottom-color: var(--accent-dim);
}

/* CORRECTION CORNER (Wave-5 Task 2) — deliberately neither the red BREAKING
   bar (a correction is not news, it's the show owning up to a past
   segment) nor the gold weather/bulletin fill: --info is the same slate-blue
   already used elsewhere on the site for "audited/flagged, read the
   disclosure" cues (frontrun's unaudited-row stamp), which is the right
   register for an on-air ombudsman moment. */
.broadcast-card__banner--correction {
  background: var(--info);
  color: var(--bg0);
  border-bottom-color: var(--line);
}

/* THE WEEKLY DOCKET (Wave-5 Task 4) — the Sunday long-form special: its own
   register, distinct from every other lower-third (not breaking, not the
   daily bulletin's gold, not weather's gold, not correction's slate-blue).
   Reuses --up (the site's "moved positively" green) deliberately — the
   week's big write-up reads as an event, not an alarm. */
.broadcast-card__banner--weekly {
  background: var(--up);
  color: var(--bg0);
  border-bottom-color: var(--line);
}

/* TAPE ALERT (tape-alert plan, Task 3) — a measured move with NO story
   attached (no post, no burst). Every fixed hue in the palette is already
   claimed by another chyron's editorial meaning (down=BREAKING,
   accent=WEATHER, info=CORRECTION, up=WEEKLY DOCKET), so rather than reuse
   one of those, this banner takes its color from the move itself — --up/
   --down by the record's own `ret` sign, set inline by bannerNode
   (web/views/broadcast.js) — the same "color follows the measured
   direction" idiom buildBroadcastThumbHtml's move chip already uses
   (src/video/showcard.js). border-bottom-color is left to the base rule's
   default so it always reads against whatever background lands here. */
.broadcast-card__banner--tapealert {
  color: var(--bg0);
}

.broadcast-card__headrow {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-bottom: var(--sp-3);
}

.broadcast-card__headline {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.35;
  color: var(--ink);
}

/* margin-left: auto (not justify-content: space-between on the row) keeps
   this pinned to the right edge even once the headline wraps onto its own
   line — with space-between, a lone item on a wrapped flex line lands at
   the line's start, which would put the "right-aligned timestamp block"
   the design direction calls for on the wrong side. */
.broadcast-card__time {
  flex: 0 0 auto;
  margin-left: auto;
  color: var(--ink-dim);
  font-size: .68rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  text-align: right;
  white-space: nowrap;
}

.broadcast-card__player { margin-bottom: var(--sp-3); }
.broadcast-card__player audio { width: 100%; height: 2.25rem; }
.broadcast-card__player video { width: 100%; max-height: 22rem; background: #000; display: block; }

.broadcast-card__crossfire {
  margin-top: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-left: 2px solid var(--line-soft);
  background: var(--surface-2, transparent);
}

.broadcast-card__crossfire-title {
  margin-bottom: var(--sp-2);
  color: var(--ink-dim);
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
}

.broadcast-card__crossfire-line {
  margin-top: var(--sp-1);
  font-size: .82rem;
  line-height: 1.4;
}

.broadcast-card__crossfire-line strong { letter-spacing: .02em; }
.broadcast-card__crossfire-line--anchor strong { color: var(--ink-dim); }
.broadcast-card__crossfire-line--left strong { color: var(--down, var(--ink-dim)); }
.broadcast-card__crossfire-line--right strong { color: var(--up, var(--ink-dim)); }

.broadcast-card__satire {
  margin-top: var(--sp-3);
  padding-top: var(--sp-2);
  border-top: 1px dashed var(--line-soft);
  color: var(--ink-faint);
  font-size: .68rem;
  font-style: italic;
  letter-spacing: .02em;
}

.broadcast-card__sources {
  margin-top: var(--sp-2);
  color: var(--ink-dim);
  font-size: .72rem;
  letter-spacing: .02em;
}

.broadcast-card__sources a { color: var(--accent); text-decoration: none; }
.broadcast-card__sources a:hover { text-decoration: underline; }

/* --------------------------------------------------------------- shows -- */

.show__masthead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-2);
}

.show__kicker {
  color: var(--accent);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.show__title {
  margin: 0 0 var(--sp-2) 0;
  font-size: 1.15rem;
  line-height: 1.35;
  color: var(--ink);
}

.show__speaker {
  font-weight: 700;
  color: var(--ink-dim);
}

.show__satire {
  margin-top: var(--sp-3);
  padding-top: var(--sp-2);
  border-top: 1px dashed var(--line-soft);
  color: var(--ink-faint);
  font-size: .68rem;
  font-style: italic;
  letter-spacing: .02em;
}

/* ---------------------------------------------------------------- showpage */

/* Per-show explainer page (#/show/<kind>) — premise/host/cadence/rules ahead
   of that show's own episode list (reusing .show__* episodeCard). Same
   tracked-uppercase kicker as .show__kicker, the same .card idiom used
   throughout the site for the host and rules blocks. */
.showpage__kicker {
  margin: 0 0 var(--sp-2);
  color: var(--accent);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.showpage__tagline {
  margin: 0 0 var(--sp-3);
  font-size: 1.3rem;
  line-height: 1.35;
  color: var(--ink);
}

.showpage__premise {
  color: var(--ink);
  font-size: .84rem;
  line-height: 1.7;
  margin: 0 0 var(--sp-4);
}

.showpage__host h3 {
  margin: 0 0 var(--sp-2);
  color: var(--ink-dim);
  font-size: .8rem;
  letter-spacing: .06em;
  text-transform: uppercase;
}

.showpage__bio {
  color: var(--ink-dim);
  font-size: .8rem;
  line-height: 1.6;
  margin: 0 0 var(--sp-2);
}
.showpage__bio:last-child { margin-bottom: 0; }

.showpage__cadence {
  margin: var(--sp-3) 0 var(--sp-4);
  color: var(--ink-faint);
  font-size: .72rem;
  letter-spacing: .02em;
}

.showpage__rules h3 {
  margin: 0 0 var(--sp-2);
  color: var(--ink-dim);
  font-size: .72rem;
  letter-spacing: .12em;
  text-transform: uppercase;
}

.showpage__rules ul {
  margin: 0;
  padding-left: 1.2em;
  color: var(--ink);
  font-size: .8rem;
  line-height: 1.7;
}

.showpage__eps-title {
  margin: var(--sp-5) 0 var(--sp-3);
  color: var(--ink-dim);
  font-size: .72rem;
  letter-spacing: .18em;
  text-transform: uppercase;
}

/* ----------------------------------------------------------------- lineup */

/* THE LINEUP — a directory of all six shows on the SHOWS tab (below the
   episode list). Cards reuse the .card idiom (bg1 panel, hairline border,
   sp-4 pad) but are anchors (link to #/show/<kind>), so hover/focus get the
   accent-border treatment other linked cards use instead of underlined text. */
.lineup__title {
  grid-column: 1 / -1;
  margin: var(--sp-5) 0 var(--sp-3);
  color: var(--ink-dim);
  font-size: .72rem;
  letter-spacing: .18em;
  text-transform: uppercase;
}

.lineup {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--sp-3);
}

.lineup__card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  margin-bottom: 0;
  color: inherit;
  text-decoration: none;
  transition: border-color .15s ease;
}

.lineup__card:hover,
.lineup__card:focus-visible {
  border-color: var(--accent);
}

.lineup__kicker {
  color: var(--accent);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.lineup__tagline {
  color: var(--ink);
  font-size: .82rem;
  line-height: 1.5;
}

.lineup__host {
  color: var(--ink-faint);
  font-size: .72rem;
  letter-spacing: .02em;
}

/* ------------------------------------------------------------------ about */

/* Methodology page — prose-first, no live data, so the chrome stays close
   to plain content: a title in the same tracked-uppercase voice as the
   masthead, section headings matching .patterns__section-title, dashed
   rules between sections (same "case file" idiom as .view-stub), and a
   definition list for the honesty glossary rendered straight off lib.js's
   EXPLAINERS (see web/views/about.js) so it can never drift from the
   on-hover tooltips elsewhere on the site. */
.about__title {
  font-size: 1.1rem;
  letter-spacing: .35em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin: 0 0 var(--sp-4);
}

.about__p {
  color: var(--ink);
  font-size: .82rem;
  line-height: 1.7;
  margin: 0 0 var(--sp-3);
}

.about__section {
  margin-top: var(--sp-6);
  padding-top: var(--sp-4);
  border-top: 1px dashed var(--line);
}

.about__section-title {
  margin: 0 0 var(--sp-3);
  color: var(--ink-dim);
  font-size: .72rem;
  letter-spacing: .18em;
  text-transform: uppercase;
}

.about__dl { margin: var(--sp-3) 0 0; }
.about__dt {
  margin-top: var(--sp-3);
  color: var(--accent);
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.about__dt:first-child { margin-top: 0; }
.about__dd {
  margin: .2em 0 0;
  color: var(--ink-dim);
  font-size: .8rem;
  line-height: 1.6;
}

.about__quote {
  margin: 0 0 var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line);
  color: var(--ink-dim);
  font-size: .72rem;
  font-style: italic;
  letter-spacing: .02em;
  background: var(--bg2);
}

.about__list {
  margin: 0 0 var(--sp-3);
  padding-left: 1.2em;
  color: var(--ink);
  font-size: .82rem;
  line-height: 1.7;
}
.about__list li { margin-bottom: var(--sp-2); }

/* Calibration audit table (Wave-3 Task 2) — same .table/.stamp components
   used sitewide, just scoped colors for the three grade stamps and a small
   muted trailing n=/D= detail next to each. */
.about__table-wrap { overflow-x: auto; margin: 0 0 var(--sp-3); }
.about__calib-detail { margin-left: .4em; color: var(--ink-faint); font-size: .7rem; }
.stamp--calib-ok { color: var(--up); }
.stamp--calib-warn { color: var(--down); }
.stamp--calib-insufficient { color: var(--ink-faint); }
.about__calib-assumptions { margin: 0 0 var(--sp-3); color: var(--ink-dim); font-size: .78rem; }
.about__calib-assumptions summary { cursor: pointer; color: var(--accent); }
.about__calib-assumptions .about__list { margin-top: var(--sp-2); }

/* -------------------------------------------------------------- headlines */

.headlines__note {
  margin: var(--sp-4) 0 var(--sp-4);
  color: var(--ink-dim);
  font-size: .72rem;
  line-height: 1.5;
  letter-spacing: .04em;
  text-transform: uppercase;
}

.headlines { display: flex; flex-direction: column; }

/* .headline reuses the .card look (bg1 panel, hairline border, sp-4 pad). */
.headline { display: flex; flex-direction: column; gap: var(--sp-2); }

.headline__title {
  margin: 0;
  color: var(--ink);
  font-size: .95rem;
  line-height: 1.4;
  font-weight: 700;
}

.headline__detail {
  margin: 0;
  color: var(--ink);
  font-size: .85rem;
  line-height: 1.6;
  overflow-wrap: anywhere;
}

.headline__coincidence {
  margin: 0;
  padding-left: var(--sp-3);
  border-left: 2px solid var(--accent-dim);
  color: var(--ink-dim);
  font-size: .8rem;
  line-height: 1.5;
  font-style: italic;
}

.headline__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-3);
  margin-top: var(--sp-2);
  padding-top: var(--sp-2);
  border-top: 1px dashed var(--line-soft);
  color: var(--ink-dim);
  font-size: .68rem;
  letter-spacing: .06em;
  text-transform: uppercase;
}

.headline__source { color: var(--accent); text-decoration: none; }
.headline__source:hover { text-decoration: underline; }

.headline__tags { display: inline-flex; flex-wrap: wrap; gap: .35em; }

.headline__tag {
  color: var(--ink);
  text-decoration: none;
  padding: .1em .4em;
  border: 1px solid var(--line);
  background: var(--bg2);
  font-weight: 600;
}
.headline__tag:hover { color: var(--accent); border-color: var(--accent-dim); }

.headline__time { margin-left: auto; color: var(--ink-dim); }
