/* Hand-written. The build never writes into site/ (docs/site-data-contract.md §2).
   No @import, no web font, no external URL of any kind — the deployed bundle
   makes zero third-party requests (contract §1). */

:root {
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
          Arial, "Noto Sans", sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --bg: #fbfbfa;
  --panel: #ffffff;
  --ink: #16181d;
  --muted: #5a6170;   /* 6.2:1 on the panel */
  --faint: #6b7280;   /* 4.8:1 — was #8a919e at 3.2:1, under AA for every 11–13px
                         label that uses it (column headers, meta lines, footer terms) */
  --line: #e3e6ea;
  --line-strong: #cfd4db;
  --accent: #1a4fd6;
  --accent-ink: #ffffff;
  --accent-soft: #eef2fe;
  --note-bg: #fff8e8;
  --note-line: #e8d199;
  --note-ink: #6b4c07;
  --shadow: 0 1px 2px rgba(20, 24, 33, .05), 0 1px 8px rgba(20, 24, 33, .04);
  /* Direction colours live in the palette with everything else. The green is a
     hair deeper than it was so it clears AA on --accent-soft too — the tint of
     the scenario answer row and of any hovered board row. */
  --up: #177647;
  --down: #a03030;
  /* A neutral tint for "this row is the subject" — derived from ink so it is
     grey in both themes and never borrows the accent or the amber. */
  --wash: color-mix(in srgb, var(--ink) 6%, transparent);
  /* The select chevron and the scroll-edge shade are the two places the palette
     used to be hardcoded, which left both looking light-mode in the dark theme. */
  --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%235a6170' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  --edge-shade: rgba(20, 24, 33, .13);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #101216;
    --panel: #171a20;
    --ink: #e9ecf1;
    --muted: #a3abb8;
    --faint: #828a98;   /* 5.0:1 on the dark panel; was 4.4 */
    --line: #262b33;
    --line-strong: #39404b;
    --accent: #7ea4ff;
    --accent-ink: #10131a;
    --accent-soft: #1b2333;
    --note-bg: #241d0d;
    --note-line: #4d3d16;
    --note-ink: #e2c887;
    --shadow: none;
    --up: #5fca8f;
    --down: #e88a8a;
    --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%23a3abb8' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
    --edge-shade: rgba(233, 236, 241, .10);  /* lighter = nearer: dark's own elevation logic */
  }
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { text-decoration-thickness: 2px; }

/* Horizontal padding only. The shorthand `padding: 0 1rem` used to zero the
   top and bottom too, and since `.wrap` outranks `main`, it silently erased
   main's 1.5rem top padding on every page — the h1 sat on the header's
   border (operator, 2026-09-02). */
.wrap { max-width: 46rem; margin: 0 auto; padding-left: 1rem; padding-right: 1rem; }

/* ---------- header ---------- */

.site-head {
  border-bottom: 1px solid var(--line);
  background: var(--panel);
}

.site-head .wrap {
  display: flex;
  align-items: baseline;
  gap: .25rem .75rem;
  flex-wrap: wrap;
  /* The links carry the height now (see nav a): the header is the same size. */
  padding-top: .5rem;
  padding-bottom: .5rem;
}

.brand {
  font-size: 1.05rem;
  font-weight: 650;
  letter-spacing: -.01em;
  color: var(--ink);
  text-decoration: none;
}

.brand:hover { color: var(--accent); }

.site-head nav {
  margin-left: auto;
  display: flex;
  gap: 1rem;
  font-size: .875rem;
}

/* .5rem above and below makes a 14px link a 37px tap target instead of 21px. */
.site-head nav a { color: var(--muted); text-decoration: none; padding: .5rem 0; }
.site-head nav a:hover,
.site-head nav a[aria-current="page"] { color: var(--ink); text-decoration: underline; }

/* Under 30rem the brand and four links never share a row anyway. Today the nav
   wraps and margin-left:auto shoves it to the right, so the header reads as a
   staggered pair; as its own full-width row, spread to the gutters, it reads
   as a nav. nowrap so "Next event" cannot break mid-phrase at 320px. */
@media (max-width: 30rem) {
  .site-head nav { margin-left: 0; flex-basis: 100%; justify-content: space-between; gap: 0; }
  .site-head nav a { white-space: nowrap; }
}

/* ---------- generic blocks ---------- */

/* 2rem above the h1, 3rem before the footer rule: main's bottom padding IS the
   gap now (the footer's own margin used to stack on top of it — 72px of dead
   air against 24px at the top). */
main { padding: 2rem 0 3rem; }

h1 {
  font-size: 1.5rem;
  line-height: 1.25;
  letter-spacing: -.02em;
  margin: 0 0 .5rem;
}

/* No one-word orphan under a two-line title, and the verdict sentence — which
   is re-rendered with different name lengths on every pick — breaks into lines
   of similar length. Progressive enhancement: browsers without text-wrap ignore it. */
h1, h2, .headline-claim, .door-t, .refuse-head { text-wrap: balance; }
.lede, .prose p, .rule-why, .note, .foot-note, .scen-labels li, .res-why, .fine { text-wrap: pretty; }

h2 {
  font-size: 1.0rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 2rem 0 .75rem;
}

.lede { font-size: 1.0rem; color: var(--muted); margin: .25rem 0 0; max-width: 38rem; }
/* .sub follows a title (tight) but also a card strip, a note, an h2 and — on the
   scenario page — another .sub, where 1.6px read as a typo. */
.sub { color: var(--muted); font-size: .9rem; margin: .75rem 0 0; }
h1 + .sub, .disc-head + .sub { margin-top: .25rem; }

.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: var(--shadow);
}

.note {
  background: var(--note-bg);
  border: 1px solid var(--note-line);
  color: var(--note-ink);
  border-radius: 8px;
  padding: .7rem .85rem;
  font-size: .875rem;
  margin: 1rem 0;
}

.note strong { font-weight: 650; }

/* The amber treatment is reserved for the projection — a board that is NOT what
   PPA publishes today. Painting the default, accurate view the same colour
   would spend the warning on the safe case and leave nothing for the risky one. */
.note.info {
  background: var(--panel);
  border-color: var(--line);
  color: var(--muted);
}

.note.info strong { color: var(--ink); }

.claim {
  font-variant-numeric: tabular-nums;
  font-size: .9rem;
  color: var(--muted);
  margin: .5rem 0 0;   /* belongs to the lede it qualifies, not to the doors below */
}

.claim b { color: var(--ink); font-weight: 650; }

/* The changes page's headline sentence. It was rendered in .claim — .9rem and
   --muted — so the one sentence answering the page's own title was smaller and
   greyer than the eight rule cards above it. */
.headline-claim {
  font-size: 1.15rem;
  line-height: 1.35;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  margin: 1rem 0 .25rem;
  max-width: 38rem;
}
.headline-claim b { font-weight: 650; }

/* ---------- controls ---------- */

.controls {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  align-items: center;
  margin: 1.5rem 0 1rem;   /* title block → first control is 1.5rem on every page */
}

.controls label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: .2rem; }

.field { flex: 1 1 15rem; min-width: 0; }

/* Secondary controls, collapsed. They are real features — the rule-set toggle
   and the decay horizon — but on the default view they are the 2% case, and
   stacking them above the table cost more vertical space than the first ten
   rows. `open` is set by the renderer whenever the reader arrived with either
   engaged, so nobody who is using them ever has to re-find them. */
.opts { margin: .6rem 0 1rem; font-size: .85rem; }
.opts > summary { color: var(--muted); cursor: pointer; padding: .35rem 0; }
.opts[open] > summary { margin-bottom: .5rem; }
.opts .controls { margin: 0; }

select {
  width: 100%;
  font: inherit;
  font-size: 1rem;   /* 16px: under that, iOS Safari zooms the page on focus */
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  padding: .5rem 2rem .5rem .6rem;
  appearance: none;
  /* Inline SVG chevron (--chevron, per theme): a data: URI is same-document, so
     this is still a zero-external-request page. */
  background-image: var(--chevron);
  background-repeat: no-repeat;
  background-position: right .7rem center;
  background-size: .7rem;
}

.toggle { display: inline-flex; border: 1px solid var(--line-strong); border-radius: 8px; overflow: hidden; }

.toggle button {
  font: inherit;
  font-size: .8rem;
  padding: .5rem .7rem;
  border: 0;
  background: var(--panel);
  color: var(--muted);
  cursor: pointer;
  white-space: nowrap;
}

.toggle button + button { border-left: 1px solid var(--line-strong); }
.toggle button[aria-pressed="true"] { background: var(--accent); color: var(--accent-ink); font-weight: 600; }

/* ---------- board table ---------- */

.board-table {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}

/* The cell inset, so the caption sits on the column text's left line, not the card edge. */
.board-table caption { text-align: left; color: var(--muted); font-size: .85rem; padding: .5rem .75rem; }

.board-table th {
  text-align: left;
  font-size: .72rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 600;
  padding: .4rem .75rem;
  /* Under border-collapse the rule belongs to the table's grid, not the cell,
     so it stayed behind when the header stuck — row 24 scrolled straight into
     "# PLAYER POINTS". An inset shadow is painted on the cell and travels. */
  border-bottom: 0;
  box-shadow: inset 0 -1px 0 var(--line-strong);
  position: sticky;
  top: 0;
  /* Must match the CARD it sits in, not the page: rows scroll underneath a
     sticky header, and a header painted --bg leaves them showing through. */
  background: var(--panel);
}

/* Numeric headers sit over the numbers they name. `.board-table th` (0,1,1)
   outranks the column classes (0,1,0), so without this "#", "Points" and "Gap"
   all hugged the left of right-set figures. */
.board-table th.col-rank,
.board-table th.col-pts,
.board-table th.col-delta { text-align: right; white-space: nowrap; }

/* Sticky earns its place on the 100-row board only: on a 5-row peek card or the
   9-row threshold table the header detaches and slides over its own rows. */
.peek .board-table th,
.scen-table th { position: static; }

/* .6rem vertical makes a row a ~44px tap target, the accessibility minimum. */
.board-table td { padding: .6rem .75rem; border-bottom: 1px solid var(--line); }
.board-table tbody tr:last-child td { border-bottom: 0; }
.board-table tbody tr:hover { background: var(--accent-soft); }

.col-rank { width: 3.2rem; text-align: right; color: var(--muted); font-size: .9rem; }
.col-pts { text-align: right; width: 6rem; font-weight: 600; white-space: nowrap; }
/* Names WRAP; they are never truncated. A rankings table whose primary content
   is a person's name must not render "Tyra Hurricane Bla…" — an extra line is a
   far smaller cost than a name the reader cannot match to a player. */
.col-player { width: 100%; overflow-wrap: anywhere; }

@media (max-width: 26rem) {
  .board-table :is(td, th, caption) { padding-left: .45rem; padding-right: .45rem; }
  .col-rank { width: 2.8rem; }
  .col-pts { width: 5.4rem; }
}
/* Underlined, always. These 100 names are the ONLY route from a board — which
   is a commodity PPA already publishes — into the per-result breakdown that is
   the reason this site exists. Ink-coloured with no underline they read as
   plain text, and the :hover rule that revealed them never fires on a phone,
   which is where the site is mostly read. Subtle underline colour so 100 rows
   do not turn into 100 blue links. */
.col-player a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  /* The 44px row is the target, not the 24px line box: padding grows the hit
     area to the cell's full height and the negative margin gives it back. */
  display: block;
  padding: .6rem 0;
  margin: -.6rem 0;
}
.col-player a:hover { color: var(--accent); text-decoration-color: var(--accent); }

tr.top3 .col-rank { color: var(--ink); font-weight: 650; }
.tie { color: var(--faint); }

/* ---------- board index cards ---------- */

.board-grid { display: grid; gap: .5rem; grid-template-columns: 1fr; padding: 0; margin: 0; list-style: none; }

@media (min-width: 34rem) { .board-grid { grid-template-columns: 1fr 1fr; } }

.board-grid a {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  padding: .7rem .85rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  text-decoration: none;
  color: var(--ink);
}

.board-grid a:hover { border-color: var(--accent); }
.board-grid .kind { margin-left: auto; font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--faint); }

/* The two doors into what only this site does. Deliberately above the boards:
   the boards are the commodity, these are the reason to be here. */
.doors { list-style: none; padding: 0; margin: 1.5rem 0 0; display: grid; gap: .5rem; }
@media (min-width: 34rem) { .doors { grid-template-columns: 1fr 1fr; } }
.doors a {
  display: block;
  padding: .8rem .9rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: 8px;
  text-decoration: none;
}
.doors a:hover { border-color: var(--accent); border-left-color: var(--accent); }
.door-t { display: block; color: var(--ink); font-weight: 650; font-size: .95rem; }
.door-d { display: block; color: var(--muted); font-size: .85rem; margin-top: .15rem; }

.peek { display: grid; gap: 1rem; grid-template-columns: 1fr; }
@media (min-width: 40rem) { .peek { grid-template-columns: 1fr 1fr; } }
.peek section { padding: .85rem; }
.peek h3 { margin: 0 0 .5rem; font-size: .95rem; }
/* Trim only the OUTER edges so the card's own padding does that job. Zeroing
   every cell instead collapses the gap between rank and name into "1Ben Johns". */
.peek .board-table :is(td, th):first-child { padding-left: 0; }
.peek .board-table :is(td, th):last-child { padding-right: 0; }
.peek .more { display: inline-block; margin-top: .5rem; font-size: .85rem; }

/* Where this player stands, and the way back to the board you came from. */
.standings { list-style: none; padding: 0; margin: 1rem 0 0; display: grid; gap: .3rem; }
@media (min-width: 34rem) { .standings { grid-template-columns: 1fr 1fr; } }
.standings a {
  display: flex; align-items: baseline; gap: .5rem;
  padding: .45rem .7rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  text-decoration: none;
  font-variant-numeric: tabular-nums;
}
.standings a:hover { border-color: var(--accent); }
.st-b { color: var(--muted); font-size: .82rem; flex: 1 1 auto; }
.st-r { color: var(--ink); font-weight: 650; }
.st-p { color: var(--muted); font-size: .85rem; }

/* ---------- contribution explainer (Step 5) ---------- */

.disc-block { margin: 1.75rem 0 0; }
.sub + .disc-block { margin-top: 1rem; }   /* the deck that introduces the first block sits with it */

.disc-head { display: flex; align-items: baseline; gap: .6rem; border-bottom: 1px solid var(--line-strong); padding-bottom: .35rem; }
.disc-head h3 { margin: 0; font-size: 1.05rem; letter-spacing: -.01em; }
.disc-head .disc-sum { margin-left: auto; font-size: 1.15rem; font-weight: 650; font-variant-numeric: tabular-nums; }

.results { list-style: none; margin: .6rem 0 0; padding: 0; }

.res {
  padding: .55rem .75rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  margin-bottom: .35rem;
}

.res-head { display: flex; align-items: baseline; gap: .6rem; }
.res-event { flex: 1 1 auto; overflow-wrap: anywhere; }
.res-pts { font-weight: 650; font-variant-numeric: tabular-nums; white-space: nowrap; }
.res-meta { font-size: .78rem; color: var(--faint); margin-top: .1rem; }
.res-why { font-size: .82rem; color: var(--muted); margin-top: .3rem; }

/* Non-counting results stay legible but visibly out of the total. The points
   are struck through, not hidden: "worth 1,000, expired" is the story. */
.res.out { background: transparent; border-style: dashed; }
.res.out .res-event { color: var(--muted); }
.res.out .res-pts { font-weight: 500; color: var(--faint); text-decoration: line-through; }

details.counting > summary {
  font-size: .82rem;
  color: var(--muted);
  cursor: pointer;
  padding: .4rem 0;
}

h4.not-counting {
  margin: 1.1rem 0 0;
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--faint);
  font-weight: 600;
}

.decay { margin-top: .9rem; padding: .7rem .85rem; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); }
.decay h4 { margin: 0 0 .4rem; font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; color: var(--faint); font-weight: 600; }
.decay-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(2, 1fr); gap: .2rem .9rem; }
@media (min-width: 30rem) { .decay-list { grid-template-columns: repeat(4, 1fr); } }
.decay-list li { display: flex; justify-content: space-between; gap: .4rem; font-size: .85rem; font-variant-numeric: tabular-nums; }
.decay-list li span:first-child { color: var(--muted); }

/* --muted, not --faint: at 12.5px --faint measured 3.17:1 on the light panel,
   under AA, and the ledger caveat is a sentence the reader must be able to
   read (pre-deploy review, 2026-09-02). */
.fine { font-size: .78rem; color: var(--muted); margin: .5rem 0 0; }

/* Direction colours, used by both the player decay list and the board overlay.
   Kept as weight + hue rather than hue alone so they survive a colour-blind
   reader: the sign character is always present. */
.up { color: var(--up); font-weight: 600; }
.down { color: var(--down); font-weight: 600; }

.col-delta { text-align: right; width: 4.6rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
@media (max-width: 26rem) { .col-delta { width: 4.1rem; } }

/* ---------- what changes on August 31 (Step 6) ---------- */

.rules { list-style: none; padding: 0; margin: .75rem 0 0; max-width: 46rem; }
.rules li { padding: .7rem 0; border-top: 1px solid var(--line); }
.rules li:first-child { border-top: 0; }
.rule-head { display: flex; gap: .6rem; align-items: baseline; flex-wrap: wrap; }
.rule-title { font-weight: 650; color: var(--ink); }
.rule-delta {
  font-variant-numeric: tabular-nums;
  font-size: .82rem;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: .05rem .5rem;
  white-space: nowrap;
}
.rule-why { color: var(--muted); margin: .2rem 0 0; }

/* The comparison is 6 columns and cannot shrink to a phone. It scrolls in its
   OWN box — the page body must never scroll sideways.

   The right-edge fade is the affordance: without it a phone reader has no cue
   that a whole second scenario (or, on the ledger, the Difference column that
   is the entire point) exists past the edge. Pure CSS, no JS, and it sits
   behind the content. */
.scroll-x {
  overflow-x: auto;
  margin-top: .85rem;
  background:
    linear-gradient(to right, var(--bg) 30%, transparent) left / 2.5rem 100% no-repeat,
    linear-gradient(to left, var(--bg) 30%, transparent) right / 2.5rem 100% no-repeat,
    radial-gradient(farthest-side at 0 50%, var(--edge-shade), transparent) left / .7rem 100% no-repeat,
    radial-gradient(farthest-side at 100% 50%, var(--edge-shade), transparent) right / .7rem 100% no-repeat;
  background-attachment: local, local, scroll, scroll;
}
/* Inside a scroller there is unlimited width, so wrapping buys nothing and
   `overflow-wrap: anywhere` was breaking "Grayso / n Goldin" mid-surname. One
   line per row; the table is simply wider inside the box built to scroll it. */
.scroll-x .col-player { white-space: nowrap; overflow-wrap: normal; }
.ledger-table td:first-child { white-space: nowrap; }

/* Jump list for the methodology page. */
.toc { margin: 1rem 0 0; }
.toc ul { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: .3rem .5rem; }
.toc a {
  display: inline-block;
  font-size: .82rem;
  color: var(--muted);
  text-decoration: none;
  padding: .25rem .55rem;
  border: 1px solid var(--line);
  border-radius: 999px;
}
.toc a:hover { color: var(--ink); border-color: var(--accent); }
main.prose section > h2 { scroll-margin-top: 1rem; }

.changes-table { min-width: 33rem; }
.changes-table th.scen {
  text-align: center;
  border-bottom: 0;
  box-shadow: inset 0 -1px 0 var(--line);
  font-weight: 650;
  color: var(--ink);
}
/* A visible seam between "today" and each hypothetical, so a reader never mixes
   up which pair of numbers they are reading. */
.changes-table th:nth-child(3),
.changes-table th.scen + th.scen,
.changes-table tbody td:nth-child(3),
.changes-table tbody td:nth-child(5) { border-left: 1px solid var(--line); }

.changes-table .delta { font-size: .82em; margin-left: .3rem; }
.changes-table .muted { color: var(--faint); }
/* Rows whose rank CHANGES — the page's subject. Deliberately a neutral tint,
   not a warning colour: a projected move is news, not an error. */
.changes-table tr.moved { background: var(--wash); }
.changes-table tr.moved .col-player { font-weight: 600; }
.changes-table tr.moved .col-rank { color: var(--ink); }   /* the pair of ranks IS the datum */

.foot-note {
  color: var(--muted);
  font-size: .85rem;
  margin-top: .75rem;
  max-width: 44rem;
}

/* ---------- methodology (Step 7) ---------- */

.prose section { margin-top: 2rem; }
.prose section p { max-width: 44rem; }
.prose ul.plain { padding-left: 1.1rem; margin: .6rem 0 0; }
.prose ul.plain li { margin-top: .45rem; max-width: 44rem; }

.ledger-table { min-width: 30rem; }

/* A fact slot that has not been filled yet shows an ellipsis — visibly
   unfinished, never a stale or invented value. The HTML authors these slots
   EMPTY; the number arrives from the artifacts or not at all. */
[data-fact]:empty::before { content: '…'; }

/* ---------- the next event (M3 Step 3) ---------- */

/* Two sides, stacked on a phone and side by side once there is room. The
   pickers are built once and never re-rendered — re-rendering an <input> while
   somebody is typing a name into it takes their cursor away — so this grid
   holds still while the card below it redraws. */
/* The verdict (or the instruction) sits above the pickers: answer first. */
.scen-lead:not(:empty) { margin: 0 0 1rem; }
.scen-lead .headline-claim { margin-top: 1rem; }
.scen-lead .note { margin-top: .6rem; }
.scen-teams { display: grid; gap: .6rem; grid-template-columns: 1fr; }
@media (min-width: 34rem) { .scen-teams { grid-template-columns: 1fr 1fr; } }

.scen-side { padding: .7rem .85rem; }
.scen-side h2 { margin: 0 0 .45rem; font-size: .72rem; text-transform: uppercase;
                letter-spacing: .06em; color: var(--faint); font-weight: 600; }
.slots { display: grid; gap: .4rem; }
.team-lines:not(:empty) { margin-top: .6rem; }
/* The other side's finish: a labelled select under their lines. */
.rf { margin-top: .7rem; display: grid; gap: .3rem; }
.rf-label { font-size: .72rem; text-transform: uppercase; letter-spacing: .06em;
            color: var(--faint); font-weight: 600; }
.rf-sum { color: var(--ink); }
.scen-table tr.one-place td { color: var(--muted); font-size: .85rem; white-space: normal; }

/* The finish matrix (M3 Step 4): the evidence under the answer, collapsed. */
.matrix { margin: 1rem 0 0; }
.matrix > summary { cursor: pointer; color: var(--accent); font-size: .9rem;
                    padding: .4rem 0; list-style-position: outside; }
.matrix > summary:hover { text-decoration: underline; }
.matrix[open] > summary { margin-bottom: .25rem; }
.matrix .note { margin: .25rem 0 .75rem; }
.matrix .fine { margin: .5rem 0 0; }
.matrix-table { min-width: 22rem; }
/* The rung label must not leave the numbers it names. This table is the one on
   the site that scrolls sideways with a MEANINGFUL row header — scroll it and
   without this you get a grid of figures with no idea which finish is which.
   The generic `.scen-table th[scope="row"]` sets position:static, so this needs
   the extra class to win. */
/* `.scen-table th[scope="row"]` sets position:static at the same (0,2,1) weight
   and appears later in this file, so the class pair is needed to win.
   Only the NUMERIC rows: the refuse and one-place rows are a sentence spanning
   the table, and pinning their label leaves the sentence sliding under it. */
/* The header's own first cell sticks with them, at a higher layer: without it
   the header row slides while the labels stay, and scrolled right the first
   player's name ends up captioning the column of rung labels (pre-deploy
   review, 2026-09-06). */
.scen-table.matrix-table thead th:first-child { z-index: 2; }
.scen-table.matrix-table thead th:first-child,
.scen-table.matrix-table tbody tr:not(.refuse):not(.one-place) th[scope="row"] {
  position: sticky;
  left: 0;
  background: var(--bg);
  /* border-collapse leaves a cell's own border behind when it sticks, so the
     divider is painted as a shadow that travels with it — the same fix the
     board's sticky column header uses. */
  box-shadow: inset -1px 0 0 var(--line);
  z-index: 1;
}
.matrix-table th[scope="col"] { text-align: right; }
.matrix-table th[scope="col"]:first-child { text-align: left; }
/* Total over its own delta: two lines in one cell keeps the table to one
   column per player, which is what fits a phone. */
.mx-cell { white-space: nowrap; }
.mx-t { display: block; font-variant-numeric: tabular-nums; }
.mx-d { display: block; font-size: .78rem; font-variant-numeric: tabular-nums; color: var(--faint); }
.mx-d.up { color: var(--up); }
.mx-d.down { color: var(--down); }
.mx-team .mx-t { font-weight: 700; }

/* Matches the <select> beside it: same border, radius and padding, so the four
   pickers and the board picker read as one control set. `appearance: none`
   also removes Safari's search decorations, which otherwise clip the value. */
input[type="search"] {
  width: 100%;
  font: inherit;
  /* 16px, not .95rem: iOS Safari zooms the viewport on focusing any input under
     16px, and these four are the page's primary control on a phone. */
  font-size: 1rem;
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  padding: .5rem .6rem;
  appearance: none;
  min-width: 0;
}
input[type="search"]::-webkit-search-decoration { appearance: none; }

.team-lines ul { list-style: none; margin: 0; padding: 0; }
.tl-row { display: flex; align-items: baseline; gap: .6rem;
          font-variant-numeric: tabular-nums; }
.tl-n { flex: 1 1 auto; overflow-wrap: anywhere; }
.tl-p { font-weight: 650; white-space: nowrap; }
/* The pair sum, printed beside the two numbers it adds: the reader can check
   the one piece of arithmetic this page does in the browser (contract §11.4). */
.team-sum { margin: .35rem 0 0; padding-top: .35rem; border-top: 1px solid var(--line);
            text-align: right; font-variant-numeric: tabular-nums; color: var(--muted);
            font-size: .9rem; }

.scen-table { margin-top: .9rem; }
.scen-table th[scope="row"] { text-align: left; font-size: .9rem; text-transform: none;
                              letter-spacing: 0; color: var(--ink); font-weight: 500;
                              padding: .6rem .75rem; border-bottom: 1px solid var(--line);
                              position: static; box-shadow: none; }
/* The minimal passing finish — the answer, marked in the evidence beneath it. */
.scen-table tr.th-key { background: var(--accent-soft); }
/* A mark a mouse hover cannot imitate: every table row turns --accent-soft on
   :hover, so the answer row also gets the doors' left bar. */
.scen-table tr.th-key th[scope="row"] { box-shadow: inset 3px 0 0 var(--accent); }
.scen-table tr.th-key th[scope="row"] { font-weight: 650; }
/* R1's row. Deliberately a SENTENCE with no figure of any kind: a number
   beside "not projectable yet" reads as the answer (m3-plan §5). */
.scen-table tr.refuse th[scope="row"], .scen-table tr.refuse td {
  border-bottom: 0;
  /* TOP, not the default middle: the refusal is three lines tall and a
     vertically centred row label would float away from the sentence it names. */
  vertical-align: top;
}
.scen-table tr.refuse td { padding: .6rem .75rem; }
.refuse-head { font-weight: 650; }
.refuse-why { color: var(--muted); font-size: .85rem; margin-top: .15rem; max-width: 34rem; }

/* Always visible, never tooltips: every one of these is a labelled assumption
   the card is making, and an assumption behind a hover is an assumption nobody
   on a phone will ever read. */
.scen-labels { list-style: none; margin: 1rem 0 0; padding: 0; }
.scen-labels li { color: var(--muted); font-size: .85rem; padding: .35rem 0;
                  border-top: 1px solid var(--line); max-width: 44rem; }

/* ---------- footer ---------- */

.prov {
  border-top: 1px solid var(--line);
  margin-top: 0;               /* main's bottom padding is the gap */
  padding: 1.5rem 0 3rem;
  font-size: .8rem;
  color: var(--muted);
}

.prov dl { display: grid; grid-template-columns: auto 1fr; gap: .25rem .75rem; margin: 1rem 0 0; }
.prov dt { color: var(--faint); }
.prov dd { margin: 0; font-variant-numeric: tabular-nums; }
.prov code { font-family: var(--mono); font-size: .95em; }
.prov .disclaim { max-width: 38rem; margin: 0; }   /* a <p>: its default 1em margins were an unchosen gap */

/* ---------- states ---------- */

.state { padding: 2rem 1rem; text-align: center; color: var(--muted); }
.state.error { color: var(--ink); }
.state h2 { text-transform: none; letter-spacing: 0; font-size: 1.05rem; color: var(--ink); margin: 0 0 .35rem; }

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus { left: .5rem; top: .5rem; background: var(--panel); padding: .5rem .75rem; border-radius: 6px; z-index: 10;
              border: 1px solid var(--line-strong); box-shadow: var(--shadow); }

/* ---------- focus ---------- */

/* One ring for keyboard focus, in the accent (6.7:1 / 7.2:1 against the panel).
   Nothing here had designed this state, and inside the rule-set toggle the
   browser default was clipped by overflow:hidden on three sides. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.toggle button:focus-visible { outline-offset: -3px; border-radius: 5px; }
.toggle button[aria-pressed="true"]:focus-visible { outline-color: var(--accent-ink); }
select:focus-visible,
input[type="search"]:focus-visible { outline-offset: 0; border-color: var(--accent); }
