/* ============================================================================
   AUTOGRADE SUNDAY — app.css
   The dashboard stylesheet: DESIGN.md sections 3 (tokens, verbatim), 4
   (typography), 5 (layout/grid) and 6 (components).

   Load order in dashboard.html is app.css first, then overlay.css. overlay.css
   owns sections 7 and 9 (chips, team overlay, explainer overlay shell) and
   depends on the tokens defined here — DESIGN.md §2 splits these into four
   files, but this app serves one, so tokens/base/components live here and the
   overlay sheet is left untouched.

   Rules obeyed: nothing is a card, three rule weights, --radius is 0, no
   shadow, no gradient, no filter, no fixed or sticky chrome, no remote asset,
   one code path for prefers-reduced-motion.
   ========================================================================= */

/* ============================================================================
   1. TOKENS — DESIGN.md §3, frozen. Values are load-bearing and were
   contrast-checked against --paper: #0B1110.
   ========================================================================= */

:root {
  color-scheme: light;

  /* ---- Paper & ink -------------------------------------------------------
     Round 3, from scratch: "Day Game." Two rounds of a dark ground still
     read dour no matter how the accent was tuned - Trey asked for brighter
     and friendlier, which a dark theme cannot be. This is a stadium in open
     daylight: soft white concourse, real grass for the one deliberate well
     (the team formation field), and sports-app-bright accent colors instead
     of a single muted spot ink. */
  --paper:          #FAFAF8;  /* page stock - soft daylight white */
  --paper-raised:   #F0F1EB;  /* rail, chips, hover rows */
  --paper-zebra:    #F3F4EE;  /* even table rows */
  --paper-sunk:     #1E7A45;  /* wells: the formation field - real turf green */
  --paper-overlay:  #FFFFFF;  /* dialog panel ground */
  --scrim:          rgba(18, 16, 12, 0.55);

  /* ---- Ink tints ---------------------------------------------------------- */
  --ink:            #16180F;  /* ~18:1  headlines, primary data, scores       */
  --ink-2:          #40433A;  /* ~12:1  serif body, secondary data            */
  --ink-3:          #666A5C;  /*  ~7:1  labels, bylines, timestamps           */
  --ink-4:          #9A9D8E;  /*  2.9:1  DECORATIVE ONLY - never the sole
                                         carrier of a fact. Watermarks, faded
                                         tail of the ticker, the word "AT".    */

  /* ---- Spot inks. Sports-app bright, each one a real, usable color. -------- */
  --amber:          #D9580D;  /* ~5.4:1 live / now / current week / focus      */
  --amber-mark:     #FCE3CE;  /* pale amber wash for a light fill              */
  --flare:          #D1402B;  /*  4.0:1  MARKS AND LARGE TEXT ONLY (>=24px or
                                         >=18.7px semibold). Below AA at agate
                                         size - use --flare-text instead.      */
  --flare-text:     #A8291A;  /* ~7.2:1 loss / denied / 423 / 429 in body or
                                         agate copy                            */
  --flare-mark:     #FBE1DC;
  --turf:           #1E8F4E;  /* ~4.8:1 win / approved / positive delta       */
  --turf-mark:      #DEF3E4;
  --steel:          #1C73B0;  /* ~5.1:1 The Flood division mark - open water   */
  --steel-mark:     #DBEAF5;

  /* Division marks. The Forge (fire) takes amber, The Flood (water) takes
     steel-blue - that pairing was always there in the names, the first two
     color passes just never used it. These appear ONLY on a division
     heading, never on an individual row, tile or crest. */
  --div-forge:      var(--amber);
  --div-flood:      var(--steel);

  /* ---- Rules. Three weights. Never a fourth. ------------------------------ */
  --rule-hair:      rgba(22, 24, 15, 0.14);     /* rows, gutters, chip edges   */
  --rule-mid:       rgba(22, 24, 15, 0.28);     /* section separators         */
  --rule-heavy:     #16180F;                    /* masthead rule only          */
  --rule-w-hair:    1px;
  --rule-w-mid:     1px;
  --rule-w-heavy:   3px;
  --rule-playoff:   2px dashed var(--amber);
  --rule-field:     rgba(255, 255, 255, 0.55);  /* yard lines - chalk on grass */
  --field-ink:      #FFFFFF;                    /* the line of scrimmage - brightest line on the field */

  /* ---- Type families ------------------------------------------------------
     All three self-hosted as latin-subset woff2 (section 4). The fallbacks
     are real faces that exist on Windows / macOS / Linux, in that order. */
  --font-display: "Oswald", "Haettenschweiler", "Franklin Gothic Condensed",
                  "Arial Narrow", "Avenir Next Condensed",
                  "Liberation Sans Narrow", "DejaVu Sans Condensed", sans-serif;
  --font-serif:   "Source Serif 4", "Charter", "Georgia", "Liberation Serif",
                  "Times New Roman", serif;
  --font-mono:    "IBM Plex Mono", ui-monospace, "SFMono-Regular", "Consolas",
                  "DejaVu Sans Mono", "Liberation Mono", monospace;

  /* ---- Type scale. Print-derived, not a modular ramp. A late "fit the whole
     league on one screen" pass (formerly section 14, now deleted) pushed
     every one of these down toward 11-13px. These are the real sizes - a
     night-game broadcast page, not a spreadsheet. --------------------------- */
  --t-masthead:  clamp(2.75rem, 7vw, 4.5rem);    /* league wordmark            */
  --t-deck:      clamp(1.0625rem, 2.4vw, 1.375rem); /* season / week strap     */
  --t-section:   1.75rem;                         /* STANDINGS, MATCHUPS, ...  */
  --t-kicker:    0.8125rem;                       /* label above a section     */
  --t-headline:  1.1875rem;                       /* team name in a row/tile   */
  --t-body:      1.0625rem;                       /* serif prose               */
  --t-agate:     0.9375rem;                       /* tables. 15px floor.       */
  --t-agate-sm:  0.8125rem;                       /* ticker, footnotes, tags   */
  --t-score:     clamp(2rem, 5vw, 3rem);          /* the number that matters   */
  --t-chip:      0.875rem;                        /* player name in a chip     */
  --t-chip-meta: 0.75rem;                         /* POS - NFL in a chip       */

  --lh-tight:    1.1;
  --lh-agate:    1.45;
  --lh-body:     1.65;
  --ls-kicker:   0.14em;
  --ls-display:  0.005em;
  --ls-agate:    0.01em;
  --ls-tag:      0.08em;

  /* ---- Space. 4px base, exposed as a scale. ------------------------------- */
  --s-1: 4px;   --s-2: 8px;   --s-3: 12px;  --s-4: 16px;
  --s-5: 24px;  --s-6: 32px;  --s-7: 48px;  --s-8: 64px;  --s-9: 96px;

  /* ---- Grid & measure -----------------------------------------------------
     --page-max back to a readable 1200px - the 1880px "deck" width asked a
     human eye to track three columns of agate across a 25-inch monitor. */
  --page-max:    1200px;
  --gutter:      clamp(16px, 2.5vw, 28px);
  --rail-w:      232px;   /* channel rail   */
  --ticker-w:    320px;   /* transactions rail, >=1240px only */
  --measure:     70ch;    /* serif prose never exceeds this */
  --pad-block:   clamp(32px, 6vw, 64px);   /* vertical padding per region */
  --row-h:       48px;    /* agate table row. Was crushed to 26px in the deck. */
  --radius:      0;       /* deliberate. Nothing is rounded. Ever. */

  /* ---- Crest sizes -------------------------------------------------------- */
  --crest-xs:    24px;   /* board avatar, ticker */
  --crest-sm:    38px;   /* standings row */
  --crest-md:    64px;   /* matchup */
  --crest-lg:    120px;  /* team tile */
  --crest-xl:    80px;   /* overlay header */

  /* ---- Focus. Visible, amber, never removed. ------------------------------ */
  --focus-ring:   2px solid var(--amber);
  --focus-offset: 2px;

  /* ---- Motion ------------------------------------------------------------- */
  --ease-out:      cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-inout:    cubic-bezier(0.65, 0.05, 0.36, 1);
  --dur-fast:      140ms;   /* hover, tag, scrim */
  --dur-mid:       220ms;   /* a single chip settling */
  --dur-overlay:   420ms;   /* the crest FLIP */
  --dur-draw:      300ms;   /* chalk line draw */
  --stagger:       20ms;    /* between ranks in the overlay */
  --unit-beat:     12ms;    /* extra gap between units */
  --unit-hold:     70ms;    /* the silence between offense and defense */
  --chip-start:   300ms;    /* when the first chip begins */

  /* ---- Layers ------------------------------------------------------------- */
  --z-sticky:  10;
  --z-scrim:   90;
  --z-overlay: 100;
}

/* Agate gets bigger and the rules get stronger on small screens. This is the
   pre-applied mitigation for the direction's known failure mode. Do not
   reintroduce backgrounds or borders to solve small-screen legibility. */
@media (max-width: 719.98px) {
  :root {
    --t-agate:    1rem;       /* 16px */
    --t-agate-sm: 0.875rem;   /* 14px */
    --row-h:      52px;
    --rule-hair:  rgba(22, 24, 15, 0.20);
    --crest-lg:   96px;
  }
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-fast: 1ms; --dur-mid: 1ms; --dur-overlay: 1ms; --dur-draw: 1ms;
    --stagger: 0ms;  --unit-beat: 0ms; --unit-hold: 0ms; --chip-start: 0ms;
  }
}

/* ---- Global digit rule. Rule 4. ------------------------------------------ */
.num, table, time, .score, .record, .countdown, .timecode {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums lining-nums slashed-zero;
  font-feature-settings: "tnum" 1, "zero" 1;
  letter-spacing: 0;
}

/* ---- Focus. Applies to everything focusable. ----------------------------- */
:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

/* ============================================================================
   2. FONTS — DESIGN.md §4.1. Self-hosted latin-subset woff2, no CDN, no
   remote @import. font-display:swap means a missing file degrades to the
   fallback stack instead of blocking; the page is fully correct with zero
   font files present.
   ========================================================================= */

/* ---------------------------------------------------------------------------
   SELF-HOSTED, latin-subset woff2, fetched from each family's own open-license
   (SIL OFL) release and committed to app/static/fonts/. No CDN, no runtime
   network fetch - this is the whole six-file set the design always called for.
   --------------------------------------------------------------------------- */
@font-face { font-family:"Oswald"; font-style:normal; font-weight:500;
  font-display:swap; src:url("/static/fonts/oswald-500.woff2") format("woff2"); }
@font-face { font-family:"Oswald"; font-style:normal; font-weight:600;
  font-display:swap; src:url("/static/fonts/oswald-600.woff2") format("woff2"); }
@font-face { font-family:"Source Serif 4"; font-style:normal; font-weight:400;
  font-display:swap; src:url("/static/fonts/source-serif-4-400.woff2") format("woff2"); }
@font-face { font-family:"Source Serif 4"; font-style:italic; font-weight:400;
  font-display:swap; src:url("/static/fonts/source-serif-4-400-italic.woff2") format("woff2"); }
@font-face { font-family:"IBM Plex Mono"; font-style:normal; font-weight:400;
  font-display:swap; src:url("/static/fonts/ibm-plex-mono-400.woff2") format("woff2"); }
@font-face { font-family:"IBM Plex Mono"; font-style:normal; font-weight:600;
  font-display:swap; src:url("/static/fonts/ibm-plex-mono-600.woff2") format("woff2"); }

/* ============================================================================
   3. BASE — reset, landmarks, typography defaults
   ========================================================================= */

*, *::before, *::after { box-sizing: border-box; }

html {
  /* §5.2: the body never scrolls sideways. Enforced structurally. */
  overflow-x: clip;
  -webkit-text-size-adjust: 100%;
}

body {
  overflow-x: clip;
  margin: 0;
  min-height: 100%;
  background: var(--paper);
  color: var(--ink-2);
  font-family: var(--font-serif);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p, figure, blockquote { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }

img, svg { display: block; max-width: 100%; }
img { height: auto; }

a { color: var(--ink); text-decoration-color: var(--rule-mid); }
a:hover { text-decoration-color: var(--amber); }

button { font: inherit; color: inherit; }

/* Screen-reader-only text. Used by the standings crest header cell and the
   playoff-line row. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Skip link. Absolute, never fixed (prohibition 12). --paper is the page
   ground, used here as a knockout so the link does not sit on top of the
   wordmark — the same technique the playoff-line label uses. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: var(--z-sticky);
  font-family: var(--font-mono);
  font-size: var(--t-agate-sm);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--amber);
  background: var(--paper);
  padding: var(--s-3) var(--s-4);
  text-decoration: none;
}
.skip-link:focus,
.skip-link:focus-visible { left: 0; }

/* ---- Page shell, regions ------------------------------------------------- */

.page {
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.region { padding-block: var(--pad-block); }
.region + .region { border-top: var(--rule-w-hair) solid var(--rule-hair); }

/* .board-wrap is not itself a .region, so the region separator has to be
   restated across it in both directions. */
.region + .board-wrap > .region:first-child,
.board-wrap + .region {
  border-top: var(--rule-w-hair) solid var(--rule-hair);
}

/* ---- The three-part section header (§4.3) -------------------------------- */

.section { margin: 0 0 var(--s-4); }
.section .kicker {
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--t-kicker);
  letter-spacing: var(--ls-kicker);
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: var(--s-1);
}
.section .head {
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--t-section);
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  line-height: var(--lh-tight);
  color: var(--ink);
  padding-bottom: var(--s-2);
  border-bottom: var(--rule-w-mid) solid var(--rule-mid);
}

/* ---- Shared small parts -------------------------------------------------- */

.region-note {
  font-family: var(--font-serif);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  color: var(--ink-3);
  max-width: var(--measure);
  margin-top: var(--s-4);
}
.section + .region-note { margin-top: 0; margin-bottom: var(--s-5); }

.empty {
  font-family: var(--font-mono);
  font-size: var(--t-agate);
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--ink-3);
  padding-block: var(--s-5);
}

.sep { color: var(--ink-4); padding-inline: var(--s-1); }

/* ---- The only permitted horizontal scroller (§5.2) ----------------------- */

.scroller {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--rule-mid) transparent;
}
.scroller > table { min-width: 520px; }

/* ============================================================================
   4. MASTHEAD — DESIGN.md §6.1
   ========================================================================= */

.masthead { padding-block: var(--s-7) var(--s-4); }

.masthead h1 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--t-masthead);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  color: var(--ink);
  margin: 0 0 var(--s-3);
  overflow-wrap: break-word;
}

.masthead .rule {
  height: var(--rule-w-heavy);
  background: var(--rule-heavy);
}

/* The strap and the explainer trigger share one line, and wrap onto two
   below ~520px rather than shrinking. */
.masthead .strap-row {
  margin-top: var(--s-3);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3) var(--s-4);
}

.masthead .strap {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--t-agate);
  line-height: var(--lh-agate);
  text-transform: uppercase;
  letter-spacing: var(--ls-agate);
  color: var(--ink-3);
}
.masthead .strap-part { white-space: nowrap; }

.masthead .strap .countdown {
  color: var(--amber);
  white-space: nowrap;
  padding-left: var(--s-2);
}
/* Window open: the same 6px amber square the LIVE tag uses. */
.masthead .strap .countdown--open {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
}
.masthead .strap .countdown--open::before {
  content: "";
  width: 6px;
  height: 6px;
  background: var(--amber);
  flex: none;
}

/* ---- The explainer trigger ----------------------------------------------
   Not a card and not a pill: a mono label over a hairline rule, which goes
   amber on hover and focus. 44px minimum target (§8). */
.explain-btn {
  appearance: none;
  background: none;
  border: 0;
  border-bottom: var(--rule-w-hair) solid var(--rule-hair);
  padding: var(--s-2) 0;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--t-agate-sm);
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--ink-3);
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-out),
              border-bottom-color var(--dur-fast) var(--ease-out);
}
.explain-btn:hover,
.explain-btn:focus-visible {
  color: var(--amber);
  border-bottom-color: var(--amber);
}

/* ============================================================================
   5. STANDINGS — DESIGN.md §6.2. The hero region.
   ========================================================================= */

.standings-grid {
  display: grid;
  gap: var(--s-7);
  grid-template-columns: 1fr;
}
/* xl only. Two eight-column agate tables do not fit comfortably below 1240px;
   the honest answer is to stack them. */
@media (min-width: 1240px) {
  .standings-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-6); }
}

.standings caption {
  text-align: left;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--t-section);
  text-transform: uppercase;
  letter-spacing: var(--ls-display);
  color: var(--ink);
  padding: 0 0 var(--s-2) var(--s-3);
  border-left: 3px solid var(--div-mark, var(--ink-3));
  margin-bottom: var(--s-2);
}
.standings--forge { --div-mark: var(--div-forge); }
.standings--flood { --div-mark: var(--div-flood); }

.standings table {
  width: 100%;
  border-collapse: collapse;
}

.standings thead th {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--t-agate-sm);
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--ink-3);
  text-align: right;
  padding: var(--s-2) var(--s-2);
  border-bottom: var(--rule-w-mid) solid var(--rule-mid);
  white-space: nowrap;
}
.standings thead th:nth-child(-n+3) { text-align: left; }

/* The team cell is a <th scope="row">, so body cells are matched as both. */
.standings tbody td,
.standings tbody th {
  height: var(--row-h);
  padding: 0 var(--s-2);
  text-align: right;
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: var(--t-agate);
  line-height: var(--lh-agate);
  color: var(--ink-2);
  border-bottom: var(--rule-w-hair) solid var(--rule-hair);
}
.standings tbody td:nth-child(-n+3),
.standings tbody th:nth-child(-n+3) { text-align: left; }

.standings tbody tr:nth-child(even):not(.playoff-line) td,
.standings tbody tr:nth-child(even):not(.playoff-line) th {
  background: var(--paper-zebra);
}
/* The playoff-line row occupies a row slot and would flip the zebra parity
   under it. Where the browser supports "of S", count only real rows. */
@supports selector(:nth-child(even of :not(.playoff-line))) {
  .standings tbody tr td,
  .standings tbody tr th { background: transparent; }
  .standings tbody tr:nth-child(even of :not(.playoff-line)) td,
  .standings tbody tr:nth-child(even of :not(.playoff-line)) th {
    background: var(--paper-zebra);
  }
}

.standings tbody tr:hover td,
.standings tbody tr:hover th { background: var(--paper-raised); }
.standings tbody tr.playoff-line:hover td { background: none; }

.standings .c-rank { color: var(--ink-3); width: 2.5ch; }
.standings .c-crest { width: calc(var(--crest-sm) + var(--s-2)); padding-right: 0; }
.standings .c-crest img {
  width: var(--crest-sm);
  height: var(--crest-sm);
  display: block;
}
.standings .c-team {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--t-headline);
  letter-spacing: var(--ls-display);
  color: var(--ink);
  white-space: nowrap;   /* never truncated — the table scrolls instead */
  padding-right: var(--s-5);
}
.standings .c-rec { color: var(--ink); }
.standings .c-diff[data-sign="pos"] { color: var(--turf); }
.standings .c-diff[data-sign="neg"] { color: var(--flare-text); }

/* ---- The playoff line ---------------------------------------------------- */
.standings tr.playoff-line td {
  height: auto;
  padding: 0;
  border: 0;
  border-top: var(--rule-playoff);
  position: relative;
  background: none;
}
.standings tr.playoff-line td::after {
  content: "PLAYOFF LINE";
  position: absolute;
  right: var(--s-2);
  top: -0.85em;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: var(--ls-tag);
  color: var(--amber);
  background: var(--paper);
  padding-inline: var(--s-2);
  white-space: nowrap;
}

/* ============================================================================
   6. MATCHUPS — DESIGN.md §6.3. A grid, not cards: column rules live in the
   gutters, drawn with border-left on every item except the first in its row.
   ========================================================================= */

.matchups {
  display: grid;
  gap: 0;
  grid-template-columns: 1fr;
}

.matchup {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-5) var(--s-4);
  border-bottom: var(--rule-w-hair) solid var(--rule-hair);
}

@media (min-width: 720px) {
  .matchups { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .matchups > .matchup { border-left: var(--rule-w-hair) solid var(--rule-hair); }
  .matchups > :nth-child(2n+1) { border-left: 0; }
}
@media (min-width: 1024px) {
  .matchups { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .matchups > .matchup { border-left: var(--rule-w-hair) solid var(--rule-hair); }
  .matchups > :nth-child(2n+1) { border-left: var(--rule-w-hair) solid var(--rule-hair); }
  .matchups > :nth-child(3n+1) { border-left: 0; }
}

.matchup .side {
  display: grid;
  justify-items: center;
  gap: var(--s-2);
  text-align: center;
  min-width: 0;
}
.matchup .side img {
  width: var(--crest-md);
  height: var(--crest-md);
}
.matchup .name {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--t-headline);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-display);
  color: var(--ink);
  overflow-wrap: break-word;   /* wraps to two lines; never an ellipsis */
}
.matchup .record {
  font-size: var(--t-agate-sm);
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--ink-3);
}
.matchup .score {
  font-size: var(--t-score);
  font-weight: 600;
  line-height: var(--lh-tight);
  letter-spacing: -0.01em;
  color: var(--ink);
}
.matchup .score[data-role="trailer"] { color: var(--ink-3); }

.matchup .mid {
  display: grid;
  justify-items: center;
  gap: var(--s-2);
}
.matchup .at {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: var(--ls-tag);
  color: var(--ink-4);
}

.tag {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--t-agate-sm);
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--ink-3);
  white-space: nowrap;
  text-align: center;
}
.tag--live {
  color: var(--amber);
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
}
.tag--live::before {
  content: "";
  width: 6px;
  height: 6px;
  flex: none;
  background: var(--amber);
  animation: pulse 2s var(--ease-inout) infinite;
}
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

/* The one non-ambient flash: a poll delivered a score different from the
   rendered one. Fires once, on change, never on first paint — a poller sets
   data-flash="true" and removes it. */
@keyframes score-flash { 0%, 100% { color: var(--ink); } 45% { color: var(--amber); } }
.matchup .score[data-flash="true"] { animation: score-flash 400ms var(--ease-out) 1; }

@media (prefers-reduced-motion: reduce) {
  .tag--live::before { animation: none; }
  .matchup .score[data-flash="true"] { animation: none; }
}

/* ============================================================================
   7. TEAMS PLATE — DESIGN.md §6.4. One of exactly two permitted region
   grounds; the tiles themselves stay flat.
   ========================================================================= */

.teams {
  background: var(--paper-sunk);
  padding: var(--s-6) var(--s-4);
  display: grid;
  gap: var(--s-5) var(--s-4);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 480px)  { .teams { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 720px)  { .teams { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .teams { grid-template-columns: repeat(6, minmax(0, 1fr)); } }

.team-tile {
  appearance: none;
  background: none;
  border: 0;
  padding: var(--s-2);
  min-width: 44px;
  min-height: 44px;
  display: grid;
  justify-items: center;
  align-content: start;
  gap: var(--s-2);
  cursor: pointer;
  text-align: center;
}
.team-tile img {
  width: var(--crest-lg);
  height: var(--crest-lg);
  transition: transform var(--dur-fast) var(--ease-out);
}
.team-tile:hover img,
.team-tile:focus-visible img { transform: translateY(-2px); }

.team-tile .name {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--t-headline);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-display);
  color: var(--ink);
  overflow-wrap: break-word;
}
.team-tile .trait {
  font-family: var(--font-mono);
  font-size: var(--t-agate-sm);
  line-height: var(--lh-agate);
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--ink-3);
  overflow-wrap: anywhere;
}

/* ============================================================================
   8. MESSAGE BOARD — DESIGN.md §6.5
   ========================================================================= */

.board {
  display: grid;
  gap: var(--s-6);
  grid-template-columns: 1fr;
}
@media (min-width: 1024px) {
  .board { grid-template-columns: var(--rail-w) minmax(0, 1fr); gap: var(--s-7); }
}

.board-rail { min-width: 0; }

/* ---- Rail at lg and above: a vertical list, selected marked by a 3px amber
   bar on its left edge. Never a filled pill, never a rounded chip. -------- */
.channels { display: block; }

.rail-kicker {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--t-kicker);
  letter-spacing: var(--ls-kicker);
  text-transform: uppercase;
  color: var(--ink-3);
  margin: var(--s-4) 0 var(--s-1) var(--s-3);
}
.rail-kicker:first-child { margin-top: 0; }

.channel {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 44px;
  text-align: left;
  background: none;
  border: 0;
  border-left: 3px solid transparent;
  padding: var(--s-2) var(--s-3);
  font-family: var(--font-mono);
  font-size: var(--t-agate);
  line-height: var(--lh-agate);
  color: var(--ink-3);
  text-decoration: none;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out);
}
.channel:hover { color: var(--ink-2); }
.channel[aria-current="true"] { border-left-color: var(--amber); color: var(--ink); }
.channel[data-open="false"]::after { content: " · LOCKED"; color: var(--flare-text); }

/* ---- Below lg the rail collapses to a horizontally scrolling snap strip
   inside its .scroller. ---------------------------------------------------- */
@media (max-width: 1023.98px) {
  .board-rail .scroller { scroll-snap-type: x mandatory; }
  .channels {
    display: flex;
    align-items: center;
    gap: var(--s-4);
    border-bottom: var(--rule-w-hair) solid var(--rule-hair);
  }
  .rail-kicker {
    margin: 0;
    white-space: nowrap;
    align-self: center;
    flex: none;
  }
  .channel {
    width: auto;
    flex: none;
    white-space: nowrap;
    scroll-snap-align: start;
    border-left: 0;
    border-bottom: 3px solid transparent;
  }
  .channel[aria-current="true"] {
    border-left-color: transparent;
    border-bottom-color: var(--amber);
  }
}

/* ---- The river ----------------------------------------------------------- */

.board-main { min-width: 0; }

.board-topic {
  padding-bottom: var(--s-3);
  margin-bottom: var(--s-2);
  border-bottom: var(--rule-w-hair) solid var(--rule-hair);
}
.board-topic .channel-name {
  font-size: var(--t-agate);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--ink);
}
.board-topic .channel-desc {
  margin-top: var(--s-1);
  font-family: var(--font-serif);
  font-size: var(--t-agate-sm);
  line-height: var(--lh-body);
  color: var(--ink-3);
  max-width: var(--measure);
}

/* board.js sets data-stale on the posts region when a poll failed but posts
   are already on screen. Say so in words — colour never carries a fact. */
.posts[data-stale="true"]::before {
  content: "RECONNECTING";
  display: block;
  padding-bottom: var(--s-2);
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--t-agate-sm);
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--amber);
}

.post {
  display: grid;
  grid-template-columns: var(--crest-xs) minmax(0, 1fr);
  gap: var(--s-3);
  padding-block: var(--s-4);
  border-bottom: var(--rule-w-hair) solid var(--rule-hair);
}
.posts > .post:last-child { border-bottom: 0; }

.post .avatar {
  width: var(--crest-xs);
  height: var(--crest-xs);
  margin-top: 2px;
}
/* The commissioner has no crest. A mark, not a plinth: a mono initial in the
   ruling ink. Unspecified in DESIGN.md — derived from the RULING colour. */
.avatar--commish {
  display: grid;
  place-items: center;
  width: var(--crest-xs);
  height: var(--crest-xs);
  border-top: 2px solid var(--amber);
}
.avatar--commish::before {
  content: "C";
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--t-chip-meta);
  line-height: 1;
  letter-spacing: 0;
  color: var(--amber);
}

.post-col { min-width: 0; }

.post-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-2) var(--s-3);
  margin-bottom: var(--s-2);
}
.post-meta .author {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--t-headline);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-display);
  color: var(--ink);
}
.post-meta time {
  font-size: var(--t-agate-sm);
  color: var(--ink-3);
  white-space: nowrap;
}

.post .body {
  font-family: var(--font-serif);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  color: var(--ink-2);
  max-width: var(--measure);
  overflow-wrap: break-word;
}
.post .body em,
.post--ruling .body { font-style: italic; }

/* Threading is exactly one level deep. */
.replies { margin-top: var(--s-4); }
.post--reply {
  margin-left: var(--s-6);
  padding-left: var(--s-4);
  border-left: var(--rule-w-hair) solid var(--rule-hair);
  border-bottom: 0;
}
@media (max-width: 479.98px) {
  .post--reply { margin-left: var(--s-3); padding-left: var(--s-3); }
}

/* Where a composer would sit: one italic serif line above a hairline.
   No disabled input, no greyed-out button, no placeholder box. */
.composer-note {
  margin-top: var(--s-4);
  padding-top: var(--s-3);
  border-top: var(--rule-w-hair) solid var(--rule-hair);
  font-family: var(--font-serif);
  font-style: italic;
  font-size: var(--t-body);
  line-height: var(--lh-body);
  color: var(--ink-3);
}

/* ============================================================================
   9. TRANSACTIONS TICKER — DESIGN.md §6.6. Never marquees, never auto-scrolls.
   ========================================================================= */

.board-wrap { display: block; }

@media (min-width: 1240px) {
  .board-wrap {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--ticker-w);
    gap: var(--s-7);
    align-items: start;
  }
  /* Side by side at xl: the separator becomes the gutter rule, not a top rule. */
  .board-wrap > .region-ticker {
    border-top: 0;
    padding-left: var(--s-6);
    border-left: var(--rule-w-hair) solid var(--rule-hair);
  }
}

.region-ticker { min-width: 0; }

.ticker {
  border-top: var(--rule-w-mid) solid var(--rule-mid);
  max-height: 60vh;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--rule-mid) transparent;
}

.tx {
  display: grid;
  grid-template-columns: auto var(--crest-xs) minmax(0, 1fr);
  gap: var(--s-3);
  align-items: baseline;
  padding-block: var(--s-3);
  border-bottom: var(--rule-w-hair) solid var(--rule-hair);
}
.tx:last-child { border-bottom: 0; }

.tx time {
  font-size: var(--t-agate-sm);
  color: var(--ink-3);
  white-space: nowrap;
}
.tx img,
.tx .avatar--commish {
  width: var(--crest-xs);
  height: var(--crest-xs);
  align-self: center;
}
.tx .copy {
  font-family: var(--font-serif);
  font-size: var(--t-agate-sm);
  line-height: var(--lh-body);
  color: var(--ink-2);
  overflow-wrap: break-word;
}

.tx-kind {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--t-agate-sm);
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--ink-3);
}
.tx-kind[data-tone="pos"]  { color: var(--turf); }
.tx-kind[data-tone="neg"]  { color: var(--flare-text); }
.tx-kind[data-tone="note"] { color: var(--amber); }

/* The oldest entries fade. Decoration only — the same moves are readable in
   full above, so --ink-4 never carries a fact alone here. */
.tx--stale .copy,
.tx--stale time { color: var(--ink-4); }

/* ============================================================================
   10. FOOTER
   ========================================================================= */

.site-footer {
  padding-block: var(--s-6) var(--s-7);
  border-top: var(--rule-w-mid) solid var(--rule-mid);
  margin-top: var(--pad-block);
}
.footer-line {
  font-family: var(--font-serif);
  font-size: var(--t-agate-sm);
  line-height: var(--lh-body);
  color: var(--ink-3);
  max-width: var(--measure);
}
.footer-meta {
  margin-top: var(--s-2);
  text-transform: uppercase;
  letter-spacing: var(--ls-agate);
  color: var(--ink-4);
}
.footer-meta time { color: var(--ink-3); }

/* ============================================================================
   11. EXPLAINER CONTENT — SPEC addendum 1.
   overlay.css owns the dialog shell, the scrim, the FLIP and the .ov-prose
   variant; the dashboard template names its blocks .explainer /
   .explain-block, which no sheet styles. These rules cover only those two
   class names, mirroring .ov-prose so the two stay visually identical, and
   do not touch anything overlay.css already sets.
   ========================================================================= */

.explainer {
  max-width: var(--measure);
  display: grid;
  gap: var(--s-5);
  padding-block: var(--s-4);
}
.explain-block {
  display: grid;
  gap: var(--s-2);
  padding-top: var(--s-3);
  border-top: var(--rule-w-hair) solid var(--rule-hair);
}
.explain-block h3 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--t-headline);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  color: var(--ink);
}
.explain-block p {
  font-family: var(--font-serif);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  color: var(--ink-2);
}

/* ============================================================================
   12. REPUTATION HEAT MAP — SPEC addendum 4.
   NOT YET IN dashboard.html and not in DESIGN.md: the spec asks for the full
   12x12 reputation grid rendered as a heat map "somewhere on the page", but
   no markup, route data or class names exist for it yet. These rules are
   written against the naming convention the rest of the template uses, so the
   region can be dropped in without a second CSS pass:

     <section class="region region-reputation" aria-labelledby="rep-head">
       <h2 class="section">…<span class="head" id="rep-head">Reputation</span></h2>
       <div class="scroller">
         <table class="repmap">
           <caption>…</caption>
           <thead><tr><th scope="col">…</th>… 12 column heads …</tr></thead>
           <tbody>
             <tr><th scope="row" class="rep-holder">Magma Franchise</th>
                 <td class="rep-cell" data-level="ally">+4</td>… </tr>
           </tbody>
         </table>
       </div>
       <p class="rep-legend">…</p>
     </section>

   data-level takes: ally (>= +3), warm, neutral, cool, enemy (<= -3), self.
   Every cell prints its score, so colour never carries the fact alone. The
   fills are the precomputed *-mark tokens, which exist for exactly this —
   a mark, not a card.
   ========================================================================= */

.repmap {
  width: 100%;
  border-collapse: collapse;
  min-width: 640px;      /* scrolls inside its .scroller, never the body */
}
.repmap caption {
  text-align: left;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--t-section);
  text-transform: uppercase;
  letter-spacing: var(--ls-display);
  color: var(--ink);
  padding: 0 0 var(--s-2);
  margin-bottom: var(--s-2);
}
.repmap thead th {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--t-agate-sm);
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--ink-3);
  padding: var(--s-2);
  text-align: center;
  vertical-align: bottom;
  border-bottom: var(--rule-w-mid) solid var(--rule-mid);
  white-space: nowrap;
}
.repmap .rep-holder {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--t-agate);
  letter-spacing: var(--ls-display);
  color: var(--ink);
  text-align: left;
  white-space: nowrap;
  padding: 0 var(--s-4) 0 0;
  height: var(--row-h);
  border-bottom: var(--rule-w-hair) solid var(--rule-hair);
}
.rep-cell {
  height: var(--row-h);
  padding: 0 var(--s-2);
  text-align: center;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums lining-nums slashed-zero;
  font-size: var(--t-agate);
  color: var(--ink-2);
  border-bottom: var(--rule-w-hair) solid var(--rule-hair);
  border-left: var(--rule-w-hair) solid var(--rule-hair);
}
.rep-cell[data-level="ally"]    { background: var(--turf-mark);  color: var(--ink); }
.rep-cell[data-level="warm"]    { color: var(--turf); }
.rep-cell[data-level="neutral"] { color: var(--ink-3); }
.rep-cell[data-level="cool"]    { color: var(--flare-text); }
.rep-cell[data-level="enemy"]   { background: var(--flare-mark); color: var(--ink); }
.rep-cell[data-level="self"]    { background: var(--paper-zebra); color: var(--ink-4); }

.rep-legend {
  margin-top: var(--s-3);
  font-family: var(--font-mono);
  font-size: var(--t-agate-sm);
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--ink-3);
}
.rep-legend .rep-ally  { color: var(--turf); }
.rep-legend .rep-enemy { color: var(--flare-text); }

/* ============================================================================
   13. X STREAMS — SPEC ADDENDUM 5. Replaces the message board region.
   Post anatomy is DESIGN.md §6.5 unchanged (22px crest, display author, mono
   relative time, serif body at --measure, replies indented exactly one level);
   what is new is the @handle, the link out to X, and the commissioner being
   visually distinct. Nothing here is a card: no fill, no radius, no shadow.
   The commissioner's mark is amber, matching .avatar--commish in §8.
   ========================================================================= */

.streams {
  display: grid;
  gap: var(--s-6);
  grid-template-columns: 1fr;
}
@media (min-width: 1024px) {
  .streams { grid-template-columns: var(--rail-w) minmax(0, 1fr); gap: var(--s-7); }
}

.stream-rail { min-width: 0; }

/* ---- Rail at lg and above: a vertical list of the thirteen accounts, the
   selected one marked by a 3px amber bar on its left edge. Never a filled
   pill, never a rounded chip. ---------------------------------------------- */
.accounts { display: block; }

.account {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 44px;
  text-align: left;
  background: none;
  border: 0;
  border-left: 3px solid transparent;
  padding: var(--s-2) var(--s-3);
  font-family: var(--font-mono);
  font-size: var(--t-agate);
  line-height: var(--lh-agate);
  color: var(--ink-3);
  text-decoration: none;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out);
}
.account:hover { color: var(--ink-2); }
.account[aria-current="true"] { border-left-color: var(--amber); color: var(--ink); }
/* The commissioner is not a franchise. Same ink the ruling mark uses. */
.account--commish { color: var(--amber); }
.account--commish[aria-current="true"] { color: var(--amber); }

/* ---- Below lg the rail collapses to a horizontally scrolling snap strip
   inside its .scroller, exactly as the channel rail did. -------------------- */
@media (max-width: 1023.98px) {
  .stream-rail .scroller { scroll-snap-type: x mandatory; }
  .accounts {
    display: flex;
    align-items: center;
    gap: var(--s-4);
    border-bottom: var(--rule-w-hair) solid var(--rule-hair);
  }
  .stream-rail .rail-kicker {
    margin: 0;
    white-space: nowrap;
    align-self: center;
    flex: none;
  }
  .account {
    width: auto;
    flex: none;
    white-space: nowrap;
    scroll-snap-align: start;
    border-left: 0;
    border-bottom: 3px solid transparent;
  }
  .account[aria-current="true"] {
    border-left-color: transparent;
    border-bottom-color: var(--amber);
  }
}

/* ---- The river ----------------------------------------------------------- */

.stream-main { min-width: 0; }

.stream-topic {
  padding-bottom: var(--s-3);
  margin-bottom: var(--s-2);
  border-bottom: var(--rule-w-hair) solid var(--rule-hair);
}
.stream-topic .stream-handle {
  font-size: var(--t-agate);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--ink);
}
.stream-topic .stream-desc {
  margin-top: var(--s-1);
  font-family: var(--font-serif);
  font-size: var(--t-agate-sm);
  line-height: var(--lh-body);
  color: var(--ink-3);
  max-width: var(--measure);
}

/* streams.js sets data-stale when a poll failed but posts are already on
   screen. Say so in words — colour never carries a fact. */
.stream[data-stale="true"]::before {
  content: "RECONNECTING";
  display: block;
  padding-bottom: var(--s-2);
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--t-agate-sm);
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--amber);
}

.xpost {
  display: grid;
  grid-template-columns: var(--crest-xs) minmax(0, 1fr);
  gap: var(--s-3);
  padding-block: var(--s-4);
  border-bottom: var(--rule-w-hair) solid var(--rule-hair);
}
.stream > .xpost:last-child { border-bottom: 0; }

.xpost .avatar {
  width: var(--crest-xs);
  height: var(--crest-xs);
  margin-top: 2px;
}

.xpost .post-meta .handle {
  font-size: var(--t-agate-sm);
  letter-spacing: 0;
  color: var(--ink-3);
  white-space: nowrap;
}
.xpost--commish .post-meta .handle { color: var(--amber); }

/* A commissioner post is a ruling: serif italic, per DESIGN.md §4.2. */
.xpost--commish .body { font-style: italic; }

/* The link out to the real post. Not a button — the page is read-only, and a
   button would imply this page does something. */
.xpost-out {
  margin-top: var(--s-2);
  font-family: var(--font-mono);
  font-size: var(--t-agate-sm);
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
}
/* 44px target per DESIGN.md §8, made of padding rather than a box — the
   underline stays on the words so it still reads as a link, not a control. */
.xpost-out a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--ink-3);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color var(--dur-fast) var(--ease-out);
}
.xpost-out a:hover,
.xpost-out a:focus-visible { color: var(--amber); }

/* Replies indent exactly one level. A reply to a reply is still one level —
   the mirror stores the thread, the page never draws a second indent. */
.xpost--reply {
  margin-left: var(--s-6);
  padding-left: var(--s-4);
  border-left: var(--rule-w-hair) solid var(--rule-hair);
}
@media (max-width: 479.98px) {
  .xpost--reply { margin-left: var(--s-3); padding-left: var(--s-3); }
}

.region-streams { min-width: 0; }

/* ============================================================================
   14. TEAM CARDS — a grid of twelve; an open card spans the row and slides
   its roster open. Native <details>; the slide uses ::details-content where
   the browser supports it and simply opens where it does not.
   ========================================================================= */
.team-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: var(--s-2);
  grid-auto-flow: row dense;
}
.team-card {
  border: var(--rule-w-hair) solid var(--rule-hair);
  background: var(--paper);
  min-width: 0;
  interpolate-size: allow-keywords;
}
.team-card[open] {
  grid-column: 1 / -1;
  border-color: var(--rule-mid);
  background: var(--paper-raised);
}
.team-card::details-content {
  block-size: 0;
  overflow: hidden;
  transition: block-size 260ms var(--ease-out), content-visibility 260ms allow-discrete;
}
.team-card[open]::details-content { block-size: auto; }
@media (prefers-reduced-motion: reduce) {
  .team-card::details-content { transition: none; }
}

.team-card summary {
  list-style: none;
  cursor: pointer;
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr);
  grid-template-rows: auto auto auto;
  column-gap: var(--s-2);
  row-gap: 1px;
  padding: var(--s-2);
  align-items: center;
}
.team-card summary::-webkit-details-marker { display: none; }
.team-card summary:hover .tc-name,
.team-card summary:focus-visible .tc-name { color: var(--amber); }
.team-card summary img { grid-row: 1 / span 3; width: 34px; height: 34px; }
.tc-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.9375rem;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tc-meta, .tc-form {
  font-size: var(--t-agate-sm);
  color: var(--ink-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.team-card[open] .tc-name,
.team-card[open] .tc-form { white-space: normal; }

.tc-roster {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-2) var(--s-5);
  padding: var(--s-2) var(--s-3) var(--s-3);
  border-top: var(--rule-w-hair) solid var(--rule-hair);
}
@media (max-width: 719.98px) { .tc-roster { grid-template-columns: 1fr; } }
.tc-group-head {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--t-agate-sm);
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--ink-3);
  padding-bottom: 2px;
  border-bottom: var(--rule-w-hair) solid var(--rule-hair);
  margin-bottom: 2px;
}
.tc-group ul { list-style: none; margin: 0; padding: 0; }
.tc-group li {
  display: grid;
  grid-template-columns: 3ch minmax(0, 1fr) auto;
  gap: var(--s-2);
  font-size: var(--t-agate-sm);
  line-height: 1.6;
}
.tc-pos { color: var(--ink-3); }
.tc-player { color: var(--ink); overflow-wrap: anywhere; }
.tc-nfl { color: var(--ink-3); }
.tc-foot {
  grid-column: 1 / -1;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: var(--t-agate-sm);
  color: var(--ink-3);
  padding-top: var(--s-1);
}
.tc-field {
  background: none;
  border: 0;
  border-bottom: var(--rule-w-hair) solid var(--rule-mid);
  color: var(--ink-2);
  font-family: var(--font-mono);
  font-size: var(--t-agate-sm);
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  cursor: pointer;
  padding: 0;
}
.tc-field:hover, .tc-field:focus-visible { color: var(--amber); border-bottom-color: var(--amber); }

/* ============================================================================
   15. MATCHUP LAYOUTS — restored as real base rules now that section 14
   ("the deck") is gone. Both the "up next" pairing row and the "last week"
   result row were ONLY ever styled inside the deck section - at anything
   narrower than about 900px the generic three-column .matchup grid (built
   for an older .side/.side markup the template no longer renders) forced a
   nowrap "FINAL - BY 259.0" tag wide and overflowed the whole page sideways.
   These are the real, width-safe rules, not a patch.
   ========================================================================= */

/* ---- Results: one row per team, stacked, tag below. ---------------------- */
.matchups--results .matchup { display: block; padding: var(--s-5) var(--s-4); }
.matchups--results .mline {
  display: grid;
  grid-template-columns: var(--crest-sm) minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s-3);
  padding-block: var(--s-1);
}
.matchups--results .mline img { width: var(--crest-sm); height: var(--crest-sm); }
.matchups--results .mline .name {
  font-size: var(--t-headline);
  color: var(--ink-3);
  overflow-wrap: break-word;
}
.matchups--results .mline--won .name { color: var(--ink); }
.matchups--results .mtag { margin-top: var(--s-2); text-align: right; }
.matchups--results .mtag .tag { white-space: normal; }

/* ---- Up next: who plays whom, no score yet. ------------------------------ */
.matchups--upcoming .matchup.pairing {
  display: grid;
  grid-template-columns: var(--crest-sm) minmax(0, 1fr) auto minmax(0, 1fr) var(--crest-sm);
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-4) var(--s-4);
}
.matchups--upcoming .matchup.pairing img { width: var(--crest-sm); height: var(--crest-sm); }
.matchups--upcoming .matchup.pairing .name {
  font-size: var(--t-headline);
  text-align: center;
  line-height: var(--lh-tight);
}
.matchups--upcoming .matchup.pairing .at {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: var(--ls-tag);
  color: var(--ink-4);
  text-transform: uppercase;
  text-align: center;
}

/* ---- The hero: the first game of the week, full width and much bigger. --- */
@media (min-width: 720px) {
  .matchups--upcoming .matchup.pairing:first-child {
    grid-column: 1 / -1;
    grid-template-columns: var(--crest-lg) minmax(0, 1fr) auto minmax(0, 1fr) var(--crest-lg);
    padding: var(--s-7) var(--s-5) var(--s-6);
    position: relative;
  }
  .matchups--upcoming .matchup.pairing:first-child::before {
    content: "This week's headline game";
    position: absolute;
    top: var(--s-3);
    left: var(--s-5);
    font-family: var(--font-mono);
    font-weight: 600;
    font-size: var(--t-agate-sm);
    text-transform: uppercase;
    letter-spacing: var(--ls-tag);
    color: var(--amber);
  }
  .matchups--upcoming .matchup.pairing:first-child img {
    width: var(--crest-lg);
    height: var(--crest-lg);
  }
  .matchups--upcoming .matchup.pairing:first-child .name {
    font-size: var(--t-section);
    text-transform: uppercase;
    letter-spacing: var(--ls-display);
  }
  .matchups--upcoming .matchup.pairing:first-child .at {
    font-size: var(--t-body);
    font-family: var(--font-serif);
    font-style: italic;
    color: var(--ink-3);
    text-transform: none;
    letter-spacing: 0;
  }
}

/* ============================================================================
   16. FEATURED QUOTES — the GMs' voices, pulled out big before the full feed.
   This is the actual hook of the project (twelve AI agents arguing), so it
   gets real type and real room instead of living only in a starved rail.
   Not a card: no fill, no radius, no shadow — a serif pull-quote per GM,
   separated by rule only, same restraint as everything else on the page.
   ========================================================================= */
.quote-feature {
  display: grid;
  gap: var(--s-6);
  margin-bottom: var(--s-6);
  padding-bottom: var(--s-6);
  border-bottom: var(--rule-w-mid) solid var(--rule-mid);
}
@media (min-width: 640px) {
  .quote-feature { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-5); }
}
.quote-feature-item {
  margin: 0;
  display: grid;
  grid-template-columns: var(--crest-xs) minmax(0, 1fr);
  grid-template-rows: auto auto;
  column-gap: var(--s-3);
  row-gap: var(--s-2);
}
.quote-feature-item .avatar { width: var(--crest-xs); height: var(--crest-xs); }
.quote-feature-lede {
  margin: 0 0 var(--s-5);
  font-family: var(--font-mono);
  font-size: var(--t-agate-sm);
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--ink-3);
}
.quote-feature-item blockquote {
  grid-column: 1 / -1;
  margin: 0;
  font-family: var(--font-serif);
  font-size: 1.3125rem;
  line-height: var(--lh-body);
  color: var(--ink);
}
.quote-feature-item blockquote::before { content: "“"; }
.quote-feature-item blockquote::after { content: "”"; }
.quote-feature-item--commish blockquote { font-style: italic; }
.quote-feature-item figcaption {
  align-self: center;
  font-family: var(--font-mono);
  font-size: var(--t-agate-sm);
  color: var(--ink-3);
}
.quote-feature-item figcaption .author {
  font-family: var(--font-display);
  font-weight: 500;
  color: var(--ink-2);
  letter-spacing: var(--ls-display);
  margin-right: var(--s-2);
}

/* ============================================================================
   17. REPUTATION DISCLOSURE — the full 12x12 grid is mostly neutral cells;
   it is collapsed behind a native <details> so the one sentence that matters
   (who's an ally, who won't deal) isn't buried under a wall of zeros.
   ========================================================================= */
.rep-disclosure {
  interpolate-size: allow-keywords;
}
.rep-disclosure summary {
  cursor: pointer;
  list-style: none;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-top: var(--s-2);
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--t-agate-sm);
  text-transform: uppercase;
  letter-spacing: var(--ls-tag);
  color: var(--ink-3);
  border-bottom: var(--rule-w-hair) solid var(--rule-hair);
  transition: color var(--dur-fast) var(--ease-out), border-bottom-color var(--dur-fast) var(--ease-out);
}
.rep-disclosure summary::-webkit-details-marker { display: none; }
.rep-disclosure summary:hover,
.rep-disclosure summary:focus-visible { color: var(--amber); border-bottom-color: var(--amber); }
.rep-disclosure[open] summary { margin-bottom: var(--s-4); }
.rep-disclosure .rep-legend { margin-top: var(--s-4); }
/* Slide open rather than the native <details> instant blink - same
   ::details-content technique as the team cards (§14). */
.rep-disclosure::details-content {
  block-size: 0;
  overflow: hidden;
  opacity: 0;
  transition: block-size 260ms var(--ease-out), opacity 200ms var(--ease-out),
    content-visibility 260ms allow-discrete;
}
.rep-disclosure[open]::details-content { block-size: auto; opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .rep-disclosure::details-content { transition: none; }
}

/* ============================================================================
   18. TEAM ACCENT COLORS — one real color per club, read off its own crest
   by eye (anthracite's cool steel, magma's molten orange, event horizon's
   violet nebula...). Used as a thin identifying edge, never a fill - the
   crest is still the real mark. This is the other half of the "too dour"
   fix: the page had exactly one spot color before. Now every GM's voice
   and every club's card carries its own.
   ========================================================================= */
[data-card="anthracite"],       [data-accent-team="anthracite"]       { --accent: #51606C; }
[data-card="bad-beat-bureau"],  [data-accent-team="bad-beat-bureau"]  { --accent: #B8392C; }
[data-card="deep-six"],         [data-accent-team="deep-six"]         { --accent: #1C7A69; }
[data-card="derecho-force"],    [data-accent-team="derecho-force"]    { --accent: #B9860F; }
[data-card="event-horizon"],    [data-accent-team="event-horizon"]    { --accent: #7247A8; }
[data-card="foundry-works"],    [data-accent-team="foundry-works"]    { --accent: #297E67; }
[data-card="frontier-marshals"],[data-accent-team="frontier-marshals"]{ --accent: #AD7C13; }
[data-card="locust-protocol"],  [data-accent-team="locust-protocol"]  { --accent: #9C730F; }
[data-card="magma-franchise"],  [data-accent-team="magma-franchise"]  { --accent: #D9601A; }
[data-card="permafrost-legion"],[data-accent-team="permafrost-legion"]{ --accent: #1A8FAE; }
[data-card="talon-strike"],     [data-accent-team="talon-strike"]     { --accent: #AC4335; }
[data-card="titan-reckoning"],  [data-accent-team="titan-reckoning"]  { --accent: #9C7A17; }

.team-card { border-top: 3px solid var(--accent, var(--rule-hair)); }
.team-card[open] { border-top-color: var(--accent, var(--rule-mid)); }

.quote-feature-item {
  border-left: 3px solid var(--accent, var(--amber));
  padding-left: var(--s-4);
}
.xpost { border-left: 3px solid var(--accent, transparent); padding-left: var(--s-3); }
.xpost--reply { border-left-color: var(--accent, var(--rule-hair)); }
.tx { border-left: 3px solid var(--accent, transparent); padding-left: var(--s-3); }

/* ============================================================================
   19. SIDE PANEL LAYOUT — GM talk and Transactions flank the center column
   on wide screens (Trey, 2026-10-05). Below ~1320px there is not room for
   three honest columns, so it falls back to the single stacked order this
   page already used: GM talk, then the games, then Transactions, then
   Reputation. Unlike the old "deck," panels are NOT height-capped or
   internally scrolling - they are exactly as roomy as the rest of the page,
   they just sit beside it and stay in view a little longer as you scroll.
   ========================================================================= */

:root { --panel-w: 320px; }

.layout { display: block; }
.layout > * + * { border-top: var(--rule-w-hair) solid var(--rule-hair); }

@media (min-width: 1320px) {
  :root { --page-max: 1560px; }

  .layout {
    display: grid;
    grid-template-columns: var(--panel-w) minmax(0, 1fr) var(--panel-w);
    grid-template-areas: "left center right";
    column-gap: var(--s-7);
    align-items: start;
  }
  .layout > * + * { border-top: 0; }

  .side-panel--left  { grid-area: left; }
  .layout-center      { grid-area: center; min-width: 0; }
  .side-panel--right { grid-area: right; }

  .side-panel--left,
  .side-panel--right {
    position: sticky;
    top: var(--s-4);
  }
  .side-panel--left  { border-right: var(--rule-w-hair) solid var(--rule-hair); padding-right: var(--s-6); }
  .side-panel--right { border-left:  var(--rule-w-hair) solid var(--rule-hair); padding-left:  var(--s-6); }

  /* All three columns start flush at the same top edge. */
  .layout .region:first-child,
  .side-panel .region { padding-top: 0; }

  /* The quote strip is two-up at this viewport width everywhere else on the
     page, but a 320px panel can't hold two columns of a pull-quote -
     force the CONTAINER back to one column. (Each quote-feature-item has
     its own, unrelated internal grid for its avatar/quote/caption - leave
     that alone.) */
  .side-panel .quote-feature { grid-template-columns: 1fr; }

  /* The center column is narrower here (~700px, not the full 1200+ the page
     gets without side panels) - the matchups and standings breakpoints
     below were tuned for the full page width and crush inside this one:
     three 230px matchup cards wrap every team name, two 330px standings
     tables force their own internal scrollbars. Cap both back down. */
  .layout-center .matchups { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .layout-center .matchups > .matchup { border-left: var(--rule-w-hair) solid var(--rule-hair); }
  .layout-center .matchups > :nth-child(2n+1) { border-left: 0; }
  .layout-center .standings-grid { grid-template-columns: 1fr; }
}
