/* FIFAGami — themed on Ledger (see docs/tokens.css, source: github.com/dgoodenough/style).
   Greyscale carries structure; color carries meaning: green = played (glowing ramp),
   red = never (when highlighted), yellow = upcoming.

   The confederation palette and the meetings ramp are declared here as custom properties
   because three things read them: this stylesheet, the canvas in app.js (via getCss), and
   render_hero.py, which parses this file. One place, three consumers. */

:root {
  /* app-level aliases onto the Ledger tokens (canvas reads these via getCss).
     Sheet-on-chrome: the grid is the dark "sheet"; the chrome around it is darker still. */
  --bg: var(--chrome);
  --panel: var(--chrome-2);
  --panel-2: var(--paper-2);
  --ink-dim: var(--ink-2);
  --line: var(--grid);
  --never: var(--paper);    /* never-played cell = the sheet */
  --never-hi: #b3362c;      /* never-played when highlighted (red floods the void) */
  --upcoming: var(--pend-solid); /* scheduled first meeting */
  --diag: #0d0e10;          /* same-team diagonal (darker than the sheet) */
  --crosshair: rgba(255, 255, 255, .08);
  --band-strip-ink: #ffffff;     /* text on a confederation colour strip */
  /* Combined (men's + women's) view categories. Blue/orange are the most
     colourblind-distinguishable pair; green (the theme accent) marks "both". */
  --both: var(--pos);       /* pairing has met in BOTH the men's and women's game */
  --men-only: #5aa9e6;      /* met in the men's game only */
  --women-only: #eb9a3f;    /* met in the women's game only */

  /* Two more kinds of empty. A blank cell used to mean three different things at once;
     these separate "hadn't happened yet" and "this team has never played anyone" from
     the plain never-met void. */
  --predebut: #1b1d20;      /* scrubbed to a year before this team's first ever match */
  --nodata: #201f24;        /* team has no matches at all in this archive */
  --nodata-line: #8d7fa8;   /* label colour marking those rows/columns */

  /* ---- confederation palette (categorical) -------------------------------
     Deliberately clear of the three semantic hues: green = played, red = never,
     yellow = upcoming. AFC used to be a red close enough to --neg to read as
     "never", and CAF a green close enough to --pos to read as "played". */
  --confed-afc: #c268b0;
  --confed-caf: #3ba9bd;
  --confed-concacaf: #c08a3c;
  --confed-conmebol: #4f8fd0;
  --confed-ofc: #8a6f5c;
  --confed-uefa: #8b6ad6;

  /* ---- meetings ramp -----------------------------------------------------
     Single-hue green glow: more green = more played. Stops are at 0/30/55/80/100%
     of the log-scaled count. */
  --ramp-0: #263c30;
  --ramp-30: #2b5a3d;
  --ramp-55: #2e7d4f;
  --ramp-80: #3aa967;
  --ramp-100: #4fd688;
  font-size: 14px;
}

/* Light ("newsprint") theme. Ledger ships the greyscale swap in tokens.css; these are the
   app-level values that also have to move — a ramp tuned for a dark sheet washes out on
   paper, and white-on-colour flips to near-black.

   app.js sets .ledger-light on <html> as well as <body>, because the canvas resolves this
   palette against document.documentElement and properties set on <body> do not reach it. */
.ledger-light {
  --never: var(--paper);
  /* Deeper than the old #d8b3ad, which sat so close to the paper that the empties read
     as blank rather than as the subject. The never-played mass has to carry the same
     weight here as it does in the dark theme; it is the point of the picture. */
  --never-hi: #c4705f;
  --diag: #b9b6ae;
  --crosshair: rgba(0, 0, 0, .10);
  --band-strip-ink: #ffffff;
  --men-only: #2c6ea8;
  --women-only: #a9631a;
  --predebut: #dcd9d1;
  --nodata: #d2ccd8;
  --nodata-line: #6b4f8a;

  --confed-afc: #a03f8c;
  --confed-caf: #1c7d90;
  --confed-concacaf: #94631d;
  --confed-conmebol: #2c6ea8;
  --confed-ofc: #6b5343;
  --confed-uefa: #6543ad;

  --ramp-0: #cfe0d4;
  --ramp-30: #9ac7ab;
  --ramp-55: #5da77f;
  --ramp-80: #2f8054;
  --ramp-100: #0f5c33;
}

* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
  background: var(--chrome);
  color: var(--ink);
  font-family: var(--font);
  overflow: hidden;
}
a { color: var(--accent); }

/* One consistent focus ring, everywhere, for keyboard users only. */
:focus-visible {
  outline: 2px solid var(--pos);
  outline-offset: 2px;
  border-radius: 2px;
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: 8px; top: -60px; z-index: 20;
  background: var(--pos); color: #06210f; font-weight: 700;
  padding: 9px 14px; border-radius: var(--radius); text-decoration: none;
  transition: top .12s;
}
.skip-link:focus { top: 8px; }

#app { display: flex; height: 100vh; height: 100dvh; width: 100vw; }

/* ---- panel ---- */
#panel {
  width: 300px; min-width: 300px; height: 100%;
  background: var(--chrome-2);
  border-right: 1px solid var(--grid-strong);
  overflow-y: auto;
  padding: 16px 16px 24px;
}
#panel header h1 { margin: 0; font-size: 1.25rem; letter-spacing: .2px; }
#panel header .sub { margin: 4px 0 0; color: var(--ink-2); font-size: .82rem; }
#panel header .sub em { color: var(--neg); font-style: normal; font-weight: 700; }

.ctl { margin-top: 18px; }
.ctl[hidden], .legend[hidden] { display: none; }
.row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.lbl { font-size: .72rem; text-transform: uppercase; letter-spacing: .8px; color: var(--ink-2); flex: 1; font-weight: 700; }
.hint { color: var(--ink-3); font-size: .72rem; }

/* segmented control */
.seg { display: flex; background: var(--paper); border: 1px solid var(--grid-strong); border-radius: var(--radius); overflow: hidden; }
.seg button { flex: 1; padding: 8px; background: none; border: none; color: var(--ink-2); cursor: pointer; font-size: .9rem; font-family: inherit; }
.seg button.active { background: var(--pos); color: var(--band-ink); font-weight: 600; }
.seg button:disabled { opacity: .45; cursor: not-allowed; }
.seg button:disabled.active { opacity: .6; }
.ledger-light .seg button.active { color: #fff; }

button.mini {
  background: var(--paper); border: 1px solid var(--grid-strong); color: var(--ink-2);
  border-radius: var(--radius); padding: 2px 8px; font-size: .72rem; cursor: pointer; font-family: inherit;
}
button.mini:hover { color: var(--ink); border-color: var(--ink); }

.check { display: flex; align-items: center; gap: 8px; font-size: .85rem; margin: 8px 0; cursor: pointer; }
.check input { accent-color: var(--pos); }

.confed-list label { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: .85rem; cursor: pointer; }
.confed-list .dot { width: 11px; height: 11px; border-radius: 2px; flex: none; }
.confed-list .cnt { margin-left: auto; color: var(--ink-3); font-size: .72rem; font-variant-numeric: tabular-nums; }
.confed-list input { accent-color: var(--pos); }

#team-search {
  width: 100%; padding: 7px 9px; background: var(--paper); border: 1px solid var(--grid-strong);
  color: var(--ink); border-radius: var(--radius); font-size: .85rem; margin-bottom: 6px; font-family: inherit;
}
.team-list { max-height: 190px; overflow-y: auto; border: 1px solid var(--grid); border-radius: var(--radius); padding: 4px 8px; background: var(--paper); }
.team-list label { display: flex; align-items: center; gap: 7px; padding: 2px 0; font-size: .8rem; cursor: pointer; }
.team-list label:nth-child(even) { background: var(--paper-2); }
.team-list .rk { margin-left: auto; color: var(--ink-3); font-size: .68rem; font-variant-numeric: tabular-nums; }
.team-list input { accent-color: var(--pos); }

#sort, .path-pick select {
  width: 100%; padding: 7px 9px; background: var(--paper); border: 1px solid var(--grid-strong);
  color: var(--ink); border-radius: var(--radius); font-size: .85rem; cursor: pointer; font-family: inherit;
}

/* timeline */
#timeline-toggle { display: none; }          /* phone-only; see the mobile block below */
#timeline-body { display: block; }
.scrub-row { display: flex; align-items: center; gap: 9px; }
#year-scrub { flex: 1; min-width: 0; accent-color: var(--pos); cursor: pointer; }
.playbtn {
  flex: none; width: 30px; height: 30px; border-radius: var(--radius); cursor: pointer;
  background: var(--paper); border: 1px solid var(--grid-strong); color: var(--pos);
  font-size: .72rem; line-height: 1; display: flex; align-items: center; justify-content: center;
  font-family: inherit; letter-spacing: -1px;
}
.playbtn:hover { border-color: var(--pos); }

/* legend */
.legend { margin-top: 18px; }
#legend-canvas { width: 100%; height: 14px; border: 1px solid var(--grid); display: block; }
.legend-ticks {
  position: relative; height: 14px; margin-top: 3px;
  font-size: .62rem; color: var(--ink-3); font-variant-numeric: tabular-nums;
}
.legend-ticks .tick { position: absolute; transform: translateX(-50%); white-space: nowrap; }
.legend-ticks .tick::before {
  content: ""; position: absolute; left: 50%; top: -3px; width: 1px; height: 3px;
  background: var(--grid-strong);
}
.legend-keys {
  display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 8px;
  font-size: .74rem; color: var(--ink-2);
}
.legend-keys .sw { display: inline-block; width: 11px; height: 11px; border-radius: 2px; margin-right: 5px; vertical-align: -1px; }
#sw-never { background: var(--never); border: 1px solid var(--grid-strong); }
body[data-never="1"] #sw-never { background: var(--never-hi); border-color: var(--never-hi); }
#sw-upcoming { background: var(--upcoming); }
#sw-diag { background: var(--diag); }
#sw-predebut { background: var(--predebut); border: 1px solid var(--grid); }
#sw-nodata { background: var(--nodata); border: 1px solid var(--nodata-line); }
.legend-combined-keys { flex-wrap: wrap; gap: 8px 16px; }
#sw-both { background: var(--both); }
#sw-men { background: var(--men-only); }
#sw-women { background: var(--women-only); }
#sw-neither { background: var(--never); border: 1px solid var(--grid-strong); }

#panel footer { margin-top: 20px; padding-top: 14px; border-top: 1px solid var(--grid); }
#panel footer p { margin: 0 0 6px; font-size: .7rem; color: var(--ink-3); line-height: 1.5; }
#panel footer b { color: var(--ink-2); }

/* ---- stage ---- */
#stage { flex: 1; height: 100%; min-width: 0; display: flex; flex-direction: column; }

/* headline = the Excel band: black, white text, stat number in semantic colour */
#headline {
  flex: none; padding: 12px 22px; background: var(--band); color: var(--band-ink);
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
}
#headline .big {
  font-size: 2.5rem; font-weight: 800; line-height: 1; color: var(--neg-on-band);
  letter-spacing: -.5px; font-variant-numeric: tabular-nums;
}
#headline .rest { font-size: .95rem; color: var(--band-ink); max-width: 60ch; }
#headline .rest b { color: var(--pos-on-band); }
#headline.allplayed .big { color: var(--pos-on-band); }
#headline.played .big { color: var(--pos-on-band); }   /* a count of what happened */
#headline.combined .big { color: var(--pos-on-band); }  /* the "both" count */

/* ---- stage bar: view tabs + toggles ---- */
#stage-bar {
  flex: none; display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 0 14px; background: var(--chrome-2);
  border-bottom: 1px solid var(--grid-strong);
}
#views { display: flex; gap: 2px; overflow-x: auto; scrollbar-width: none; }
#views::-webkit-scrollbar { display: none; }
#views button {
  background: none; border: none; border-bottom: 2px solid transparent;
  color: var(--ink-2); font-family: inherit; font-size: .85rem; cursor: pointer;
  padding: 11px 12px 9px; white-space: nowrap; display: flex; align-items: center; gap: 6px;
}
#views button:hover { color: var(--ink); }
#views button.active { color: var(--ink); border-bottom-color: var(--pos); font-weight: 600; }
#views .pip {
  background: var(--pend-fill); color: var(--pend-fill-ink); border-radius: 8px;
  padding: 1px 6px; font-size: .66rem; font-weight: 700; font-variant-numeric: tabular-nums;
}
#views .pip:empty { display: none; }

#stage-tools { margin-left: auto; display: flex; align-items: center; gap: 6px; padding: 6px 0; }
.tool {
  background: var(--paper); border: 1px solid var(--grid-strong); color: var(--ink-2);
  border-radius: var(--radius); padding: 5px 10px; font-size: .76rem; cursor: pointer;
  font-family: inherit; display: flex; align-items: center; gap: 6px; white-space: nowrap;
}
.tool:hover:not(:disabled) { color: var(--ink); border-color: var(--ink-2); }
.tool:disabled { opacity: .4; cursor: not-allowed; }
.tool[hidden] { display: none; }
.tool.on { color: var(--ink); border-color: var(--pos); background: var(--pos-fill); }
.tool.icon { padding: 5px 9px; font-size: .95rem; line-height: 1; }
.tool .tsw { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }
.tool .tsw.never { background: var(--never-hi); }
.tool .tsw.upcoming { background: var(--upcoming); }
.tool .hint:empty { display: none; }

/* ---- views ---- */
.view { flex: 1; min-height: 0; }
.view[hidden] { display: none; }
#canvas-wrap { position: relative; height: 100%; overflow: hidden; }
#grid { display: block; width: 100%; height: 100%; cursor: grab; touch-action: none; }
#grid.panning { cursor: grabbing; }
#grid:focus { outline: none; }
#grid:focus-visible { outline: 2px solid var(--pos); outline-offset: -2px; }

#zoom-ctl {
  position: absolute; right: 14px; bottom: 14px; z-index: 6; display: flex; gap: 4px;
}
#zoom-ctl button {
  background: var(--paper); border: 1px solid var(--grid-strong); color: var(--ink);
  width: 34px; height: 34px; border-radius: var(--radius); cursor: pointer; font-size: 1rem;
  display: flex; align-items: center; justify-content: center; font-family: inherit;
  box-shadow: 0 1px 3px rgba(0,0,0,.12);
}
#zoom-ctl button#zoom-fit { width: auto; padding: 0 12px; font-size: .82rem; }
#zoom-ctl button:hover { border-color: var(--pos); color: var(--pos); }

#tooltip {
  position: absolute; pointer-events: none; z-index: 7;
  background: var(--paper); border: 1px solid var(--grid-strong); border-radius: var(--radius);
  padding: 8px 10px; font-size: .8rem; max-width: 240px; box-shadow: 0 2px 10px rgba(0,0,0,.18);
}
#tooltip .vs, #peek .vs { font-weight: 700; margin-bottom: 3px; }
#tooltip .n, #peek .n { color: var(--pos); font-weight: 700; }
#tooltip .up, #peek .up { color: var(--pend); font-weight: 700; }
#tooltip .silent, #peek .silent { color: var(--nodata-line); font-weight: 700; }
#tooltip .pre, #peek .pre { color: var(--ink-2); font-weight: 700; }
#tooltip .dim, #peek .dim { color: var(--ink-2); font-size: .74rem; }

/* Peek: what a tap landed on, and a button to commit to it. Touch's substitute for hover,
   and the reason a 2px cell is no longer a coin flip. */
#peek[hidden] { display: none; }
#peek {
  position: absolute; z-index: 8; min-width: 190px; max-width: min(320px, calc(100% - 16px));
  background: var(--paper); border: 1px solid var(--grid-strong); border-radius: var(--radius);
  padding: 10px 12px 10px; font-size: .84rem; box-shadow: 0 4px 16px rgba(0,0,0,.3);
}
.peek-act { display: flex; gap: 8px; margin-top: 9px; }
.peek-act button {
  flex: 1; padding: 8px 10px; border-radius: var(--radius); font-family: inherit;
  font-size: .78rem; cursor: pointer; border: 1px solid var(--grid-strong);
  background: var(--paper-2); color: var(--ink);
}
.peek-act .peek-open { background: var(--pos); border-color: var(--pos); color: #06210f; font-weight: 700; }

#loading {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  color: var(--ink-2); background: var(--chrome); font-size: .95rem;
}

/* single-team focus (one team manually selected) */
#canvas-wrap.focus #zoom-ctl { display: none; }
#teamfocus[hidden] { display: none; }
#teamfocus {
  position: absolute; inset: 0; z-index: 7; background: var(--paper);
  display: flex; flex-direction: column;
}
#teamfocus .tf-head { flex: none; padding: 12px 20px; border-bottom: 1px solid var(--grid-strong); }
.tf-title { font-weight: 700; font-size: 1.05rem; }
.tf-sum { color: var(--ink-2); font-size: .85rem; margin-top: 4px; }
.tf-sum b { font-variant-numeric: tabular-nums; }
.tf-sum .never { color: var(--neg); }
.tf-list { overflow-y: auto; padding: 6px 12px 16px; }
.tf-row {
  display: grid; grid-template-columns: 12px minmax(0,1fr) 100px 40px 46px; gap: 11px;
  align-items: center; width: 100%; background: none; border: none; color: var(--ink);
  text-align: left; cursor: pointer; padding: 5px 8px; border-radius: var(--radius);
  font-size: .85rem; font-family: inherit;
}
.tf-row:nth-child(even) { background: var(--paper-2); }
.tf-row:hover { background: var(--pos-fill); }
.tf-dot { width: 11px; height: 11px; border-radius: 2px; }
.tf-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tf-bar { height: 8px; background: var(--paper-2); border: 1px solid var(--grid); overflow: hidden; }
.tf-bar > span { display: block; height: 100%; background: var(--pos); }
.tf-n { text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; }
.tf-last { text-align: right; color: var(--ink-3); font-size: .72rem; font-variant-numeric: tabular-nums; }
.tf-row.none { opacity: .5; }
.tf-row.none .tf-n { color: var(--ink-3); font-weight: 400; }
.tf-sep {
  color: var(--ink-2); font-size: .72rem; text-transform: uppercase; letter-spacing: .6px;
  margin: 14px 8px 4px; font-weight: 700;
}

/* ---- list views (fixtures / one-offs / near misses / connect) ---- */
.listview { overflow-y: auto; padding: 0 0 40px; background: var(--chrome); }
.lv-head { padding: 20px 24px 14px; max-width: 74ch; }
.lv-head h2 { margin: 0 0 6px; font-size: 1.15rem; letter-spacing: -.2px; }
.lv-head p { margin: 0; color: var(--ink-2); font-size: .88rem; line-height: 1.55; }
.lv-group { padding: 0 12px; }
.lv-group h3 {
  margin: 18px 12px 6px; font-size: .72rem; text-transform: uppercase; letter-spacing: .8px;
  color: var(--ink-2); display: flex; align-items: baseline; gap: 8px;
}
.lv-row {
  display: flex; align-items: baseline; gap: 14px; width: 100%;
  background: none; border: none; border-top: 1px solid var(--grid);
  color: var(--ink); text-align: left; cursor: pointer; font-family: inherit;
  padding: 11px 12px; font-size: .9rem;
}
.lv-row:hover { background: var(--paper); }
.lv-row.pending { opacity: .5; }
.lv-teams {
  flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: baseline;
  column-gap: 10px; row-gap: 2px;
}
.lv-t { min-width: 0; }
.lv-v { color: var(--ink-3); font-size: .78rem; }
.lv-right { flex: none; text-align: right; display: flex; flex-direction: column; gap: 2px; }
.lv-when {
  font-weight: 700; font-size: .82rem; font-variant-numeric: tabular-nums; color: var(--ink);
}
.lv-when.now { color: var(--pend); }
.lv-when.soon { color: var(--pos); }
.lv-when.past { color: var(--ink-3); }
.lv-sub { color: var(--ink-3); font-size: .7rem; }
.lv-empty { padding: 28px 24px; color: var(--ink-2); max-width: 60ch; }
/* Says which archive and which filter a list is showing — the views print numbers, so they
   have to be explicit about the data behind them. */
.lv-scope { color: var(--ink); font-weight: 600; }
#gender-note { margin: 6px 0 0; }
.lv-more { padding: 14px 24px; color: var(--ink-3); font-size: .78rem; }
.path-pick { display: flex; align-items: center; gap: 10px; margin-top: 14px; max-width: 520px; }
.path-arrow { color: var(--ink-3); flex: none; }
.path-sum { margin: 16px 24px 0; color: var(--ink-2); font-size: .9rem; }
.path-sum b { color: var(--pos); font-size: 1.1rem; font-variant-numeric: tabular-nums; }

/* ---- match-detail card ---- */
#detail[hidden] { display: none; }   /* id selector would otherwise beat [hidden] */
#detail {
  position: fixed; top: 76px; right: 18px; z-index: 30;
  width: 330px; max-width: calc(100vw - 36px); max-height: calc(100vh - 96px);
  display: flex; flex-direction: column;
  background: var(--paper); border: 1px solid var(--grid-strong); border-radius: var(--radius);
  box-shadow: 0 4px 18px rgba(0,0,0,.32); overflow: hidden;
}
#detail .dh {
  display: flex; align-items: flex-start; gap: 8px; padding: 10px 14px;
  background: var(--band); color: var(--band-ink); flex: none;
}
#detail .dt { flex: 1; font-weight: 700; font-size: .95rem; line-height: 1.3; }
#detail .dt .vs { color: #b9bcbf; font-weight: 400; }
#detail .dx {
  background: none; border: none; color: #b9bcbf; font-size: 1.5rem; line-height: .8;
  cursor: pointer; padding: 0 2px; font-family: inherit;
}
#detail .dx:hover { color: var(--band-ink); }
#detail .db { overflow-y: auto; padding: 12px 14px 14px; }
#detail .db.dim { color: var(--ink-2); }
#detail .sum { font-size: .8rem; margin-bottom: 8px; color: var(--ink-2); }
#detail .sum b { color: var(--ink); font-size: 1.05rem; font-variant-numeric: tabular-nums; }
#detail .sum .w { color: var(--pos); font-weight: 700; }
#detail .sum .d { color: var(--ink-2); font-weight: 700; }   /* draws had no rule at all */
#detail .sum .l { color: var(--neg); font-weight: 700; }
#detail .sum .gd { color: var(--ink); font-variant-numeric: tabular-nums; }
#detail .never { color: var(--neg); font-weight: 700; padding: 6px 0; }
#detail .mlist { display: flex; flex-direction: column; }
#detail .mr {
  display: grid; grid-template-columns: 40px 50px 1fr; gap: 8px; align-items: baseline;
  padding: 3px 0; font-size: .78rem; border-top: 1px solid var(--grid);
}
#detail .mr .yr { color: var(--ink-2); font-variant-numeric: tabular-nums; }
#detail .mr .sc { font-weight: 700; font-variant-numeric: tabular-nums; }
#detail .mr.w .sc { color: var(--pos); } #detail .mr.l .sc { color: var(--neg); }
#detail .mr .tn { color: var(--ink-3); font-size: .72rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#detail .det-gender + .det-gender { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--grid-strong); }
#detail .det-label { display: block; font-size: .72rem; text-transform: uppercase; letter-spacing: .6px; font-weight: 700; margin-bottom: 5px; }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ---- phones ---------------------------------------------------------------
   The full 211-column grid is something you zoom into deliberately, not a landing
   screen, so app.js defaults the view here to the fixtures feed. The grid tab still
   works, and now actually fits: the label gutter shrinks with the cells. */
@media (max-width: 720px) {
  body { overflow-y: auto; overflow-x: hidden; }
  #app { flex-direction: column; height: auto; min-height: 100vh; min-height: 100dvh; width: 100%; }
  /* Grid + headline first; controls scroll below it. */
  #stage { order: 1; flex: none; height: auto; }
  #panel {
    order: 2; width: 100%; min-width: 0; height: auto; max-height: none; overflow: visible;
    border-right: none; border-top: 1px solid var(--grid-strong);
  }
  #panel header { display: none; }                /* title lives in the headline already */
  /* The grid is square and the phone is not, so a fixed-vh viewport leaves a band of
     dead space under a fitted grid. Track the width instead: at fit the grid fills
     the box, and zooming in still has room to pan. */
  #canvas-wrap { height: min(70vh, 115vw); }
  .view { flex: none; }
  .listview { max-height: none; overflow: visible; }
  #headline { padding: 10px 16px; }
  #headline .big { font-size: 2rem; }
  #headline .rest { font-size: .82rem; }

  #stage-bar { padding: 0 8px; gap: 4px; }
  #views button { padding: 10px 10px 8px; font-size: .82rem; }
  #stage-tools { margin-left: 0; width: 100%; padding: 0 4px 8px; overflow-x: auto; }
  .tool { padding: 7px 11px; }

  #zoom-ctl button { width: 42px; height: 42px; font-size: 1.15rem; }
  #zoom-ctl button#zoom-fit { width: auto; padding: 0 14px; }
  /* Bottom sheet: a side card on a phone is a card with nowhere to go. */
  #detail {
    top: auto; bottom: 0; left: 0; right: 0; width: auto; max-width: none;
    max-height: 76vh; border-radius: var(--radius) var(--radius) 0 0;
    border-left: none; border-right: none; border-bottom: none;
  }
  #detail .dx { font-size: 1.9rem; padding: 0 8px; }
  .lv-head { padding: 16px 16px 12px; }
  .lv-row { padding: 13px 10px; }
  .peek-act button { padding: 10px; }
  .ctl { margin-top: 22px; }

  /* Timeline scrubber moved up into the stage (under the headline, above the grid). */
  #timeline-slot #timeline {
    margin: 0; padding: 8px 16px 10px; border-bottom: 1px solid var(--grid-strong); background: var(--chrome-2);
  }
  #timeline-slot #timeline { padding: 0 16px; }
  #timeline-slot #timeline .row { display: none; }   /* replaced by the toggle */
  #timeline-slot .timeline-help { display: none; }

  /* Collapsed, the scrubber costs one row instead of three, which is the difference
     between the grid starting above the fold and below it. */
  #timeline-slot #timeline-toggle {
    display: flex; align-items: center; gap: 8px; width: 100%;
    min-height: 44px; padding: 0; margin: 0;
    background: none; border: 0; color: var(--ink-2);
    font: inherit; text-align: left; cursor: pointer;
  }
  #timeline-slot #timeline-toggle .lbl { margin: 0; }
  #timeline-slot #year-summary { font-variant-numeric: tabular-nums; }
  /* A collapsed control that is silently filtering the grid is a trap, so a scrubbed
     year is called out in the accent colour on the closed header. */
  #timeline-slot #timeline.scrubbed #year-summary { color: var(--pos); font-weight: 600; }
  #timeline-slot .tl-chevron { margin-left: auto; font-size: .8rem; color: var(--ink-3); }
  #timeline-slot #timeline.collapsed #timeline-body { display: none; }
  #timeline-slot #timeline.collapsed .tl-chevron { transform: rotate(-90deg); }
  #timeline-slot #timeline-body { padding-bottom: 10px; }
  /* The timeline belongs to the grid; it should not sit above a list view. */
  body:not([data-view="grid"]) #timeline-slot #timeline { display: none; }
}

/* ---- fact strip -------------------------------------------------------------
   The headline prints a number; this prints the reason to care about it. One fact
   at a time, drawn from docs/data/facts.json, each pointing at the view that proves
   it. Sits directly under the headline so it is read before the controls are. */
#factstrip {
  flex: none; display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  padding: 9px 22px; background: var(--paper); color: var(--ink);
  border-bottom: 1px solid var(--grid-strong);
}
#fact { margin: 0; flex: 1 1 34ch; font-size: .9rem; line-height: 1.45; }
#fact .fact-stat {
  font-weight: 800; color: var(--neg); font-variant-numeric: tabular-nums;
  letter-spacing: -.2px; margin-right: .35em;
}
.fact-tools { display: flex; align-items: center; gap: 8px; flex: none; }
#fact-link {
  font-size: .8rem; font-weight: 600; white-space: nowrap;
  border: 1px solid var(--pos); border-radius: var(--radius); padding: 3px 11px;
  text-decoration: none; color: var(--pos);
}
#fact-link:hover { background: var(--pos); color: var(--chrome); }
#fact-next {
  font: inherit; font-size: .8rem; white-space: nowrap; cursor: pointer;
  background: none; color: var(--ink-2);
  border: 1px solid var(--grid-strong); border-radius: var(--radius); padding: 3px 10px;
}
#fact-next:hover { color: var(--ink); border-color: var(--ink-2); }
/* The strip is the hook on a phone, where it is the only prose above the fold. */
@media (max-width: 760px) {
  #factstrip { padding: 9px 16px; gap: 9px; }
  #fact { font-size: .84rem; flex-basis: 100%; }
}
@media (prefers-reduced-motion: no-preference) {
  #fact { transition: opacity .18s ease; }
}
#factstrip.swapping #fact { opacity: 0; }

/* ---- MOCKUP: grid left, everything else in a right rail -----------------------
   The square was bound by height, not width: 159px of headline, fact strip and
   view tabs stacked above it while ~400px of width went unused inside the canvas.
   Moving that chrome out of the vertical stack and into the rail gives the grid
   the full viewport height, which is the largest a square can be without scrolling. */
/* Gated on shape, not just width. The rail wins only when the window is wider than it is
   tall by a good margin: the square becomes min(width - rail, height), so on a nearly
   square window a 380px rail costs more than the 159px of stacked chrome it removes. At
   721x800 the naive version produced a 341px grid where the stacked layout gives 560. */
@media (min-width: 1000px) and (min-aspect-ratio: 7/5) {
  #app { flex-direction: row; }
  #stage { order: 1; }
  #panel {
    order: 2;
    width: 380px; min-width: 380px;
    border-right: none; border-left: 1px solid var(--grid-strong);
    padding: 0 0 24px;                    /* chrome sits flush; controls get their own pad */
    display: flex; flex-direction: column;
  }
  /* The reparented chrome, now stacked down the rail. */
  #panel > #headline { flex: none; padding: 14px 16px; }
  #panel > #headline .big { font-size: 2.6rem; }
  #panel > #headline .rest { font-size: .88rem; max-width: none; }
  #panel > #factstrip { flex: none; padding: 10px 16px; }
  #panel > #factstrip #fact { flex-basis: 100%; font-size: .85rem; }
  #panel > #stage-bar { flex: none; padding: 0 10px; }
  /* Five tabs will not sit on one 348px line, and letting them wrap leaves a lone
     "Connect" stranded on row two. A 3-column grid makes two even rows instead. */
  #panel > #stage-bar { flex-direction: column; align-items: stretch; gap: 0; padding: 0; }
  #panel > #stage-bar #views {
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 0;
    overflow-x: visible; border-bottom: 1px solid var(--grid);
  }
  #panel > #stage-bar #views button { justify-content: center; padding: 10px 6px 8px; }
  #panel > #stage-bar #stage-tools {
    display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 10px;
  }
  #panel #panel-body { padding: 0 16px; overflow-y: auto; flex: 1; min-height: 0; }

  /* The head-to-head is anchored to the viewport's right edge, which used to put it over
     the grid. With the rail there it landed on the controls instead, so pull it clear:
     it floats over the grid, which now has the room, and the rail stays readable. */
  #detail { right: calc(380px + 18px); max-width: calc(100vw - 380px - 36px); }
  #panel-head { flex: none; padding: 14px 16px 12px; border-bottom: 1px solid var(--grid); }

  /* The stage is now nothing but the view, so the square gets the whole window. */
  #stage { padding: 0; }
  .view { flex: 1; min-height: 0; }
}
