
/* ── VIEW TRANSITIONS + MOTION ──────────────────────────────────────────── */
@view-transition{navigation:auto}
::view-transition-old(root){animation:.2s ease-in both tp-fade-out}
::view-transition-new(root){animation:.2s ease-in both tp-fade-in}
@keyframes tp-fade-out{from{opacity:1}to{opacity:0}}
@keyframes tp-fade-in {from{opacity:0}to{opacity:1}}
@keyframes slideIn{from{opacity:0;transform:translateX(-10px)}to{opacity:1;transform:none}}
@keyframes riseIn {from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){
*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;}}

/* ── DESIGN TOKENS ─────────────────────────────────────────────────────── */
:root {
  --page:  #ffffff;
  --panel: #f0f0f0;
  --ink:   #222222;
  --ink-2: #5b5b5b;
  --ink-3: #8b8b8b;
  --line:  #e4e4e1;
  --rule:  #d7d7d4;
  --hover: #f7f7f6;
  --blue:  #008fd5;
  --red:   #fc4f30;
  --gold:  #e5ae38;
  --green: #6d904f;
  --r-1: 2px;
  --r-2: 3px;
  --serif: 'Fraunces', Georgia, serif;
  --sans:  'Archivo', system-ui, sans-serif;
  --team:  #cfb53b;
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
/* Guaranteed guard for tab/sub-tab panels -- mirrors team_pages/renderer.py's
   inlined [hidden]{display:none}, since some component CSS elsewhere sets
   display on ancestors that could otherwise out-specificity the UA default. */
[hidden] { display: none !important; }
html { scroll-behavior: smooth; scroll-padding-top: 108px; }
body { font-family: var(--sans); font-size: 16px; line-height: 1.5;
       color: var(--ink); background: var(--page);
       -webkit-font-smoothing: antialiased; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration:.001ms !important; transition-duration:.001ms !important; }
}
:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; border-radius: var(--r-1); }

/* ── BREADCRUMB ─────────────────────────────────────────────────────────── */
.tp-breadcrumb {
  max-width: 1140px;
  margin: 0 auto;
  padding: 10px 24px 0;
  font-family: var(--sans);
  font-size: 12px;
  color: var(--ink-3);
}
.tp-breadcrumb a {
  color: var(--ink-2);
  text-decoration: none;
  border-bottom: 1px solid var(--rule);
}
.tp-breadcrumb a:hover { color: var(--ink); border-color: var(--ink-2); }
.tp-breadcrumb__sep { margin: 0 6px; color: var(--ink-3); }
.tp-breadcrumb__current { color: var(--ink-3); }

/* ── IDENTITY BAND (crown + vitals + record bar = one card) ─────────────── */
/* Player-page cohort audit Phase 1 item 6
   (docs/audits/player_page_cohort_audit_2026-08-04/). These three used to
   ship as three separately-chromed blocks: the crown (white, own 4px team
   top border, own bottom border), the vitals strip (grey var(--panel) fill,
   own bottom border) and the record bar (white, own margin) -- three seams
   and a grey band through the middle of what should read as one masthead.
   Worse, only the crown was ever centred: .id-strip had no max-width at all
   and the record bar had neither max-width nor horizontal padding, so its
   receipt cards started at x=0, flush to the viewport edge.
   This wrapper now owns the ONE team ribbon and the ONE bottom seam; each
   sub-row inside it is plain white on the same content column.
   The inline style="--team:...;--team-on:..." lives on THIS element (see
   layout.py::compose + crown.py::crown_team_style), not on .tp-crown --
   custom properties cascade to descendants, so the band has to declare it
   for its own border-top to resolve to anything but the :root gold fallback. */
.tp-idband {
  border-top: 4px solid var(--team);
  border-bottom: 1px solid var(--line);
  background: var(--page);
}
/* Hairline between sub-rows. border-TOP on the following row, never
   border-bottom on the preceding one: the band ships in four real
   configurations (crown alone / +vitals / +record / all three) and a
   trailing border-bottom would double-line against the wrapper's own seam
   whenever that row happened to land last. */
.tp-idband > .id-strip,
.tp-idband > .player-record-bar {
  border-top: 1px solid var(--line);
}
/* The record bar's base rules live in player_pages/record_bar.py's
   RECORD_BAR_CSS, which is also concatenated into reporting.py's legacy
   player bundle (~line 1908) -- so it is re-geometried here, in band context
   only, rather than edited there. Zeroing its margin-bottom matters: left
   alone it leaves 20px of dead white above the band's bottom seam and the
   band stops reading as one card. */
.tp-idband > .player-record-bar {
  max-width: 1140px;
  margin: 0 auto;
  padding: 13px 24px 15px;
}

/* ── PLAYER CROWN ───────────────────────────────────────────────────────── */
/* Horizontal padding sits on __inner, not here, so the crown's content column
   is `max-width: 1140px` + `padding: 0 24px` -- byte-identical to .tp-tabbar
   and .tp-shell below it (and, with the global border-box, to .id-strip and
   .player-record-bar above). The crown used to pad itself and centre a full
   1140px inner inside the padded box, which started its text 24px LEFT of
   the tab bar and every module on the page. */
.tp-crown {
  padding: 22px 0 18px;
}
.tp-crown__inner {
  max-width: 1140px;
  margin: 0 auto;
  padding: 0 24px;
  display: flex;
  align-items: flex-start;
  gap: 18px;
}
.tp-crown__mono {
  width: 68px;
  height: 68px;
  flex-shrink: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-2);
  background: var(--panel);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--serif);
  font-size: 24px;
  font-weight: 600;
  color: var(--ink-2);
  position: relative;
  overflow: hidden;
}
.tp-crown__mono::after {
  content: '';
  position: absolute;
  bottom: 0; left: 0; right: 0;
  height: 3px;
  background: var(--team);
}
.tp-crown__mono--jersey {
  /* Jersey number gets its own numeral face; background/color stay on the
     base .tp-crown__mono rule (panel fill + the ::after ribbon) rather than
     a solid team-color fill -- team color is a 4px accent, never a fill. */
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 18px;
  font-weight: 800;
  letter-spacing: -.02em;
}
.tp-crown__text { flex: 1; min-width: 0; }
.tp-crown__name {
  font-family: var(--serif);
  font-size: clamp(22px, 4vw, 34px);
  font-weight: 600;
  line-height: 1.06;
  color: var(--ink);
  letter-spacing: -.02em;
}
.tp-crown__hook {
  font-family: var(--serif);
  font-size: 15px;
  font-weight: 500;
  color: var(--ink-2);
  line-height: 1.45;
  margin-top: 5px;
  max-width: 64ch;
}
.tp-crown__meta {
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-3);
  margin-top: 5px;
}
.tp-crown__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin-top: 11px;
  align-items: center;
}
@media (max-width: 600px) {
  /* Confirmed live at 390px: .tp-chip / .confidence are white-space:nowrap
     by design (correct for a one-line badge), but a long chip's text (e.g.
     "Returning to Notre Dame for 2026", ~33 chars) can exceed the crown's
     available column width once the 68px jersey badge + gap are subtracted
     from a narrow viewport. With nothing between .tp-crown__chips and body
     constraining width, the chip's un-wrappable content pushes every
     ancestor up to body wider than the viewport -- confirmed as the root
     cause of the sitewide-looking overflow (hero dek, nav, footer all
     visually clipped by body's overflow-x:hidden safety net, despite each
     of THEM being correctly sized on their own). Scoped to the crown chip
     row specifically, not .tp-chip/.confidence globally, since both are
     shared components used correctly nowrap elsewhere on the site. */
  .tp-crown { padding: 18px 0 16px; }
  .tp-crown__inner { padding: 0 16px; }
  .tp-crown__chips .tp-chip,
  .tp-crown__chips .confidence {
    white-space: normal;
    max-width: 100%;
  }
}

/* ── CHIPS ──────────────────────────────────────────────────────────────── */
.tp-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  font-size: 12px;
  font-weight: 600;
  border-radius: var(--r-1);
  border: 1px solid var(--rule);
  background: var(--page);
  color: var(--ink-2);
  white-space: nowrap;
  line-height: 1.6;
}
.tp-chip--score { background: var(--ink); color: #fff; border-color: var(--ink); }
.tp-chip--tier  { background: var(--panel); color: var(--ink-2); }
.tp-chip--up    { background: #ecf3ec; border-color: var(--green); color: #2e6b30; }
.tp-chip--nfl   { background: #ecf3ec; border-color: var(--green); color: #2e6b30; }
.tp-chip--warn  { background: #fdf8ec; border-color: var(--gold); color: #7a5800; }

/* Status chip colors -- mirrors status_strip.py's per-archetype accent exactly
   (green=active/returning, gold=in-flux/transfer, blue=NFL, gray=inactive) so
   the same status never reads as a different color in two places on the site. */
.tp-chip--status-returning_2026,
.tp-chip--status-portal_withdrew    { background: #ecf3ec; border-color: var(--green); color: #2e6b30; }
.tp-chip--status-transferred_college,
.tp-chip--status-portal_open,
.tp-chip--status-hs_recruit_only    { background: #fdf8ec; border-color: var(--gold); color: #7a5800; }
.tp-chip--status-nfl_drafted_2026,
.tp-chip--status-nfl_drafted_prior  { background: #eff6fb; border-color: var(--blue); color: #0a5a86; }
.tp-chip--status-nfl_udfa,
.tp-chip--status-exhausted_eligibility,
.tp-chip--status-medical_retirement,
.tp-chip--status-historical_alum,
.tp-chip--status-unknown            { background: var(--panel); border-color: var(--rule); color: var(--ink-2); }

/* ── MAIN TAB BAR ───────────────────────────────────────────────────────── */
.tp-tabbar-wrap {
  border-bottom: 1px solid var(--line);
  background: var(--page);
  position: sticky;
  top: 56px;
  z-index: 800;
}
.tp-tabbar {
  max-width: 1140px;
  margin: 0 auto;
  display: flex;
  padding: 0 24px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.tp-tabbar::-webkit-scrollbar { display: none; }
.tp-tab {
  appearance: none;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 13px 18px;
  font: 600 14px/1 var(--sans);
  color: var(--ink-2);
  cursor: pointer;
  white-space: nowrap;
  transition: color .12s, border-color .12s;
}
.tp-tab:hover { color: var(--ink); background: var(--hover); }
.tp-tab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--blue); }

/* ── CONTENT SHELL ──────────────────────────────────────────────────────── */
.tp-shell {
  max-width: 1140px;
  margin: 0 auto;
  padding: 28px 24px 80px;
}
@media (max-width: 600px) {
  .tp-shell { padding: 20px 16px 60px; }
}

/* ── STORY SUB-TAB BAR ──────────────────────────────────────────────────── */
.tp-subbar {
  display: flex;
  border-bottom: 1px solid var(--rule);
  margin-bottom: 28px;
  overflow-x: auto;
  scrollbar-width: none;
}
.tp-subbar::-webkit-scrollbar { display: none; }
.tp-subtab {
  appearance: none;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 8px 14px;
  font: 600 13px/1 var(--sans);
  color: var(--ink-3);
  cursor: pointer;
  white-space: nowrap;
  transition: color .12s, border-color .12s;
}
.tp-subtab:hover { color: var(--ink); }
.tp-subtab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--gold); }

/* ── SECTION HEADERS ────────────────────────────────────────────────────── */
.tp-sec-hdr {
  font: 700 11px/1 var(--sans);
  color: var(--ink-3);
  letter-spacing: .05em;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--rule);
  margin-bottom: 18px;
  margin-top: 36px;
}
.tp-sec-hdr:first-child { margin-top: 0; }

/* ── JUMP NAV ───────────────────────────────────────────────────────────── */
.tp-jump-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  border: 1px solid var(--rule);
  border-radius: var(--r-1);
  overflow: hidden;
  width: fit-content;
  margin-bottom: 24px;
}
.tp-jump-nav a {
  display: block;
  padding: 7px 14px;
  font: 600 12px/1 var(--sans);
  color: var(--ink-2);
  border-right: 1px solid var(--rule);
  text-decoration: none;
  background: var(--page);
  white-space: nowrap;
}
.tp-jump-nav a:last-child { border-right: none; }
.tp-jump-nav a:hover { background: var(--panel); color: var(--ink); }

/* ── PROSE / LEDE ───────────────────────────────────────────────────────── */
.tp-lede {
  font-family: var(--serif);
  font-size: 19px;
  font-weight: 500;
  line-height: 1.45;
  color: var(--ink);
  margin-bottom: 18px;
}
/* Essay body. Archivo 18/28 at a 65ch measure -- player-page cohort audit
   2026-08-04, pass 4 §1.1: this shipped at 16px with no measure cap, running
   ~120-130 characters per line at a 1440 viewport, double the doc-90 reading-
   column law. .tp-prose-sm (the style-note prose) is deliberately untouched. */
.tp-prose {
  font-family: var(--sans);
  font-size: 18px;
  line-height: 28px;
  max-width: 65ch;
  text-align: left;
  color: var(--ink);
}
.tp-prose p + p { margin-top: 15px; }
/* Essay heading -- doc-90 h3 (Fraunces 22/28/600). Deliberately NOT .tp-sec-hdr,
   which is the small letterspaced kicker register used for tab sections. */
.tp-essay-hdr {
  font: 600 22px/28px var(--serif);
  color: var(--ink);
  margin-bottom: 14px;
}
.tp-prose-sm { font-size: 14px; line-height: 1.65; color: var(--ink-2); }
.tp-prose-sm p + p { margin-top: 12px; }
/* Scout's excerpt + deep-link stand-in for the full essay (fable_prioritization_
   pass.md §2.1 -- "kill the verbatim duplication on Scout"). Same body register
   as .tp-prose-sm since this is a supporting pointer, not the essay itself. */
.tp-essay-excerpt p { font-size: 14px; line-height: 1.65; color: var(--ink-2); margin-bottom: 8px; }
.tp-essay-excerpt__link { font: 600 13px/1 var(--sans); color: var(--blue); text-decoration: none; }
.tp-essay-excerpt__link:hover { text-decoration: underline; }

/* ── STORY STYLE NOTE ───────────────────────────────────────────────────── */
.tp-style-note {
  border-top: 1px solid var(--rule);
  margin-top: 28px;
  padding-top: 20px;
}
.tp-style-note__hdr {
  font: 700 11px/1 var(--sans);
  color: var(--ink-3);
  letter-spacing: .05em;
  margin-bottom: 12px;
}
.tp-style-note__src {
  margin-top: 10px;
  font: 500 12px/1.5 var(--sans);
  color: var(--ink-3);
}

/* ── SOURCES BLOCK ──────────────────────────────────────────────────────── */
.tp-sources {
  border-top: 1px solid var(--rule);
  margin-top: 20px;
  padding-top: 14px;
}
.tp-sources__hdr { font: 700 11px/1 var(--sans); color: var(--ink-3); letter-spacing: .04em; margin-bottom: 8px; }
.tp-sources__list { list-style: none; display: flex; flex-direction: column; gap: 4px; }
.tp-sources__item { font-size: 12px; color: var(--ink-3); }
.tp-sources__item a { color: var(--ink-2); text-decoration: none; border-bottom: 1px solid var(--rule); }
.tp-sources__item a:hover { color: var(--ink); }

/* ── SIGNATURE GAME CARD ────────────────────────────────────────────────── */
.tp-sig-game {
  border: 1px solid var(--line);
  border-left: 4px solid var(--team);
  background: var(--page);
  border-radius: var(--r-2);
  padding: 16px 20px;
  margin-bottom: 16px;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0 20px;
  align-items: center;
}
.tp-sig-game__label {
  grid-column: 1 / -1;
  font: 700 10px/1 var(--sans);
  color: var(--ink-3);
  letter-spacing: .05em;
  margin-bottom: 10px;
}
.tp-sig-game__score-block {
  text-align: center;
  padding-right: 20px;
  border-right: 1px solid var(--line);
}
.tp-sig-game__result { font: 800 28px/.9 var(--sans); color: var(--green); letter-spacing: -.03em; }
.tp-sig-game__opp-line { font-size: 13px; color: var(--ink-3); margin-top: 4px; }
.tp-sig-game__opp-line a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--rule); }
.tp-sig-game__opp-line a:hover { color: var(--ink); }
.tp-sig-game__line {
  font: 700 15px/1 var(--sans);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  margin-bottom: 7px;
}
.tp-sig-game__note { font-size: 13px; color: var(--ink-2); line-height: 1.5; }
.tp-sig-game__cast { font-size: 12.5px; color: var(--ink-3); line-height: 1.5; margin-top: 6px; }
.tp-sig-game__cast a { color: var(--ink-2); text-decoration: none; border-bottom: 1px solid var(--rule); }
.tp-sig-game__cast a:hover { color: var(--ink); }

/* ── MODULE WRAPPER ─────────────────────────────────────────────────────── */
.tp-mod { margin-bottom: 24px; }
.tp-mod:last-child { margin-bottom: 0; }

/* box_savant renders identically twice per page (Stats tab "2025 snapshot" +
   Scout tab "How he plays" -- both read the same page_data["new_box_savant_html"]
   string, computed once in reporting.py). The Stats-tab instance is where the
   card's own footnote/trend chrome needs to be taught; on Scout it sits beneath a
   take alongside claim cards, where that chrome is redundant. Suppressed here via
   the tab-panel wrapper's id (`_panel()` in layout.py), not a Python change --
   box_savant.py itself must never know it's being rendered inside a specific tab. */
#tpp-scout .box-savant__footnote,
#tpp-scout .box-savant__trend {
  display: none;
}

/* ── SEASON PLACEHOLDER (offseason honest-empty state) ─────────────────── */
.tp-season-empty {
  border: 1px solid var(--line);
  border-radius: var(--r-2);
  background: var(--panel);
  padding: 20px;
  font-size: 14px;
  color: var(--ink-2);
}

/* ── FOOTER ─────────────────────────────────────────────────────────────── */
.tp-foot {
  border-top: 1px solid var(--rule);
  margin-top: 40px;
  padding-top: 20px;
  font-size: 12px;
  color: var(--ink-3);
  line-height: 1.8;
}
.tp-foot a { color: var(--ink-3); }

/* ── IDENTITY STRIP (vitals row of the identity band) ───────────────────── */
/* No background and no border of its own any more: it is a sub-row of
   .tp-idband, which owns the band's single team ribbon and single bottom
   seam, and the grey var(--panel) fill it used to carry read as a separate
   section cutting through the middle of the masthead. The divider above it
   is .tp-idband > .id-strip's border-top. Geometry (max-width 1140 + 24px
   padding, border-box) matches .tp-crown__inner / .tp-tabbar / .tp-shell
   exactly -- it previously had no max-width at all and ran edge-to-edge
   while the crown above it was centred. */
.id-strip {
  max-width: 1140px; margin: 0 auto;
  padding: 9px 24px; display: flex; align-items: center; gap: 0;
}
.id-strip__facts {
  flex: 1; font-size: 12px; font-weight: 500; color: var(--ink-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.id-strip__val { font-weight: 600; color: var(--ink); }
.id-strip__dot { color: var(--ink-3); margin: 0 7px; }
.id-strip__social {
  display: flex; align-items: center; gap: 6px; margin-left: 14px; flex-shrink: 0;
}
.id-soc-icon { display: inline-flex; align-items: center; opacity: 1; transition: opacity .15s; }
.id-soc-icon:hover { opacity: .72; }
.id-strip__via {
  font-size: 10px; color: var(--ink-3); margin-left: 12px; white-space: nowrap; flex-shrink: 0;
}
.id-strip__via a { color: inherit; text-decoration: none; }
.id-strip__via a:hover { text-decoration: underline; }
/* Narrow-viewport rules for the band's lower two rows. Reconciled from the
   old 540px breakpoint onto 600px -- the same one .tp-crown, .tp-crown__chips
   and .tp-shell already use -- so the whole band changes gear at one width
   instead of the crown re-padding at 600 and the strip 60px later. Must sit
   AFTER the .id-strip base rule above (equal specificity, later source wins),
   which is why it is down here rather than folded into the crown's own 600px
   block near the top of this file. */
@media (max-width: 600px) {
  .id-strip { padding: 8px 16px; flex-wrap: wrap; }
  .id-strip__facts { width: 100%; white-space: normal; margin-bottom: 5px; }
  .id-strip__via { display: none; }
  .tp-idband > .player-record-bar { padding: 11px 16px 13px; }
}

/* ── IDENTITY BIO (Scout tab Background section) ─────────────────────────── */
.id-bio { padding: 12px 16px; }
.id-bio p { font-size: 13px; line-height: 1.6; color: var(--ink-2); margin-bottom: 8px; }
.id-bio p:last-of-type { margin-bottom: 0; }
.id-bio__via { font-size: 10px !important; color: var(--ink-3) !important; margin-top: 8px !important; }
.id-bio__via a { color: inherit; text-decoration: none; }
.id-bio__via a:hover { text-decoration: underline; }

/* ── Player-page component registry (audit item 14) ─────────────────────── */
/* Card tiers. Doc 90 §2.7: white surface + mandatory 1px --line hairline,
   --r-2 radius, no shadow. The panel gray is chart-plot fill and never a
   card background -- that ban is what tier 2 encodes. */
.pc-card {
  background: var(--page, #ffffff);
  border: 1px solid var(--line, #e4e4e1);
  border-radius: var(--r-2, 3px);
  padding: 16px 20px;
  margin: 0 0 24px;
}
/* Tier 1 -- "the site stands behind this": 4px accent ribbon. --pc-ribbon is
   injected inline per card (strict hex only); the blue fallback is the
   production accent from the doc-90 palette. */
.pc-card--t1 { border-left: 4px solid var(--pc-ribbon, var(--blue, #008fd5)); }
/* Tier 2 -- "context / derived": white + hairline, NO ribbon, NO fill.
   Quietness is the absence of the ribbon plus the eyebrow-register title
   (P4 §7 disagreement #1 overriding P1 §5.1's gray-panel proposal). */
.pc-card--t2 { padding: 14px 18px; }
.pc-card--t2 .pc-card__title {
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-2, #5b5b5b);
}
/* Tier 3 -- "model opinion / thin sample, labeled": thin box + the chip. */
.pc-card--t3 { padding: 12px 16px; border-color: var(--line, #e4e4e1); }
.pc-card--t3 .pc-card__title {
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink, #222222);
}
.pc-card__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  margin-bottom: 10px;
}
.pc-card__head > .pc-kicker { flex: 1 0 100%; }
.pc-card__title {
  font-family: var(--serif, 'Fraunces', Georgia, serif);
  font-size: 19px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--ink, #222222);
  margin: 0;
}
.pc-card__sub {
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
  font-size: 13px;
  line-height: 18px;
  font-weight: 400;
  color: var(--ink-3, #8b8b8b);
  margin: 0;
}
.pc-card__conf { margin-left: auto; }
.pc-card__body { font-family: var(--sans, 'Archivo', system-ui, sans-serif); }

/* Kicker / eyebrow. Sentence case -- doc 90 bans all-caps tracked labels, so
   there is deliberately no text-transform here. */
.pc-kicker {
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .04em;
  line-height: 1.5;
  color: var(--ink-3, #8b8b8b);
  margin: 0 0 2px;
}
.pc-kicker--accent { color: var(--gold, #e5ae38); }
.pc-kicker--quiet  { color: var(--ink-3, #8b8b8b); opacity: .8; }

/* Stat-hero numeral -- doc 90 data-hero row: Archivo 30-74 / .85, weight 800,
   tabular-nums. Owner ruling 2026-08-06: Archivo, never Fraunces. */
.pc-hero { display: block; margin: 0 0 8px; }
.pc-hero__num {
  display: block;
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
  font-weight: 800;
  line-height: .85;
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--ink, #222222);
}
.pc-hero--sm .pc-hero__num { font-size: 30px; }
.pc-hero--md .pc-hero__num { font-size: 40px; }
.pc-hero--lg .pc-hero__num { font-size: clamp(40px, 4vw, 56px); }
.pc-hero--xl .pc-hero__num { font-size: clamp(48px, 6vw, 74px); }
.pc-hero__label {
  display: block;
  margin-top: 6px;
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-2, #5b5b5b);
}
.pc-hero__sub {
  display: block;
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
  font-size: 12px;
  line-height: 16px;
  color: var(--ink-3, #8b8b8b);
}

/* Receipt card -- pass 3 §5. The quote-tweet contract, flat: 1px border,
   <=3px radius, no shadow, and a mandatory date. */
.pc-receipt {
  border: 1px solid var(--line, #e4e4e1);
  border-radius: var(--r-2, 3px);
  background: var(--page, #ffffff);
  padding: 12px 14px;
  margin: 0 0 12px;
}
.pc-receipt__quote {
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink, #222222);
  margin: 0 0 8px;
  quotes: '\201C' '\201D';
}
.pc-receipt__quote::before { content: open-quote; }
.pc-receipt__quote::after  { content: close-quote; }
.pc-receipt__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
  font-size: 12px;
  color: var(--ink-3, #8b8b8b);
}
.pc-receipt__source { font-weight: 600; color: var(--ink-2, #5b5b5b); }
a.pc-receipt__source { text-decoration: none; }
a.pc-receipt__source:hover { text-decoration: underline; color: var(--ink, #222); }
.pc-receipt__sep { color: var(--ink-3, #8b8b8b); }
.pc-receipt__date { font-variant-numeric: tabular-nums lining-nums; }

/* Field position -- "who's around you" on a rank ladder (part 10).
   A list, not a chart: no fills, no bars, no plot area. The only visual weight
   is on the subject row, and it comes from type weight plus a 2px identity
   accent -- doc 90 keeps team color to a thin accent that carries data, never
   a fill or a section tint. */
.pc-field { margin: 8px 0 0; }
.pc-field__list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--line, #e4e4e1);
}
.pc-field__row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 4px 0 4px 8px;
  border-bottom: 1px solid var(--line, #e4e4e1);
  border-left: 2px solid transparent;
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
  font-size: 13px;
  line-height: 1.45;
  color: var(--ink-2, #5b5b5b);
}
.pc-field__row--subject {
  border-left-color: var(--pc-field-accent, var(--ink, #222222));
  color: var(--ink, #222222);
  font-weight: 700;
}
.pc-field__rank {
  min-width: 3.2em;
  font-weight: 800;
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--ink-3, #8b8b8b);
}
.pc-field__row--subject .pc-field__rank { color: var(--ink, #222222); }
.pc-field__name { flex: 1; min-width: 0; }
.pc-field__name a { color: inherit; text-decoration: none; }
.pc-field__name a:hover { text-decoration: underline; }
.pc-field__you {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--ink-3, #8b8b8b);
}
/* The elision row states the count it swallowed -- a bare ellipsis would hide
   how big the gap is, which is exactly the number a reader is judging. */
.pc-field__gap {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 0 3px 8px;
  border-bottom: 1px solid var(--line, #e4e4e1);
}
.pc-field__gap-rule {
  flex: 1;
  height: 0;
  border-top: 1px dotted var(--line, #e4e4e1);
}
.pc-field__gap-n {
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
  font-size: 11px;
  color: var(--ink-3, #8b8b8b);
  font-variant-numeric: tabular-nums lining-nums;
}
.pc-field__more {
  display: inline-block;
  margin-top: 6px;
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
  font-size: 12px;
  color: var(--blue, #008fd5);
  text-decoration: none;
}
.pc-field__more:hover { text-decoration: underline; }

/* Keep-reading bar -- P4 §5.1. One row, hairline-topped, eyebrow register. */
.pc-keep {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 20px;
  margin-top: 32px;
  padding-top: 14px;
  border-top: 1px solid var(--line, #e4e4e1);
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-2, #5b5b5b);
}
.pc-keep a { color: var(--ink, #222222); text-decoration: none; }
.pc-keep a:hover { text-decoration: underline; }
.pc-keep__next { font-weight: 600; }
.pc-keep__teaser { font-weight: 400; color: var(--ink-2, #5b5b5b); }
.pc-keep__arrow { color: var(--ink-3, #8b8b8b); }
.pc-keep__pagers { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; }
.pc-keep__eyebrow { flex: 1 0 100%; font-size: 11.5px; color: var(--ink-3, #8b8b8b); }
.pc-keep__pager { white-space: nowrap; }
.pc-keep__pager--prev { margin-right: auto; }
.pc-keep__pager--next { margin-left: auto; }
.pc-keep__more { color: var(--ink-3, #8b8b8b); }
.pc-keep__more-label { font-weight: 600; color: var(--ink-2, #5b5b5b); }
@media (max-width: 600px) {
  .pc-keep { flex-direction: column; align-items: flex-start; }
}

/* -- Inline annotation popover (audit item 16) ---------------------------- */
/* Doc 90: radius <=3px, flat, no shadow -- a hairline border does the
   separation work a shadow would elsewhere. Positioned via plain CSS
   (relative wrapper + absolute panel), no JS-computed geometry. */
.pc-pop {
  position: relative;
  display: inline-block;
}
.pc-pop__trigger {
  appearance: none;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
/* "term" trigger -- pass 3 §6: "a subtle inline highlight (thin underline or
   the faintest tint -- flat, no pill)". */
.pc-pop__trigger--term {
  border-bottom: 1px dotted var(--ink-3, #8b8b8b);
}
.pc-pop__trigger--term:hover,
.pc-pop__trigger--term:focus-visible {
  border-bottom-color: var(--ink, #222222);
}
/* "icon" trigger -- a small circular info glyph, not a pill (doc 90 bans
   pills outright; a circle at this size reads as a glyph, not a button). */
.pc-pop__trigger--icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  border: 1px solid var(--ink-3, #8b8b8b);
  color: var(--ink-3, #8b8b8b);
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  vertical-align: super;
}
.pc-pop__trigger--icon:hover,
.pc-pop__trigger--icon:focus-visible {
  border-color: var(--ink, #222222);
  color: var(--ink, #222222);
}
.pc-pop__panel {
  position: absolute;
  /* Above .tp-tabbar-wrap's sticky z-index:800 (player_pages_tabbed/css.py)
     -- the only other stacking context in the player-page bundle a popover
     could visually collide with (e.g. a crown-chip popover opened while the
     page is scrolled enough for the sticky tab bar to sit near the crown). */
  z-index: 900;
  top: calc(100% + 6px);
  left: 0;
  width: max-content;
  max-width: min(300px, 84vw);
  background: var(--page, #ffffff);
  border: 1px solid var(--line, #e4e4e1);
  border-radius: var(--r-2, 3px);
  padding: 12px 14px;
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-2, #5b5b5b);
  text-align: left;
  white-space: normal;
}
.pc-pop__panel[hidden] { display: none; }
/* Cohort strip payload -- one line, no card chrome of its own (it lives
   inside the popover's own bordered panel already). */
.pc-pop__cohort { margin: 0; color: var(--ink, #222222); }
.pc-pop__cohort-pctl { font-weight: 700; }
.pc-pop__cohort-sep { color: var(--ink-3, #8b8b8b); }
.pc-pop__cohort-set { color: var(--ink-3, #8b8b8b); }
/* Glossary payload */
.pc-pop__glossary-term {
  font-family: var(--serif, 'Fraunces', Georgia, serif);
  font-weight: 600;
  font-size: 14px;
  color: var(--ink, #222222);
  margin: 0 0 4px;
}
.pc-pop__glossary-def { margin: 0; color: var(--ink, #222222); }
.pc-pop__glossary-scale { margin: 6px 0 0; color: var(--ink-3, #8b8b8b); font-size: 12px; }
.pc-pop__glossary-link { margin: 8px 0 0; }
.pc-pop__glossary-link a {
  font-weight: 600;
  color: var(--blue, #008fd5);
  text-decoration: none;
}
.pc-pop__glossary-link a:hover { text-decoration: underline; }
/* Receipt-card payload needs nothing extra here -- it ships its own
   .pc-receipt styling from PLAYER_COMPONENTS_CSS; the popover panel is just
   its positioned frame. */
@media (max-width: 600px) {
  .pc-pop__panel { max-width: min(260px, 80vw); }
}

/* Terminal tentpole hero -- item 18. This fragment is the page's one earned
   >=48px numeral moment (S9's display-numeral budget) -- it stays a bespoke,
   module-scoped size rather than joining the shared registry, because the
   whole point is that nothing else on the site looks like this. */
.tp-tentpole-hero {
  font-family: var(--serif, 'Fraunces', Georgia, serif);
  font-size: clamp(34px, 3vw + 20px, 52px);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.015em;
  color: var(--ink, #222222);
  margin: 2px 0 0 0;
}
.teammates-strip {
  margin: 0 0 20px;
  padding-top: 10px;
  border-top: 1px solid var(--line, #e4e4e1);
}
.teammates-strip__eyebrow {
  margin: 0 0 8px;
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--ink-3, #8b8b8b);
}
.teammates-strip__row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
}
.teammates-strip__item {
  display: flex;
  align-items: baseline;
  gap: 6px;
  text-decoration: none;
  color: inherit;
  font-family: var(--sans, 'Archivo', system-ui, sans-serif);
}
.teammates-strip__name {
  font-size: 13px;
  font-weight: 700;
  color: var(--ink, #222222);
}
.teammates-strip__item:hover .teammates-strip__name { text-decoration: underline; }
.teammates-strip__pos {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--ink-3, #8b8b8b);
}
@media (max-width: 480px) {
  .teammates-strip__row { gap: 6px 12px; }
}
/* Player Standing 17-rung Rail */
.player-standing {
  margin: 1rem 0 1.5rem 0;
  padding: clamp(14px, 1.8vw, 20px) clamp(16px, 2.0vw, 24px);
  background: #ffffff;
  border: 1px solid #e4e4e1;
  /* Module ribbon per docs/design-system/100-accent-semantics-contract.md
     (2026-08-06): this card's outer accent is a decorative default, not tied
     to a specific meaning -- blue (verified production), matching the other
     7 production/record modules the same audit flagged. The internal gold
     usage below (tier-label, filled/current/projected rungs) is untouched:
     it's a deliberate, already-documented EARNED-recognition encoding (see
     the Production-track comment further down), which the contract's own
     honors/discourse binding endorses, not a violation of it. */
  border-left: 4px solid #008fd5;
  border-radius: 3px;
  font-variant-numeric: tabular-nums;
}
/* Round 4 (2026-08-07): `.player-standing__head` (a flex row holding the
   eyebrow and a "Tier 5 · Apex" badge) and `.player-standing__tier-label`
   (the badge itself) are both GONE, along with the rule that separated them
   from the headline.
   Why the badge went: the card was naming the same fact three times before
   the reader reached any prose -- "17 rungs" (eyebrow), "Tier 5" (badge),
   "R15" (headline) -- and the badge's tier word was ALSO the rightmost label
   of the axis directly below the rail, so "Apex" was printed twice on one
   card. The axis is the honest home for tier vocabulary (it is a scale
   legend), so the tier read now lives there and only there, carried by
   __tier-marker--current below.
   Why the rule went with it: with the badge deleted the border-bottom was
   separating the eyebrow from its OWN headline. An eyebrow and the headline
   it introduces are one unit; a rule between them reads as a divider between
   two things. The eyebrow is now a plain block sitting directly on the
   headline, which is also the 538/Pudding treatment. */
.player-standing__eyebrow {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: none;
  color: #8b8b8b;
  margin: 0 0 3px 0;
}
.player-standing__rung-label {
  font-family: 'Fraunces', Georgia, serif;
  font-size: clamp(22px, 1.6vw + 8px, 30px);
  letter-spacing: 0.02em;
  color: #222222;
  margin: 0 0 4px 0;
  line-height: 1;
}
.player-standing__story {
  font-family: 'Fraunces', Georgia, serif;
  font-size: 13px;
  font-style: italic;
  line-height: 1.4;
  color: #5b5b5b;
  margin: 0 0 12px 0;
  max-width: 56ch;
}
.player-standing__rail {
  display: flex;
  gap: 3px;
  align-items: stretch;
  margin-top: 8px;
}
.player-standing__rung {
  flex: 1;
  min-width: 0;
  height: 14px;
  border-radius: 2px;
  background: #e4e4e1;
  position: relative;
}
.player-standing__rung--filled {
  background: #e5ae38;
}
.player-standing__rung--current {
  /* Flat fill, not a gradient -- doc 90 SS1.1 forbids gradients outright
     (doc 100 SS4.3 Kind C flagged this exact rule). The "current rung" read
     is carried by the box-shadow ring alone, same as before. */
  background: #e5ae38;
  box-shadow: 0 0 0 1px rgba(229, 174, 56, 0.6);
}
/* Two-track redesign (2026-07-25): a projected/model-elevated rung (Wave 2B
   is_projected) is EARNED-adjacent but not earned -- dashed/hollow instead
   of the solid gold fill above, so "how sure are we" is legible on the rail
   itself instead of a floating "PROJECTED" text badge (retired this pass).
   Cells before it stay solid-filled: only the rung actually in question is
   hollow. */
.player-standing__rung--projected {
  background: transparent;
  border: 1.5px dashed #e5ae38;
}
/* `.player-standing__rung-id` was deleted here 2026-08-07. It styled a
   per-cell R-ordinal caption that this module has never once emitted -- the
   class appears in no render and in no commit in the file's history (checked
   with `git log -S`). The rail's `data-rung-id="R00".."R16"` ATTRIBUTE is a
   different thing and is still emitted, unchanged; only the never-used class
   rule is gone. Zero rendered-output difference. */
/* gap MUST stay 3px to match .player-standing__rail's own gap -- the whole
   alignment argument in the tier_markers builder depends on the two rows
   sharing one gap budget. `justify-content` is gone: with weighted flex the
   markers fill the row exactly, and space-between would have redistributed
   any rounding remainder into the gaps and re-introduced the drift. */
.player-standing__tier-row {
  display: flex;
  margin-top: 18px;
  gap: 3px;
}
/* No `flex: 1` here -- the per-marker `style="flex:N"` inline weight is the
   whole point (see the builder comment). min-width:0 lets a marker narrower
   than its text stay at its true geometric width instead of being pushed
   wide by its own content, which would drag every marker after it out of
   alignment. Overflow spills rather than wraps: a wrapped two-line label
   changes the row height and, at 9px, is less legible than one that simply
   extends past its box. */
.player-standing__tier-marker {
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: none;
  text-align: center;
  color: #8b8b8b;
  white-space: nowrap;
}
/* The current tier -- gold, per doc 100's EARNED binding, the same meaning
   the filled gold rail cells above carry. This one rule is what allows the
   "Tier N · <name>" badge to be deleted without losing the tier read: the
   axis is now simultaneously the scale legend and the you-are-here marker,
   where before those were two separate pieces of UI naming the same fact. */
.player-standing__tier-marker--current {
  color: #e5ae38;
  font-weight: 700;
}
/* Narrow widths. At ~390px a rail cell is ~16px, so a 2-rung tier's marker
   box is ~35px while "Not on team" needs ~52px -- all six labels cannot be
   legible at once, and six colliding 9px gray strings is worse than fewer.
   `visibility` rather than `display`/`width` deliberately: a hidden marker
   KEEPS its box, so the surviving current-tier label stays at exactly the
   x-position it occupies on desktop, still centred over its own tier's
   cells. The scale's range is still readable off the rail itself; what a
   phone reader needs from this row is "which tier am I in", and that is the
   one label left standing. */
/* 2026-08-07: the endpoints survive too. Hiding everything but the current
   marker left a single gold word alone in the row -- an axis with no start and
   no end is not an axis, it is a label, and the reader lost the one thing this
   row exists to give them (where the player sits ALONG a progression). Keeping
   the first and last markers restores both anchors for two more ~30px strings.
   When the current tier IS an endpoint the two rules simply agree. */
@media (max-width: 560px) {
  .player-standing__tier-marker { visibility: hidden; }
  .player-standing__tier-marker--current,
  .player-standing__tier-marker--edge { visibility: visible; }
}
.player-standing--empty {
  color: #8b8b8b;
  font-style: italic;
  font-size: var(--fs-meta, 0.78rem);
  font-family: 'Fraunces', Georgia, serif;
}
/* Two-track redesign (2026-07-25): axis labels above each track ("RECOGNITION"
   above the existing rung rail, "PRODUCTION" above the new percentile bar
   below). Only rendered when both tracks are present -- a lone "Recognition"
   label above the only track is chrome nobody asked for; see
   player_standing__track-label--first below. */
.player-standing__track-label {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.10em;
  color: #8b8b8b;
  margin: 16px 0 6px 0;
}
.player-standing__track-label--first {
  margin-top: 4px;
}
/* Production track (CFB Index Score) -- deliberately BLUE, not gold: gold on
   this component means EARNED recognition (accolades), blue is the sitewide
   convention for a model/computed claim (same reasoning as .player-standing__
   synergy-tag above). A continuous percentile bar, not 17 discrete cells --
   the composite score is a continuous 0-100 number, not an ordinal ladder. */
.player-standing__prod-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.player-standing__prod-bar {
  flex: 1;
  min-width: 0;
}
/* Track/fill/dot markup now comes from the shared theme/percentile_bar.py
   component (render_percentile_track(), Phase 3 item 13 consolidation,
   2026-08-06) -- .player-standing__prod-bar above is now just an
   accessibility wrapper (role="img"/aria-label), not the visual box.
   .player-standing__prod-track uses the same compound-selector pattern as
   box_savant.py's own override: a taller track that always wins over the
   shared component's default height regardless of stylesheet load order
   (two classes on one selector beats one). */
.wcfb-percentile-bar__track.player-standing__prod-track {
  height: 14px;
  background: #e4e4e1;
}
/* This rail is deliberately BLUE, not diverging red/grey/blue: color
   already carries fixed meaning on this component (gold cells above =
   earned recognition, blue = a model/computed claim -- see the module
   docstring's Wave 2B note), and the value here is a continuous CFB Index
   Score, not a peer percentile where "red = bad" is the right read. A
   mid-range score rendering red would misstate that. The 3-component
   selector (class + class + attribute) always outranks the shared
   component's own 2-component data-pct-* rules regardless of load order --
   same technique as the height override above, applied to background. */
.player-standing .wcfb-percentile-bar__fill[data-pct-low="true"],
.player-standing .wcfb-percentile-bar__fill[data-pct-medium="true"],
.player-standing .wcfb-percentile-bar__fill[data-pct-high="true"] {
  background: #008fd5;
}
.player-standing__prod-label {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  font-weight: 600;
  color: #222222;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* Narrow widths (2026-08-07). The label is `white-space: nowrap` beside a
   `flex: 1` bar, so a long summary -- "38 · #382 of 544 safeties" is ~150px --
   takes its space out of the bar, and below ~480px the bar collapses to a
   stub that can no longer show a fill position. Stacking gives the label its
   full width and the bar the row's, and puts the number FIRST (order: -1),
   which is the reading order everywhere else on the site: the value, then the
   scale it sits on. */
@media (max-width: 480px) {
  .player-standing__prod-row {
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
  }
  .player-standing__prod-label {
    order: -1;
    white-space: normal;
  }
}
/* Origin line -- recruiting pedigree vs. current rank. Gray, because it is
   context for the Production number directly above rather than a claim of its
   own (doc 100 binds gray to context). The "Now #N of M" half is ink and
   tabular: it is the same measured fact the bar above draws, and the whole
   point of the line is that the eye can land on it against the pedigree. */
.player-standing__origin {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.5;
  color: #8b8b8b;
  margin: 6px 0 0 0;
  max-width: 62ch;
}
.player-standing__origin-now {
  color: #222222;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
/* Collection callout -- a navigation offer, not a claim, so it takes the
   link blue the rest of the card already uses for its one other outbound
   link (the market's "full market ›") rather than introducing a hue. Sits on
   a hairline rule so it reads as a footer to the card rather than a fifth
   track. */
.player-standing__collection {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.5;
  margin: 12px 0 0 0;
  padding-top: 8px;
  border-top: 1px solid #e4e4e1;
}
.player-standing__collection a {
  color: #008fd5;
  text-decoration: none;
}
.player-standing__collection a:hover { text-decoration: underline; }
/* The model qualifier on a projected headline. Deliberately quiet -- it is a
   caveat on the word beside it, not a second headline -- but it is real text
   in the h3, which is the whole point: the dashed rail cell it accompanies is
   not a disclosure a reader can read. */
.player-standing__rung-qualifier {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 0.5em;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: #8b8b8b;
  vertical-align: 0.35em;
}
/* No-rung headline slot for the partial render (see the `standing_rung` arg
   docstring). Matches .player-standing--empty's voice -- this IS that copy,
   just no longer the only thing on the card. */
.player-standing__no-rung {
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-size: var(--fs-meta, 0.78rem);
  color: #8b8b8b;
  margin: 2px 0 0 0;
}
/* Wave 2B (2026-07-21): synergy note (fable §21) -- "~our read" gap between
   the accolade rung and the archetype profile. Blue, not gold: the whole
   rail is gold for EARNED recognition (filled/current cells above); blue is
   reserved sitewide for a model claim, and this line is exactly that claim
   (see fable Round 5 R4.2C ruling on the ghost/bracket hue). Not the shared
   .ttag trust-tag component (common/components.py) -- its CSS is not wired
   into the player-page bundle (verified 2026-07-21: no player_pages module
   imports render_trust_tag), so a bespoke, self-contained tag avoids an
   unstyled cross-module dependency. */
.player-standing__synergy {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.45;
  color: #5b5b5b;
  margin: 0 0 12px 0;
  max-width: 62ch;
}
.player-standing__synergy-tag {
  display: inline-block;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.03em;
  color: #008fd5;
  margin-right: 3px;
}
.player-standing__synergy-season {
  display: inline-block;
  margin-left: 6px;
  font-size: 11px;
  font-style: italic;
  color: #8b8b8b;
}
/* Gap bracket (fable R6.1 bend #2) -- replaces the illegibly-small ghost-cell
   idea (Round 5 verdict: a 1px dashed outline on a ~17x14px cell is a speck).
   A thin dashed span drawn ABOVE the rung cells, from the earned rung to the
   R09 cap, with a "playing like" label at its far (right) end. Dashed keeps
   the earned(solid)-vs-unearned(dashed) encoding already in play on this
   rail (--filled/--current are solid); blue keeps it in the model's voice,
   never gold (gold already means "earned" on this component). Percentage
   geometry approximates the 17 equal-flex cells + gaps -- an annotation, not
   a pixel-exact chart. */
.player-standing__rail {
  position: relative;
}
.player-standing__rail--has-bracket {
  margin-top: 30px;
}
.player-standing__bracket {
  position: absolute;
  top: -13px;
  height: 8px;
  border-left: 1px dashed #008fd5;
  border-right: 1px dashed #008fd5;
  border-top: 1px dashed #008fd5;
  border-radius: 2px 2px 0 0;
  pointer-events: none;
}
.player-standing__bracket-label {
  position: absolute;
  right: 0;
  top: -17px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  letter-spacing: 0.02em;
  color: #8b8b8b;
  white-space: nowrap;
}

/* ── The Ladder: next-season award outlook (2026-08-06) ──────────────────────
   A THIRD signal on a component that already carries two, so the first design
   question was where to put it without making the other two ambiguous. What is
   already spoken for:
     - solid gold cell        = rung EARNED
     - dashed gold cell       = current rung, model-elevated (is_projected)
     - dashed BLUE bracket    = "playing like" a higher rung (synergy; a claim
                                about how he plays NOW)
     - solid blue percentile  = Production track (CFB Index Score)
   The new signal is none of those: it is a base-rate ODDS estimate that a
   specific future rung gets reached NEXT season.

   Three treatments were built out on paper before this one was chosen:

   (1) Ghost-fill the target cells on the rail, weighted by probability.
       Rejected, and rejected once before -- the fable Round 5 verdict on the
       original ghost-cell idea stands: a rail cell is ~17x14px, and a
       low-opacity fill or 1px dashed outline at that size is a speck, not a
       signal. It would also collide head-on with the dashed-gold "projected"
       cell, which is the same size, the same shape, and means something else.

   (2) A second bracket above the rail, hue-shifted from the synergy bracket.
       Rejected as the worst of the three: two dashed annotated brackets
       drawn in the same 8px band, both anchored at the earned rung, both
       spanning rightward. They would literally overlap for any player who has
       both, and a reader would have no way to learn which dash means "plays
       like a first-teamer" and which means "might be named one". That is
       exactly the conflation this feature must not create.

   (3) A third labeled TRACK below the rail -- chosen. The component already
       established an axis-label-plus-row grammar when the Production track
       landed (2026-07-25), so this reuses a vocabulary the reader has already
       been taught on this exact card rather than inventing a fourth overlay
       idiom. It is legible at any width, it cannot geometrically collide with
       the bracket, and it puts the three claims in three physically separate
       bands: what he EARNED (rail), how he PLAYS (bracket), what is NEXT
       (this). The rail then gets one small, unambiguous companion mark -- see
       __rung-target below.

       This bracket-plus-outlook case is LIVE, not hypothetical -- measured
       2026-08-07: 19 players carry both an ahead_of_recognition synergy bracket
       and a 2026 outlook row, and all 19 were rendered and inspected at 900px
       and 390px. Worth stating because the co-occurrence is easy to conclude is
       impossible and it is not: an earlier check found zero and read that as
       the two features being mutually exclusive. They are not. Sitewide the
       bracket fires for 72 of 11,109 profiled players (0.65%), and the gate is
       `rung < 8` (similarity_engine.standing_vs_profile) -- which overlaps the
       outlook population heavily rather than excluding it, since 554 of a
       600-player outlook sample also sit at rung < 8. The zero was a base-rate
       artifact of the sample size, nothing structural. Do not "simplify" this
       section on the assumption the two never appear together.

   Hue: blue. Per docs/design-system/100-accent-semantics-contract.md this is a
   model claim, not earned recognition, so gold is out -- gold here would read
   as "already his", the single most damaging misread available. Blue is
   already this component's model-claim hue (synergy tag, Production fill), and
   the contract binds hue to MEANING, not to uniqueness: two model-claim
   surfaces sharing blue is the contract working, not a collision. The two are
   still told apart by form -- Production is a solid blue FILL, the outlook is
   blue only on a hollow ring and a single word of label. */
.player-standing__outlook {
  margin-top: 6px;
}
.player-standing__outlook-line {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.5;
  color: #5b5b5b;
  margin: 0 0 1px 0;
}
.player-standing__outlook-tier {
  font-weight: 700;
  color: #008fd5;
}
/* not_in_range is deliberately gray, not red: doc 100 binds red to LOSSES.
   A long-odds award projection is not a loss, it is context. */
.player-standing__outlook-tier--not_in_range { color: #8b8b8b; }
.player-standing__outlook-award { font-weight: 700; color: #222222; }
/* The basis line is its own block now, not a trailing clause on the award
   sentence -- see the outlook_html comment on why the year/conference collision
   needed a line break rather than a shorter sentence. Gray per doc 100: it is
   context for the claim above, not a claim. */
.player-standing__outlook-basis {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.5;
  color: #8b8b8b;
  margin: 0;
  max-width: 62ch;
  font-variant-numeric: tabular-nums;
}
/* "#8 of 16" in ink, as ONE unit. This is the card's only differentiation
   between a dominant returner and a coin-flip, and it is spent here because the
   rank is a measured fact while the four p_estimate values it maps to have
   overlapping intervals (see the _cohort_figure comment). Ink rather than blue:
   the figure is an observation about last season, not a model claim, and doc 100
   binds blue to the claim. */
.player-standing__outlook-figure {
  font-weight: 700;
  color: #222222;
}
/* Realignment note. Inherits the gray of the basis line it now closes, because
   it is the same KIND of thing -- context for the number, not a claim of its
   own, and doc 100 binds gray to context. Deliberately NOT one of the
   meaning-bound hues:
   a conference move is neither a win nor a loss nor a model claim, and coloring
   it would assert a judgement the site has not made. Italic is the only
   differentiation, so the eye separates "where he ranked" from "where he is
   going" without a second color entering a card that already carries two. */
.player-standing__outlook-move {
  font-style: italic;
}
/* The exact percentage lives here and ONLY here -- owner constraint: compute
   the number exactly, show a qualitative tier, keep the number one tap away.
   A <details> rather than a title= tooltip so it is reachable on touch and by
   keyboard; tooltips are neither. */
.player-standing__outlook-detail {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: #8b8b8b;
  margin: 4px 0 0 0;
}
.player-standing__outlook-detail > summary {
  cursor: pointer;
  color: #008fd5;
  font-size: 11.5px;
  list-style: none;
}
.player-standing__outlook-detail > summary::-webkit-details-marker { display: none; }
.player-standing__outlook-detail > summary::after { content: ' ›'; }
.player-standing__outlook-detail[open] > summary::after { content: ' ‹'; }
.player-standing__outlook-detail p {
  margin: 6px 0 0 0;
  line-height: 1.5;
  max-width: 62ch;
  font-variant-numeric: tabular-nums;
}
/* Market line (Heisman) -- the LEDE of the outlook block, not its footnote.
   Kept visually distinct from the curve-derived claim below because its
   PROVENANCE is different (a real sportsbook price, not our base rate), and a
   reader who cannot tell those apart cannot judge either. The rule now sits
   BELOW it rather than above, because it separates a market quote from our
   estimate in that reading order. Only ever rendered when the player is
   actually in the priced field; heisman_trajectory.py owns that gate and this
   module never invents a line.

   SOLID, not dotted, and that is a measured choice rather than a style
   preference: `1px dotted #e4e4e1` computed to a 0.67px dot pattern and was
   invisible in the real 900px and 390px renders, which would have left this
   comment asserting a separation the page did not actually draw. Solid at the
   same token is the treatment __head already uses a few rules up, and that one
   demonstrably renders.

   The percentage is the largest thing in the outlook block and still clearly
   subordinate to the rung headline above (13px vs the headline's clamped
   22-30px Fraunces) -- promoted, not shouting. */
.player-standing__outlook-market {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.45;
  color: #5b5b5b;
  margin: 0 0 8px 0;
  padding-bottom: 8px;
  border-bottom: 1px solid #e4e4e1;
}
.player-standing__outlook-market-tag {
  display: block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.10em;
  color: #008fd5;
  margin-bottom: 1px;
}
/* Wraps so the "full market" link can sit at the far end on a wide card and
   fall under the number on a narrow one, instead of forcing a horizontal
   overflow at 390px. */
.player-standing__outlook-market-body {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0 8px;
}
.player-standing__outlook-market-num {
  font-size: 17px;
  font-weight: 800;
  color: #008fd5;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.player-standing__outlook-market-meta {
  color: #5b5b5b;
  font-variant-numeric: tabular-nums;
}
/* Deep-links to the Scout tab (`?tab=scout`), which owns the full market block
   -- the American price, the book count and the movement chips. Was
   `#accolade-trajectory` until 2026-08-07: that id exists only in reporting.py's
   legacy pre-tabbed player card and appears in 0 of 400 sampled built pages,
   so the link was a no-op on every real page. margin-left:auto parks it at the
   trailing edge when the row fits on one line and collapses harmlessly when it
   wraps. */
.player-standing__outlook-market-more {
  margin-left: auto;
  font-size: 11.5px;
  color: #008fd5;
  text-decoration: none;
  white-space: nowrap;
}
.player-standing__outlook-market-more:hover { text-decoration: underline; }

/* The rail's companion mark: a hollow blue ring inside the TARGET rung cell.
   A ring rather than a fill, an outline rather than a dash, and blue rather
   than gold -- so it is confusable with neither the solid-gold earned cell nor
   the dashed-gold projected cell. Unlike the rejected ghost-fill (1) above, a
   centred ring survives the 17x14px cell because it is a shape at the cell's
   centre rather than a treatment of its edge. Rendered only when the target
   rung is genuinely AHEAD of the earned rung; a player already at or past it
   gets the outlook track without a rail mark, because there is nothing ahead
   of him to point at. */
.player-standing__rung-target {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 6px;
  height: 6px;
  border-radius: 50%;
  border: 1.5px solid #008fd5;
  background: transparent;
  pointer-events: none;
}

/* Box-Score Savant card */
.box-savant {
  margin: 1rem 0 1.5rem 0;
  padding: clamp(16px, 2vw, 22px) clamp(18px, 2.2vw, 26px);
  background: #ffffff;
  border: 1px solid #e4e4e1;
  border-left: 4px solid var(--team-color, #222222);
  border-radius: 3px;
  font-variant-numeric: tabular-nums;
}
.box-savant__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 6px;
  border-bottom: 1px solid #e4e4e1;
  padding-bottom: 8px;
}
.box-savant__title {
  font-family: "Fraunces", serif;
  font-size: 1.05rem;
  font-weight: 600;
  margin: 0;
  color: #222222;
}
.box-savant__subtitle {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 0.78rem;
  color: #8b8b8b;
  margin: 2px 0 0 0;
}
.box-savant__head-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}
.box-savant__meta {
  font-size: 0.74rem;
  color: #8b8b8b;
}
.box-savant__lede {
  font-size: 0.92rem;
  line-height: 1.45;
  color: #5b5b5b;
  margin: 8px 0 14px 0;
}
.box-savant__lede strong { color: #222222; }
.box-savant__band-label {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 0.72rem;
  font-weight: 600;
  color: #8b8b8b;
  margin: 14px 0 8px 0;
}
.box-savant__band-label:first-child { margin-top: 0; }
.box-savant__bars { display: flex; flex-direction: column; gap: 9px; }
.box-savant__bar-row {
  display: grid;
  grid-template-columns: 11.5rem 1fr 4rem;
  gap: 14px;
  align-items: center;
}
.box-savant__bar-label {
  font-size: 0.86rem;
  color: #5b5b5b;
}
.box-savant__bar-label small {
  display: block;
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  color: #8b8b8b;
  margin-top: 1px;
}
/* Track/fill/dot markup itself now comes from the shared
   theme/percentile_bar.py component (render_percentile_track(),
   .wcfb-percentile-bar__track/__fill/__dot — Phase 3 item 13 consolidation,
   2026-08-06). This block only carries box_savant's own modifiers on top of
   that shared element: a taller track + quartile tick marks. The compound
   selectors (two classes required) are deliberate, not decorative — the
   shared component's own `.wcfb-percentile-bar__track { height: 4px }`
   rule ships in the SAME cascade (via _player_pages_v2_css() ->
   cfb-index.<hash>.css, which loads before stats_table.css on both player
   renderers), so a single-class `.box-savant__bar-track { height: 12px }`
   override would depend on stylesheet load order to win. Two classes on
   one selector always outranks one class on specificity, regardless of
   order. */
.wcfb-percentile-bar__track.box-savant__bar-track {
  height: 12px;
  overflow: visible;
}
.box-savant__bar-track::before,
.box-savant__bar-track::after {
  content: "";
  position: absolute;
  top: -3px;
  bottom: -3px;
  width: 1px;
  background: rgba(34,34,34,0.15);
}
.box-savant__bar-track::before { left: 25%; }
.box-savant__bar-track::after  { left: 75%; }
.box-savant__bar-value {
  text-align: right;
  font-weight: 600;
  font-size: 0.88rem;
  color: #222222;
}
.box-savant__bar-value small {
  display: block;
  font-size: 0.64rem;
  letter-spacing: 0.06em;
  color: #8b8b8b;
  font-weight: 500;
}
.box-savant__details {
  margin-top: 4px;
}
.box-savant__details summary {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 0.78rem;
  font-weight: 600;
  color: #5b5b5b;
  cursor: pointer;
  padding: 6px 0;
}
.box-savant__details .box-savant__bars { margin-top: 8px; }
.box-savant__trend {
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid #e4e4e1;
}
.box-savant__trend-title {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 0.72rem;
  font-weight: 600;
  color: #8b8b8b;
  margin: 0 0 8px 0;
}
.box-savant__trend-null {
  display: grid;
  grid-template-columns: 104px 1fr;
  align-items: baseline;
  gap: 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: #8b8b8b;
  padding: 3px 0;
}
.box-savant__trend-null-label { font-weight: 600; color: #222222; }
.box-savant__footnote {
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px solid #e4e4e1;
  font-size: 0.74rem;
  color: #8b8b8b;
  line-height: 1.45;
}
.box-savant--empty {
  color: #8b8b8b;
  font-style: italic;
  padding: 14px;
}
@media (max-width: 640px) {
  .box-savant__bar-row {
    grid-template-columns: 9rem 1fr 3.4rem;
    gap: 10px;
  }
  .box-savant__bar-label { font-size: 0.80rem; }
  .box-savant__bar-value { font-size: 0.80rem; }
  .box-savant__trend-null { grid-template-columns: 1fr; }
}

.player-record-bar {
  margin: 0 0 1.25rem 0;
}
.player-record-bar__eyebrow {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: #8b8b8b;
  margin: 0 0 8px 0;
}
.beat-since-banner {
  display: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  font-weight: 700;
  color: #008fd5;
  background: rgba(0, 143, 213, 0.08);
  border-radius: 3px;
  padding: 5px 10px;
  margin: 0 0 8px 0;
}
.beat-since-banner.visible { display: inline-block; }
.player-record-bar .beat-timeline {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
/* The banner now lives INSIDE .beat-timeline (storylines-since.js scopes its
   lookup to the timeline element -- a preceding sibling was never found, see
   the render_record_bar docstring). That makes it a flex item in the same
   wrapped row as the .beat cards; flex-basis:100% forces it onto its own
   line above them instead of squeezing in beside the first card. */
.player-record-bar .beat-timeline > .beat-since-banner {
  flex-basis: 100%;
}
/* Since-you-looked polish (Phase 3-4 item 19; pass 6 S2, scoping-memo lines
   231-234): "the since-you-looked banner stops being a separate banner --
   it is the Latest line's returning-visitor state. One component, two
   states, not three surfaces." The banner must stay a DOM child of
   .beat-timeline (storylines-since.js's bootThreadTimeline() scopes its
   querySelector('.beat-since-banner') to the timeline element it just
   found -- a sibling is invisible to that lookup; tests/test_record_bar.py
   TestMarkupShape pins the nesting) and the JS's own copy ("N new beats
   since you last looked") is shared with Storylines pages and stays
   untouched -- so the fix here is presentational only: on wide viewports
   the banner floats up to sit level with "The record" eyebrow instead of
   forcing its own full-width row above the beat cards, closing the gap
   between the shipped stacked-row layout and the spec's "one row, not
   three surfaces" intent. Below the site's standard 600px breakpoint it
   reverts to the original stacked block -- pass 6 S1's own mobile note
   ("record bar stays; total chrome above the tab bar must fit one 390px
   screen minus banner") assumes the banner still claims its own row on a
   phone-width screen, so the cutover is deliberate, not an oversight.

   Anchored to .beat-timeline, NOT .player-record-bar: the outer element's
   padding/border are declared a second time, in band context, by
   player_pages_tabbed/css.py's `.tp-idband > .player-record-bar` rule
   (13px/24px there, 11px/16px under its own 600px breakpoint) -- anchoring
   there would need those exact numbers duplicated and re-synced across two
   files every time either changes. .beat-timeline sits inside that padding
   already, so `right: 0` lines up with the real content-column edge (the
   same edge .tp-crown__inner / .tp-tabbar / .id-strip are byte-matched to)
   with no magic numbers, and `bottom: 100%` puts the banner directly above
   the beat cards -- which is exactly the eyebrow's row, since the eyebrow
   is the only other thing above the timeline. */
@media (min-width: 601px) {
  .player-record-bar .beat-timeline {
    position: relative;
  }
  .player-record-bar .beat-timeline > .beat-since-banner.visible {
    position: absolute;
    bottom: 100%;
    right: 0;
    flex-basis: auto;
    margin: 0 0 2px 0;
  }
}
.player-record-bar .beat {
  flex: 1 1 auto;
  min-width: 140px;
  background: #ffffff;
  border: 1px solid #e4e4e1;
  border-radius: 3px;
  padding: 8px 11px;
  font-family: 'Archivo', system-ui, sans-serif;
}
.player-record-bar .beat.is-new {
  border-color: #008fd5;
  background: rgba(0, 143, 213, 0.05);
}
.player-record-bar .beat__label {
  font-size: 12.5px;
  font-weight: 600;
  color: #222222;
  line-height: 1.3;
}
.player-record-bar .beat__meta {
  font-size: 10.5px;
  color: #8b8b8b;
  margin-top: 2px;
}

.fork-card { border: 1px solid rgba(34,34,34,0.14); border-radius: 2px; background: #ffffff; padding: 18px 20px; margin: 24px 0; }
.fork-card__title { font: 600 20px/1.2 'Fraunces', Georgia, serif; color: #222222; margin: 0 0 2px; }
.fork-card__kicker { font: 400 13px/1.4 'Archivo', system-ui, sans-serif; color: #6b6b6b; margin: 0 0 16px; }
.fork-card__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.fork-card__side { min-width: 0; }
.fork-card__label { display: block; font: 700 11px/1 'Archivo', system-ui, sans-serif; letter-spacing: 0.08em; color: #6b6b6b; margin-bottom: 8px; }
.fork-card__claim, .fork-card__outcome { font: 400 15px/1.5 'Archivo', system-ui, sans-serif; color: #222222; margin: 0 0 10px; }
.fork-card__outcome { font-variant-numeric: tabular-nums; }
.fork-card__assertion { font: 400 13px/1.45 'Archivo', system-ui, sans-serif; color: #6b6b6b; margin: 0; font-variant-numeric: tabular-nums; }
.fork-card__verdict { display: inline-block; font: 700 12px/1 'Archivo', system-ui, sans-serif; padding: 5px 10px; border-radius: 2px; letter-spacing: 0.06em; }
.fork-card__verdict.hit { background: rgba(109,144,79,0.15); color: #6d904f; }
.fork-card__verdict.miss { background: rgba(252,79,48,0.15); color: #fc4f30; }
.fork-card__verdict.push { background: rgba(229,174,56,0.15); color: #e5ae38; }
.fork-card__verdict.pending { background: rgba(149,160,173,0.15); color: #6b6b6b; }
.fork-card__accent { height: 4px; border-radius: 0; margin: -18px -20px 16px; background: #008fd5; }
.fork-card__accent.hit { background: #6d904f; }
.fork-card__accent.miss { background: #fc4f30; }
.fork-card__accent.push { background: #e5ae38; }
.fork-card__accent.pending { background: #d5d5d5; }
@media (max-width: 640px) { .fork-card__grid { grid-template-columns: 1fr; gap: 16px; } }
/* Inline fork badge popover payload (item 17b) -- lives inside a
   .pc-pop__panel frame (popover.py), so no border/background of its own. */
.fork-badge-pop { margin: 0; }
.fork-badge-pop__outcome { margin: 0 0 8px; color: #222222; }

.footer{background:var(--panel,#f0f0f0);border-top:1px solid var(--rule,var(--border,#e0e0e0));
  margin-top:40px;font-family:var(--sans,'Archivo',system-ui,sans-serif);color:var(--ink,#222)}
.footer .page{max-width:1200px;margin:0 auto;padding:32px 20px 20px}
.footer .chrome{display:flex;justify-content:space-between;font-size:10px;
  font-weight:700;letter-spacing:.06em;color:var(--ink-3,var(--ink-2,#8b8b8b));
  border-bottom:1px solid var(--rule,var(--border,#e0e0e0));padding-bottom:16px;margin-bottom:24px}
.footer-cols{display:grid;grid-template-columns:repeat(4,1fr);gap:24px;margin-bottom:24px}
@media(max-width:680px){.footer-cols{grid-template-columns:1fr 1fr}}
@media(max-width:400px){.footer-cols{grid-template-columns:1fr}}
.footer-col__heading{font-size:10px;font-weight:700;letter-spacing:.06em;
  color:var(--ink-3,var(--ink-2,#8b8b8b));margin-bottom:10px}
.footer-col ul{list-style:none;margin:0;padding:0}
.footer-col ul li{font-size:12px;color:var(--ink-2,#555);margin-bottom:5px}
.footer-col ul li a{color:var(--ink-2,#555);text-decoration:none}
.footer-col ul li a:hover{color:var(--ink,#222)}
.bottom-chrome{display:flex;justify-content:space-between;font-size:10px;
  color:var(--ink-3,var(--ink-2,#8b8b8b));border-top:1px solid var(--rule,var(--border,#e0e0e0));
  padding-top:14px;letter-spacing:.04em}

/* Discourse Arcs — "What fans are saying" (Phase 6, doc-90 light) */
.da{margin:clamp(20px,3vw,30px) 0;}
.da-head{margin-bottom:14px;}
.da-title{font:600 1.15rem/1.25 'Fraunces',Georgia,serif;color:var(--ink,#222222);margin:0 0 4px;}
.da-honesty{font:400 .78rem/1.4 'Archivo',sans-serif;color:var(--ink-2,#5b5b5b);margin:0;}
.da-cards{display:flex;flex-direction:column;gap:12px;}
.da-card{background:var(--panel,#f0f0f0);border:1px solid var(--line,#e4e4e1);
  border-radius:3px;padding:14px 16px;border-left:2px solid var(--red,#fc4f30);
  scroll-margin-top:52px;}
.da-card.da-media{border-left-color:var(--ink-3,#8b8b8b);}
.da-card:focus-visible{outline:2px solid var(--ink,#222222);outline-offset:2px;}
.da-eyebrow{display:flex;align-items:baseline;justify-content:space-between;
  gap:10px;margin-bottom:8px;flex-wrap:wrap;}
.da-aspect{font:600 .68rem/1 'Archivo',sans-serif;letter-spacing:.04em;
  text-transform:capitalize;color:var(--ink-2,#5b5b5b);}
.da-prevalence{font:600 .72rem/1 'Archivo',sans-serif;color:var(--ink-2,#5b5b5b);
  border-left:1px solid var(--line,#e4e4e1);padding-left:8px;}
.da-headline{font:600 1.05rem/1.3 'Fraunces',Georgia,serif;color:var(--ink,#222222);
  margin:0 0 8px;text-wrap:balance;}
.da-receipt{margin:0 0 10px;padding-left:12px;border-left:2px solid var(--line,#e4e4e1);}
.da-receipt p{font:400 .92rem/1.5 'Archivo',sans-serif;color:var(--ink,#222222);margin:0;}
.da-provenance{font:400 .75rem/1.4 'Archivo',sans-serif;color:var(--ink-2,#5b5b5b);margin:0;}
.da-more{margin-top:10px;}
.da-more summary{font:600 .82rem/1 'Archivo',sans-serif;color:#1a5dad;cursor:pointer;
  list-style:none;padding:6px 0;}
.da-more summary::-webkit-details-marker{display:none;}
.da-more summary:focus-visible{outline:2px solid var(--ink,#222222);outline-offset:2px;}
.da-extra{display:flex;flex-direction:column;gap:12px;margin-top:10px;}
@media(prefers-reduced-motion:reduce){.da-card{transition:none;}}
