/* Bricolage Grotesque, variable, self-hosted. Never fonts.googleapis.com at
   runtime - a third-party font request is the slowest thing on a page people
   open on club wifi. */
@font-face {
  font-family: 'Bricolage Grotesque';
  src: url('/fonts/bricolage-grotesque-latin.woff2') format('woff2-variations');
  font-weight: 200 800;
  font-stretch: 75% 100%;
  font-display: swap;
}

:root {
  color-scheme: dark;

  --page-bg: #0f2318;
  --card: #1c3a27;
  --card-pick: #22492f;
  --card-live: #1c3a27;

  --line: #2e4536;
  --line-soft: #263b2d;
  --line-live: #4a5c3c;
  --line-box: var(--ink);

  --ink: #f2ecdc;
  --dim: #9db3a3;
  --faint: #9db3a3;
  --fainter: #7c9186;
  --faintest: #65796c;
  --muted: #9db3a3;

  /* Acid is the live feed and the alert system. Nothing else. */
  --acid: #d8ff3f;
  --on-acid: #12241a;
  --acid-dim: #4d5e1f;
  --acid-text: var(--acid);

  /* Live is what's happening on court right now - a distinct signal from
     acid, which stays reserved for the alert system (a pick, a push). */
  --live: #ff6b4a;
  --on-live: #12241a;
  --live-text: #ff7a5c;

  /* Solid, un-blurred offset shadow behind cards/panels/buttons. */
  --ink-shadow: rgba(0, 0, 0, .45);

  /* The footer bar is always a dark slab in both themes - the mockup's one
     fixed anchor - so it carries its own pair of tokens rather than borrowing
     the theme's foreground. */
  --bar-bg: #131313;
  --bar-ink: #f4efe2;
  --bar-dim: #9a9a90;

  --portrait-border: var(--ink);

  /* Occasion: gold. A third accent, admitted on one condition - gold marks
     the OCCASION (a final, the clasico), never the STATE. It is allowed on a
     border, a court engraving, a struck medallion, a foil band; it is never
     allowed on the bell, the live badge or a status pill. That is what keeps
     "acid = alerts, coral = live" meaning what a visitor already learned. */
  --gold: #e3b04b;
  --gold-deep: #a97d24;
  --gold-ink: #2a1e08;
  --gold-hair: rgba(227, 176, 75, .55);
  --gold-line: rgba(227, 176, 75, .34);
  --gold-flash: #fff4d2;

  /* The card's court panel - a green court in both themes, with cream
     lines and text sized to read on the green. The score board is the
     dark net-post slab; the trailing team's numbers sit greyer still. */
  --court: #1f4030;
  --court-line: rgba(242, 236, 220, .24);
  --court-net: rgba(242, 236, 220, .62);
  --court-ink: #f2ecdc;
  --court-dim: #9db3a3;
  --court-lose: #7f958a;
  --board-bg: #12241a;
  /* the detail modal's headshot fallbacks: a plain silhouette on the court,
     an initials chip in the line-up strip. Court-green in both themes. */
  --court-mono: #2b5040;
  --court-mono-fg: #4a6d5c;
  --mono-chip: #d8d0b4;
  --mono-chip-fg: #4a5340;

  --display: 'Bricolage Grotesque', system-ui, -apple-system, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --shell-w: 468px;
  /* the court card splits its width in two, so the desktop columns need
     more room than the old stacked card did */
  --col-w: 344px;
}

/* Light is an explicit choice (see the inline script in index.html, which
   resolves system preference before first paint and sets this attribute) -
   never a bare prefers-color-scheme block, so the toggle and the initial
   system read share one source of truth. */
:root[data-theme="light"] {
  color-scheme: light;

  --page-bg: #f2ecdc;
  --card: #fffdf6;
  --card-pick: #f5f7dc;
  --card-live: #fffdf6;

  --line: #e2dcc6;
  --line-soft: #ece5d2;
  --line-live: #b7c76a;
  --line-box: var(--ink);

  --ink: #12241a;
  --dim: #5c6b5e;
  --faint: #5c6b5e;
  --fainter: #74836f;
  --faintest: #8b9686;
  --muted: #5c6b5e;

  --acid: #d8ff3f;
  --on-acid: #12241a;
  --acid-dim: #55680f;
  --acid-text: #55680f;

  --live: #ff6b4a;
  --on-live: #12241a;
  --live-text: #b23a1e;

  --ink-shadow: var(--ink);

  --portrait-border: var(--ink);

  /* The court stays green in both themes, so the court's own gold (the rays,
     the hairline) does not move; these are the values for gold on cream -
     deep enough to read as metal rather than as highlighter. */
  --gold: #c2901f;
  --gold-deep: #8c6212;
  --gold-ink: #2a1e08;
  --gold-hair: rgba(140, 98, 18, .6);
  --gold-line: rgba(140, 98, 18, .38);
  --gold-flash: #7a5209;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background-color: var(--page-bg);
  background-image:
    repeating-linear-gradient(0deg, var(--line) 0 1.5px, transparent 1.5px 27px),
    repeating-linear-gradient(90deg, var(--line) 0 1.5px, transparent 1.5px 27px);
  color: var(--ink);
  font: 400 15px/1.55 var(--display);
  -webkit-font-smoothing: antialiased;
}

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

.lbl {
  font: 700 10px/1 var(--display);
  letter-spacing: .1em;
  text-transform: uppercase;
  margin: 0;
}
.mono { font: 400 11.5px/1.6 var(--mono); margin: 0; }
.faint { color: var(--faintest); }
.sd { color: var(--dim); font-weight: 500; }
.ico { flex: none; vertical-align: -2px; }

/* ---------- shell: one centred column ---------- */
.shell {
  max-width: var(--shell-w);
  margin: 0 auto;
  padding: 0 20px 148px;
  display: flex;
  flex-direction: column;
  gap: 22px;
  min-height: 100vh;
}

/* ---------- top bar ---------- */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-top: 22px;
}
.brand { display: flex; align-items: center; gap: 10px; }
.brand-mark { display: block; flex: none; border-radius: 7px; }
.wordmark {
  position: relative;
  margin: 0;
  font: 800 15px/1 var(--display);
  font-stretch: 82%;
  letter-spacing: .03em;
  text-transform: uppercase;
}
/* Editorial "BETA" kicker hugging the wordmark - no box, carried by the
   acid colour. --acid-text (not raw --acid) keeps it legible on light. */
.beta-flag {
  position: absolute;
  top: -7px;
  right: -30px;
  font: 700 8px/1 var(--display);
  letter-spacing: .16em;
  color: var(--acid-text);
}

.toggles { display: flex; align-items: center; gap: 8px; flex: none; }
.pill-btn {
  appearance: none;
  -webkit-appearance: none;
  height: 38px;
  min-width: 44px;
  padding: 0 14px;
  flex: none;
  border: 2px solid var(--ink);
  background: var(--card);
  color: var(--ink);
  font: 400 14px/1 var(--mono);
  letter-spacing: .04em;
  display: grid;
  place-items: center;
  cursor: pointer;
  border-radius: 999px;
  box-shadow: 3px 3px 0 var(--ink-shadow);
}
.pill-btn.round { width: 38px; min-width: 0; padding: 0; }
.pill-btn:hover { color: var(--acid-text); }

/* ---------- tournament badge ---------- */
.now-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  align-self: flex-start;
  background: var(--acid);
  color: var(--on-acid);
  font: 700 12px/1 var(--display);
  letter-spacing: .04em;
  text-transform: uppercase;
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 8px 14px 8px 10px;
  margin: 0;
}
.now-badge .dot { background: var(--on-acid); border-color: var(--on-acid); }
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--acid); border: 1.5px solid var(--ink); flex: none; }

/* ---------- hero ---------- */
.hero-wrap { position: relative; }
.hero {
  font-family: var(--display);
  font-size: 66px;
  line-height: .88;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: -.045em;
  text-transform: uppercase;
  margin: 0;
}
.hero-on { color: var(--acid-text); }

.hero-line1 { position: relative; display: inline-block; }
.squiggle {
  position: absolute;
  left: 1%;
  bottom: -.14em;
  width: 98%;
  height: .16em;
  overflow: visible;
}
.squiggle path { fill: none; stroke: var(--acid); stroke-width: 7; stroke-linecap: round; }
.squiggle-live path { stroke: var(--live); }

/* The one handwritten touch - it sits in the whitespace to the right of the
   two-word "Live now" headline. */
.hero-note {
  position: absolute;
  right: 0;
  top: 6px;
  max-width: 42%;
  margin: 0;
  text-align: right;
  font: italic 600 15px/1.15 var(--display);
  color: var(--live-text);
  transform: rotate(-4deg);
}

.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;
}

/* ---------- onboarding: search + quick picks ---------- */
.search-row { display: flex; align-items: center; gap: 10px; }
.search {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1 1 auto;
  min-width: 0;
  background: var(--card);
  border: 2.5px solid var(--ink);
  border-radius: 999px;
  padding: 12px 14px 12px 20px;
  box-shadow: 3px 3px 0 var(--ink-shadow);
  color: var(--dim);
}
.search-input {
  appearance: none;
  -webkit-appearance: none;
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: none;
  color: var(--ink);
  font: 700 15px/1.2 var(--display);
}
.search-input:focus { outline: none; }
.search-input::placeholder { color: var(--faintest); font-weight: 400; }
.search-input::-webkit-search-cancel-button { -webkit-appearance: none; display: none; }
.search-clear {
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: var(--line-soft);
  color: var(--ink);
  cursor: pointer;
}

/* On the intro screen the search sits in the flow (the wrapper collapses out);
   on the board and filtered list the wrapper is an opaque band that pins the
   pill to the top so it is always one tap away. Court headers park just below
   it, hence the --dock-h other rules key their sticky offset off. */
:root { --dock-h: 66px; }
.search-dock { display: contents; }
.search-dock[data-stuck="true"] {
  display: block;
  position: sticky;
  top: 0;
  z-index: 4;
  margin: 0 -20px;
  padding: 9px 20px 11px;
  background: var(--page-bg);
}

/* ---------- draw filter: all / men / women, behind an icon ----------
   A court chip or the search box earns its width; this is reached for far
   less, so it is a small icon beside search rather than a row of its own -
   the popover opens over the board instead of pushing it down. */
.draw-filter { position: relative; flex: none; }
.draw-trigger[data-active="true"] { background: var(--ink); color: var(--page-bg); }
.draw-scrim {
  position: fixed;
  inset: 0;
  z-index: 5;
  border: 0;
  background: none;
  cursor: default;
}
.draw-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 6;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 140px;
  padding: 6px;
  border: 2.5px solid var(--ink);
  border-radius: 14px;
  background: var(--card);
  box-shadow: 3px 3px 0 var(--ink-shadow);
}
.draw-item {
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  border-radius: 8px;
  background: none;
  padding: 9px 10px;
  text-align: left;
  font: 700 14px/1 var(--display);
  color: var(--ink);
  cursor: pointer;
}
.draw-item:hover { background: var(--card-pick); }
.draw-item[aria-selected="true"] { background: var(--ink); color: var(--page-bg); }

/* The second filter, under a hairline in the same menu: a checkbox rather
   than a fourth draw, because it stacks with the draw. The row never inverts
   - the box carries the state, so an unticked row beside a selected draw
   cannot be misread as "men is off". */
.draw-sep { height: 1.5px; margin: 4px 2px; background: var(--line-soft); }
.draw-check { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
.draw-box {
  flex: none;
  width: 16px;
  height: 16px;
  display: grid;
  place-items: center;
  border: 2px solid var(--ink);
  border-radius: 5px;
  color: var(--on-acid);
}
.draw-check[aria-checked="true"] .draw-box { background: var(--acid); }
.draw-check .ico { width: 11px; height: 11px; }
.draw-count { margin-left: auto; font: 700 10px/1 var(--mono); color: var(--faint); }

.chips-pick { display: flex; flex-wrap: wrap; gap: 8px; }
.chip-pick {
  appearance: none;
  -webkit-appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 40px;
  padding: 9px 15px;
  border: 2px solid var(--ink);
  border-radius: 999px;
  background: var(--card);
  color: var(--ink);
  font: 700 14px/1 var(--display);
  cursor: pointer;
}
.chip-pick:hover { background: var(--card-pick); }

.link-out {
  appearance: none;
  -webkit-appearance: none;
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 0 2px;
  border: 0;
  border-bottom: 2px solid currentColor;
  background: none;
  color: var(--acid-text);
  font: 700 14px/1 var(--display);
  cursor: pointer;
}

/* the intro screen has no fixed footer bar to clear */
.shell[data-mode="onboarding"] { padding-bottom: 40px; }

/* ---------- board ---------- */
.board { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.board-when { color: var(--faint); }
/* the poll countdown lives at the very end of the card, a quiet "still live" */
.board-foot { color: var(--faintest); text-align: center; padding-top: 2px; }
/* ...unless the feed has gone stale, when it is the one warning on the board */
.board-foot.is-stale { color: var(--live-text); }

/* court index - a horizontal pill row, the current court filled */
.index { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; padding-bottom: 2px; margin: 0 -20px; padding-left: 20px; padding-right: 20px; }
.index::-webkit-scrollbar { display: none; }
.chip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 14px;
  border: 2px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  flex: none;
  text-decoration: none;
  min-height: 40px;
  font: inherit;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
}
.chip .lbl { color: var(--dim); }
.chip .cdot { width: 7px; height: 7px; border-radius: 50%; background: var(--line-box); border: 1.3px solid var(--ink); flex: none; }
.chip[data-picked="true"]:not([data-live="true"]) .cdot { background: var(--ink); }
.chip[data-live="true"] .cdot { background: var(--live-text); }
.chip[data-live="true"] .lbl,
.chip[data-picked="true"] .lbl { color: var(--ink); }
.chip[aria-current="true"] { background: var(--ink); border-color: var(--ink); }
.chip[aria-current="true"] .lbl { color: var(--page-bg); }
.chip[aria-current="true"] .cdot { border-color: var(--page-bg); }

/* court sections: one court per screen below desktop, swiped between with
   native scroll-snap - the index chips above double as tabs into the same
   scroller, so tap-to-jump and swipe land on the same mechanism. */
.columns {
  display: flex;
  flex-direction: row;
  gap: 0;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  margin: 0 -20px;
}
.columns::-webkit-scrollbar { display: none; }
.col {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
  flex: none;
  width: 100%;
  padding: 0 20px;
  box-sizing: border-box;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  scroll-margin-left: 20px;
  /* .colhead can't self-correct via sticky here (see below), so the anchor
     jump needs its own allowance for the dock or the heading lands under it. */
  scroll-margin-top: var(--dock-h);
}
/* .columns' own overflow-x makes it a scroll container, which hijacks any
   descendant's position:sticky to stick relative to *it* instead of the
   page - .colhead would render permanently offset by --dock-h rather than
   actually sticking as you scroll. One court per screen rarely runs long
   enough to need the hand-off anyway, so switch it off here the same way
   the desktop grid already does below. */
.colhead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  position: static;
  padding-top: 0;
  padding-bottom: 9px;
  background: none;
  border-bottom: 2.5px solid var(--ink);
}
.courtname {
  font-family: var(--display);
  font-size: 22px;
  line-height: 1;
  font-weight: 800;
  font-stretch: 80%;
  letter-spacing: -.025em;
  text-transform: uppercase;
  margin: 0;
}
.colhead .lbl { color: var(--faint); }

/* A search narrows the board in place - same columns, only the matching
   cards. On a phone the one-court-per-screen scroller would strand a lone
   result on an empty page, so a filtered board stacks its courts instead. */
.shell[data-mode="filtered"] .columns {
  flex-direction: column;
  gap: 20px;
  overflow: visible;
  scroll-snap-type: none;
}
.shell[data-mode="filtered"] .col {
  width: 100%;
  scroll-snap-align: none;
}
/* the court index is the desktop rail's jump nav; on a phone the court
   headers in the stack already say which court, so drop the extra strip */
.shell[data-mode="filtered"] > .index { display: none; }

/* ---------- cards ---------- */
.card {
  background: var(--card);
  border: 2.5px solid var(--ink);
  border-radius: 18px;
  /* tighter than the old stacked card - the reclaimed room goes to the
     court so the pitch reads with some depth, not as a thin strip */
  padding: 12px 17px;
  display: flex;
  flex-direction: column;
  gap: 9px;
  min-height: 44px;
  box-shadow: 5px 5px 0 var(--ink-shadow);
}
.card[role="button"] { cursor: pointer; }
.card[role="button"]:focus-visible { outline: 2px solid var(--acid); outline-offset: 2px; }
/* min-height locks the row to the tallest control it can hold (the bell
   pill), so a card showing only the live badge - or nothing - is the same
   height as one with the pill. Part of keeping every card identical. */
.card-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 34px; }
.card-top .lbl { color: var(--dim); }
.card-ctl { display: flex; align-items: center; gap: 10px; flex: none; }
.pair { font-size: 15px; line-height: 1.4; margin: 0; }

/* ---------- the court: the two teams across a top-down padel court ----------
   The players band is drawn as a court seen from above - a team on each
   half, the net down the centre, service lines at the quarters. */
.court {
  position: relative;
  border-radius: 12px;
  padding: 11px;
  background-color: var(--court);
  background-image:
    linear-gradient(90deg, transparent calc(25% - .75px), var(--court-line) calc(25% - .75px), var(--court-line) calc(25% + .75px), transparent calc(25% + .75px)),
    linear-gradient(90deg, transparent calc(75% - .75px), var(--court-line) calc(75% - .75px), var(--court-line) calc(75% + .75px), transparent calc(75% + .75px));
  box-shadow: inset 0 0 0 1.5px var(--court-line);
  overflow: hidden;
}
.court-net {
  position: absolute;
  inset: 0 auto 0 50%;
  width: 2px;
  transform: translateX(-1px);
  background: var(--court-net);
}
.court-tee {
  position: absolute;
  left: 25%;
  right: 25%;
  top: 50%;
  height: 1.5px;
  transform: translateY(-.75px);
  background: var(--court-line);
}
.card[data-state="live"] .court-net { background: var(--live); }

/* The board sits dead-centre on the net; the two players of each team
   sit at the front and back of their half, so the board drops into the
   gap between the name rows instead of colliding with them. A small
   centre gap keeps a long name off the board's corners. */
.halves { position: relative; display: grid; grid-template-columns: 1fr 1fr; column-gap: 28px; }
/* deep enough that the two players read as sitting at their own baselines,
   with open court between them - and, on live/finished, room for the board.
   Fixed for every state (and for the wider deep-third-set board, which
   gains columns sideways, not height) so all cards match. */
.half { display: flex; align-items: stretch; min-height: 96px; min-width: 0; }
.names { display: flex; flex-direction: column; justify-content: space-between; gap: 6px; min-width: 0; width: 100%; }
.half-r .names { align-items: flex-end; text-align: right; }
.pline { display: flex; align-items: center; gap: 8px; min-width: 0; max-width: 100%; }
/* the right team mirrors - flag to the outside */
.half-r .pline { flex-direction: row-reverse; }
.pname {
  font-size: 14px;
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -.005em;
  color: var(--court-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pname .sd { color: var(--court-dim); font-weight: 500; }

.flag {
  width: 16px;
  height: 11px;
  flex: none;
  border-radius: 2px;
  border: 1px solid rgba(18, 36, 26, .55);
  object-fit: cover;
  display: block;
}
.flag-blank { background: var(--court-line); }

/* the score: an umpire's board on the net - centred on it, a row per
   team, a column per set played (the score up to now). */
.court-board {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  display: grid;
  align-items: center;
  gap: 3px 8px;
  padding: 5px 9px;
  background: var(--board-bg);
  border: 1.5px solid var(--court-net);
  border-radius: 8px;
  font: 600 12px/1 var(--mono);
}
.bcell { color: var(--court-ink); text-align: center; min-width: 9px; }
/* the trailing team reads one step quieter */
.bcell-dim { color: var(--court-lose); }

.card-foot {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--faint);
  border-top: 1.5px solid var(--line-soft);
  /* one line of text, sitting in equal air on three sides: the same gap
     court-to-divider (negative margin against the card's stack gap),
     divider-to-text, and text-to-card-edge (negative margin against the
     card's pad). line-height near 1 so the padding is the visible gap. */
  margin-top: -3px;
  padding-top: 6px;
  margin-bottom: -6px;
  line-height: 1.1;
}
/* a fixed start time ("Starts 9:00") reads firmer than a soft "Not before" */
.card-foot-firm { color: var(--ink); }
/* a card with nothing to say in the foot (no clock, no scheduled time) still
   reserves the exact same row - same markup, placeholder text - so it stands
   as tall as one that does. visibility:hidden keeps the box (and its space)
   while hiding the divider and the dummy time. */
.card-foot-blank { visibility: hidden; }

.card[data-state="finished"] { border-color: var(--line-soft); opacity: .4; box-shadow: none; }
.card[data-state="live"] {
  border-color: var(--ink);
  border-left: 5px solid var(--live);
  padding-left: 13px;
}
.card[data-state="upNext"] .card-top .lbl { color: var(--ink); }
.card.is-picked { background: var(--card-pick); }

/* ---------- the occasion: a final, or the clasico ----------
   Direction G. The foil band across the head of the card is the light source
   and the court is the surface the light lands on, which is the reading that
   makes the two belong to each other rather than merely sit together. See
   public/occasion.js for which matches get here - almost none, deliberately. */
.card-occasion { border-color: var(--gold); }

/* The band. Hard-edged facets rather than a soft gradient: that is how a real
   foil stamp catches light, at one angle and not as a wash. Full-bleed, so the
   negative margins cancel the card's padding. */
.bill {
  position: relative;
  overflow: hidden;
  margin: -12px -17px 0;
  padding: 11px 17px 10px;
  background: linear-gradient(100deg, var(--gold-deep) 0 34%, var(--gold) 34% 61%, var(--gold-deep) 61%);
  color: var(--gold-ink);
  /* the card's 18px radius less its 2.5px border */
  border-radius: 15.5px 15.5px 0 0;
  display: flex;
  align-items: center;
  gap: 11px;
}
/* a live card carries a 5px coral edge, so its band starts further in */
.card[data-state="live"] .bill { margin-left: -13px; padding-left: 13px; }
.bill-rays {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: repeating-conic-gradient(from 0deg at 50% 130%,
    rgba(42, 30, 8, .11) 0 1.4deg, transparent 1.4deg 7.5deg);
}
.bill-t {
  position: relative;
  margin: 0;
  font: 800 21px/1 var(--display);
  font-stretch: 80%;
  letter-spacing: -.02em;
  text-transform: uppercase;
}
/* "The final" beside "El Clásico" - the second fact, when there are two */
.bill-sub {
  position: relative;
  margin: 0 0 0 auto;
  color: var(--gold-ink);
  opacity: .66;
}
.bill .mark { width: 22px; height: 22px; flex: none; position: relative; }

/* The court, engraved. The rays are the bottom layer of the background stack
   so both service lines still paint over them and the pitch reads as a pitch;
   they radiate from the centre of the net, which is the point both teams face.
   Court colours do not change with the theme, so neither do these. */
.court-occasion {
  background-image:
    linear-gradient(90deg, transparent calc(25% - .75px), var(--court-line) calc(25% - .75px), var(--court-line) calc(25% + .75px), transparent calc(25% + .75px)),
    linear-gradient(90deg, transparent calc(75% - .75px), var(--court-line) calc(75% - .75px), var(--court-line) calc(75% + .75px), transparent calc(75% + .75px)),
    repeating-conic-gradient(from 0deg at 50% 50%, rgba(227, 176, 75, .15) 0 1.6deg, transparent 1.6deg 8.5deg);
  box-shadow:
    inset 0 0 0 1.5px rgba(227, 176, 75, .55),
    inset 0 0 0 5px var(--court),
    inset 0 0 0 6.5px rgba(227, 176, 75, .3);
}

/* The medallion, struck on the net - the umpire's board's own slot, using the
   same knock-out so the court lines stop cleanly at its edge. It only appears
   on a match still to play: once there is a score the score wins that slot,
   and every corner of this little court already has a name in it, so there is
   nowhere honest to park a mark instead. */
.crest {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--board-bg);
  border: 1.5px solid rgba(227, 176, 75, .85);
  display: grid;
  place-items: center;
  color: #e3b04b;
  box-shadow: inset 0 0 0 2.5px var(--board-bg), inset 0 0 0 3.5px rgba(227, 176, 75, .45);
}
.crest .mark { width: 26px; height: 26px; display: block; }

/* ---------- the glint ----------
   Light passing across the gold, once, when the card arrives on screen (the
   IntersectionObserver in public/app.js adds .is-lit). A card that glitters on
   a loop is a slot machine; a card that catches the light as you scroll onto
   it is foil. Nothing else moves: not the court, not the score, and never the
   bell - an animated alert control would read as a control that is doing
   something. */
.sheen {
  position: absolute;
  top: -30%;
  bottom: -30%;
  left: 0;
  width: 26%;
  z-index: 3;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 252, 238, .6) 48%, transparent);
  /* only legible over gold; over anything dark a screen blend is a white bar */
  mix-blend-mode: screen;
  transform: translateX(-170%) skewX(-18deg);
}
.is-lit .sheen { animation: glint .86s cubic-bezier(.25, .7, .3, 1) forwards; }
/* struck metal, so the medallion's sweep is wider and lands late - the same
   light reaching a curved surface after the flat one */
.crest .sheen { width: 46%; animation-delay: .26s; }
@keyframes glint {
  from { opacity: 1; transform: translateX(-170%) skewX(-18deg); }
  to { opacity: 1; transform: translateX(330%) skewX(-18deg); }
}

@media (prefers-reduced-motion: reduce) {
  .is-lit .sheen { animation: none; }
}

/* live badge - fill + ink text, legible on paper without darkening the fill */
.badge-live {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: var(--live);
  color: var(--on-live);
  font: 800 11px/1 var(--display);
  letter-spacing: .05em;
  text-transform: uppercase;
  border: 1.5px solid var(--ink);
  border-radius: 999px;
  padding: 5px 10px 5px 7px;
  flex: none;
}
.badge-live .dot-live { background: var(--on-live); border-color: var(--on-live); }

/* the pick control: the acid bell pill is now a real button and the *only*
   alert toggle - tapping the rest of the card opens the detail modal. live
   and finished matches have nothing left to alert about, so they show no
   control at all. Shared by the card and the modal footer. */
.bell-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  border: 2px solid var(--ink);
  border-radius: 999px;
  background: var(--card);
  color: var(--ink);
  font: 800 11px/1 var(--display);
  letter-spacing: .01em;
  padding: 7px 12px 7px 10px;
  white-space: nowrap;
  cursor: pointer;
  /* the same hard offset the cards and chips carry - gives the control a bit
     of volume so pressing it lands with a click */
  box-shadow: 3px 3px 0 var(--ink-shadow);
  transition: transform .07s ease, box-shadow .07s ease;
}
.bell-pill .ico { width: 14px; height: 14px; }
.bell-pill.on { background: var(--acid); color: var(--on-acid); }
.bell-pill:active { transform: translate(3px, 3px); box-shadow: 0 0 0 var(--ink-shadow); }
.bell-pill:focus-visible { outline: 2px solid var(--acid); outline-offset: 2px; }
.bell-pill.on:focus-visible { outline-color: var(--ink); }

/* once the visitor knows the control (has a pick or a subscription) the word
   drops and it shrinks to the bell alone */
.bell-pill.pick-compact {
  width: 34px;
  height: 34px;
  padding: 0;
  justify-content: center;
}
.bell-pill.pick-compact .ico { width: 16px; height: 16px; }

/* ---------- match detail modal ----------
   Direction B: the board card's top-down court, opened up - a bigger
   headshot on each half, the umpire's board on the net, the full line-up
   below. Opened by tapping a card; the acid bell is the only control that
   does not open it. */
.m-scrim {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: grid;
  place-items: start center;
  overflow-y: auto;
  padding: 20px;
  background: rgba(0, 0, 0, .55);
}
.m-modal {
  width: 100%;
  max-width: 390px;
  margin: auto;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 18px 18px 16px;
  background: var(--card);
  border: 2.5px solid var(--ink);
  border-radius: 18px;
  box-shadow: 6px 6px 0 var(--ink-shadow);
  animation: modal-in .16s ease-out;
}
@keyframes modal-in {
  from { transform: translateY(8px); }
}
@media (prefers-reduced-motion: reduce) { .m-modal { animation: none; } }

.m-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; }
.m-round {
  margin: 4px 0 0;
  font-family: var(--display);
  font-size: 19px;
  line-height: 1.15;
  font-weight: 800;
  font-stretch: 82%;
  letter-spacing: -.02em;
  text-transform: uppercase;
  color: var(--ink);
}
.m-close {
  flex: none;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 2.5px solid var(--ink);
  border-radius: 50%;
  background: var(--card);
  color: var(--ink);
  display: grid;
  place-items: center;
  cursor: pointer;
}
.m-close:focus-visible { outline: 2px solid var(--acid); outline-offset: 2px; }

.m-status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  align-self: flex-start;
  padding: 5px 10px;
  border: 1.5px solid var(--ink);
  border-radius: 999px;
  background: var(--line-soft);
  color: var(--ink);
  font: 800 10.5px/1 var(--display);
  letter-spacing: .08em;
  text-transform: uppercase;
}
.m-status.live { background: var(--live); color: var(--on-live); }
.m-status.next { background: var(--acid); color: var(--on-acid); }
.m-status .m-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }

/* Unlike the card, the modal's court stands on its end: a real ~10x20m
   court seen from behind the baseline, one team on the near half and one on
   the far. The width is capped and centred so the panel never runs the full
   modal height, and the service lines sit at the real ~15% / ~85% of the
   court's length rather than the card's looser quarters. */
.m-court {
  position: relative;
  width: min(100%, 255px);
  margin-inline: auto;
  aspect-ratio: 5 / 9;
  display: flex;
  border-radius: 12px;
  padding: 16px 14px;
  background-color: var(--court);
  background-image:
    linear-gradient(180deg, transparent calc(15% - .75px), var(--court-line) calc(15% - .75px), var(--court-line) calc(15% + .75px), transparent calc(15% + .75px)),
    linear-gradient(180deg, transparent calc(85% - .75px), var(--court-line) calc(85% - .75px), var(--court-line) calc(85% + .75px), transparent calc(85% + .75px));
  box-shadow: inset 0 0 0 1.5px var(--court-line);
  overflow: hidden;
}
.m-net {
  position: absolute;
  inset: 50% 0 auto 0;
  height: 2px;
  transform: translateY(-1px);
  background: var(--court-net);
}
.m-court.is-live .m-net { background: var(--live); }
.m-tee {
  position: absolute;
  top: 15%;
  bottom: 15%;
  left: 50%;
  width: 1.5px;
  transform: translateX(-.75px);
  background: var(--court-line);
}
.m-court .court-board { font: 600 13px/1 var(--mono); padding: 6px 9px; gap: 3px 9px; }

/* the occasion, in the modal: the same band and the same engraving, one size
   up. Opening the card should feel like opening the same object. */
.m-modal-occasion { border-color: var(--gold); }
.m-bill {
  position: relative;
  overflow: hidden;
  margin: -18px -18px 0;
  padding: 13px 18px 12px;
  background: linear-gradient(100deg, var(--gold-deep) 0 34%, var(--gold) 34% 61%, var(--gold-deep) 61%);
  color: var(--gold-ink);
  border-radius: 15.5px 15.5px 0 0;
  display: flex;
  align-items: center;
  gap: 12px;
}
.m-bill .mark { width: 24px; height: 24px; flex: none; position: relative; }
.m-bill .bill-t { font-size: 23px; }

.m-court-occasion {
  background-image:
    linear-gradient(180deg, transparent calc(15% - .75px), var(--court-line) calc(15% - .75px), var(--court-line) calc(15% + .75px), transparent calc(15% + .75px)),
    linear-gradient(180deg, transparent calc(85% - .75px), var(--court-line) calc(85% - .75px), var(--court-line) calc(85% + .75px), transparent calc(85% + .75px)),
    repeating-conic-gradient(from 0deg at 50% 50%, rgba(227, 176, 75, .15) 0 1.6deg, transparent 1.6deg 8.5deg);
  box-shadow:
    inset 0 0 0 1.5px rgba(227, 176, 75, .55),
    inset 0 0 0 5px var(--court),
    inset 0 0 0 6.5px rgba(227, 176, 75, .3);
}
/* At 64px the drawing reads as a drawing rather than as a mark - the card's
   26px version only has to be recognisable. */
.m-crest { width: 64px; height: 64px; }
.m-crest .mark { width: 32px; height: 32px; }

.m-halves {
  position: relative;
  flex: 1;
  display: grid;
  grid-template-rows: 1fr 1fr;
  row-gap: 20px;
}
.m-half {
  position: relative;
  display: flex;
  justify-content: space-evenly;
  gap: 14px;
}
/* each player takes an equal share of the half and no more, so a long
   surname wraps by its headshot instead of stretching the block wide
   and being clipped by the court's edge */
.m-cp { flex: 1 1 0; min-width: 0; max-width: 140px; }
.m-half-t { align-items: flex-start; }
.m-half-b { align-items: flex-end; }
.m-half.lost { opacity: .5; }
.m-won {
  position: absolute;
  left: 0;
  padding: 3px 5px;
  border-radius: 4px;
  background: var(--acid);
  color: var(--on-acid);
  font: 800 8.5px/1 var(--display);
  letter-spacing: .1em;
  text-transform: uppercase;
}
/* the players sit at the back of each half, so the winner tag goes the
   other way - tucked against the net on the winning side. */
.m-half-t .m-won { bottom: -4px; }
.m-half-b .m-won { top: -4px; }

.m-cp { display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; }
/* The near half is flipped so the name sits above the headshot, pointing at
   the net - the mirror of the far half, whose name sits below for the same
   reason. Both teams' avatars stay anchored to their own baselines. */
.m-half-b .m-cp { flex-direction: column-reverse; }
.m-av-wrap {
  position: relative;
  width: 58px;
  height: 58px;
  flex: none;
  border: 2px solid var(--court-net);
  border-radius: 50%;
  overflow: hidden;
  background: var(--court-mono);
}
.m-av { display: block; width: 100%; height: 100%; object-fit: cover; }

/* Loading state: a pulsing tinted disc over the wrapper while the profile
   resolves or the headshot decodes. Same opacity pulse the live dots use. */
.m-av-skel {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  animation: pulse 1.9s ease-in-out infinite;
}
.m-av-wrap .m-av-skel { background: var(--court-mono); color: var(--court-mono-fg); }
.m-lu-av .m-av-skel { background: var(--mono-chip); color: var(--mono-chip-fg); }
.m-av-skel svg { display: block; width: 62%; height: 62%; }
/* The flag is pulled out of the name's own box and hung in the left margin, so
   the surname stays centred on the headshot however many lines it wraps to. */
.m-cp-row { position: relative; display: flex; justify-content: center; max-width: 100%; min-width: 0; }
.m-cp-row .flag { position: absolute; right: 100%; margin-right: 6px; top: 1px; }
.m-sname {
  font: 700 13.5px/1.1 var(--display);
  letter-spacing: -.01em;
  color: var(--court-ink);
  min-width: 0;
  overflow-wrap: break-word;
}
.m-sname .sd { color: var(--court-dim); font-weight: 500; }

.m-lineup { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 14px; }
.m-lu { display: flex; align-items: center; gap: 9px; min-width: 0; }
.m-lu-av {
  position: relative;
  width: 32px;
  height: 32px;
  flex: none;
  border: 1.5px solid var(--ink);
  border-radius: 50%;
  overflow: hidden;
  background: var(--mono-chip);
}
.m-mono {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  background: var(--mono-chip);
  color: var(--mono-chip-fg);
  font: 800 11px/1 var(--display);
  letter-spacing: .02em;
}
.m-lu-txt { min-width: 0; }
.m-full {
  margin: 0;
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -.01em;
  color: var(--ink);
}
.m-nick { margin: 1px 0 0; font-size: 11px; color: var(--faint); }
.m-nick::before { content: '\201C'; }
.m-nick::after { content: '\201D'; }

.m-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding-top: 12px;
  border-top: 1.5px solid var(--line-soft);
  font: 400 11.5px/1.4 var(--mono);
  color: var(--faint);
}
.m-foot .ico { flex: none; }
.m-foot-row { justify-content: space-between; }
.m-foot-row > span { display: inline-flex; align-items: center; gap: 6px; min-height: 20px; }

/* first-run nudge above the court list - see BoardHint in app.js */
.board-hint {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  margin: 0;
  padding: 10px 12px;
  border: 1.5px solid var(--acid-dim);
  border-radius: 12px;
  color: var(--faint);
  font: 400 12px/1.5 var(--mono);
}
.board-hint .ico { flex: none; margin-top: 1px; color: var(--acid-text); }

/* iOS Home Screen install guidance - see IosInstallCard in app.js. A real
   card at the top of the board on mobile; hidden on desktop (below). */
.ios-install {
  display: flex;
  flex-direction: column;
  gap: 12px;
  background: var(--card);
  border: 2.5px solid var(--ink);
  border-radius: 18px;
  padding: 16px;
  box-shadow: 5px 5px 0 var(--ink-shadow);
}
.ios-kicker {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font: 800 11px/1 var(--display);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--acid-text);
}
.ios-kicker .ico { flex: none; color: var(--acid-text); }
.ios-lead { margin: 0; font-size: 14px; line-height: 1.45; color: var(--ink); }
.ios-copy {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font: 800 12px/1 var(--display);
  letter-spacing: .02em;
  text-transform: uppercase;
  background: var(--acid);
  color: var(--on-acid);
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 10px 15px;
  cursor: pointer;
  box-shadow: 3px 3px 0 var(--ink-shadow);
}
.ios-copy.is-done {
  background: transparent;
  color: var(--acid-text);
  box-shadow: none;
}
.ios-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.ios-steps li {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  font: 400 12.5px/1.5 var(--mono);
  color: var(--faint);
}
.ios-n {
  flex: none;
  width: 19px;
  height: 19px;
  margin-top: 1px;
  border: 1.5px solid var(--acid-dim);
  border-radius: 50%;
  color: var(--acid-text);
  display: grid;
  place-items: center;
  font: 700 10px/1 var(--display);
}
.ios-n .ico { width: 11px; height: 11px; }

/* The install flow as a simulated iPhone - see IosInstallDemo in app.js. One
   9s clock drives everything: the finger, each sheet, the home screen, the
   captions and the bar, which fills 0-100 over the loop. Keep it CSS-only - a
   re-render restarts an animation, which is what made a per-step bar jump in
   the design draft. The screen is drawn in real iPhone points (393 x 852) and
   scaled down by --iv-k, so the timeline below can aim at real coordinates.
   Timeline, in seconds: tap ••• 1.0, Share 1.65, drag the sheet 2.6-3.0, Add
   to Home Screen 3.55, Add 4.35, home screen 4.6, icon pops 5.05-5.55, tap it
   6.4, app opens 6.45-6.9, Enable alerts 7.8. Safari is drawn in iOS greys,
   the app in its own tokens. */
.iv { display: flex; flex-direction: column; align-items: center; gap: 14px; padding-top: 4px; --iv-k: .56; }
.iv * {
  animation-duration: 9s;
  animation-iteration-count: infinite;
  animation-timing-function: ease-in-out;
  animation-fill-mode: both;
}
.iv-device {
  position: relative;
  width: calc(393px * var(--iv-k));
  height: calc(852px * var(--iv-k));
  padding: 7px;
  box-sizing: content-box;
  border-radius: 40px;
  background: #0b0b0c;
  box-shadow: 0 0 0 1.5px #3a3a3c, 0 0 0 4px #1a1a1c, 0 18px 34px rgba(0, 0, 0, .45);
}
.iv-clip { position: relative; width: 100%; height: 100%; border-radius: 33px; overflow: hidden; }
.iv-scr {
  position: absolute;
  left: 0;
  top: 0;
  width: 393px;
  height: 852px;
  transform: scale(var(--iv-k));
  transform-origin: 0 0;
  overflow: hidden;
  font: 400 17px/1.25 -apple-system, 'SF Pro Text', system-ui, 'Helvetica Neue', sans-serif;
  color: #fff;
  background: #000;
  text-align: left;
}
.iv-scr > *, .iv-home > *, .iv-safari > *, .iv-app > * { position: absolute; }
.iv-scr svg { flex: none; }

/* home screen */
.iv-home {
  inset: 0;
  background:
    radial-gradient(120% 70% at 20% 10%, #2d6b4d 0%, transparent 60%),
    radial-gradient(90% 60% at 90% 80%, #c48a2a 0%, transparent 60%),
    linear-gradient(160deg, #123a2a, #0b1d2e 70%);
}
.iv-grid {
  left: 27px;
  top: 80px;
  display: grid;
  grid-template-columns: repeat(4, 64px);
  column-gap: 27px;
  row-gap: 16px;
  transform-origin: 50% 40%;
  animation-name: iv-grid;
}
.iv-ap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: 64px;
  font-size: 12px;
  line-height: 14px;
  text-align: center;
  white-space: nowrap;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .3);
}
.iv-ap i, .iv-ap img { display: block; width: 64px; height: 64px; border-radius: 15px; }
.iv-me { transform-origin: 32px 32px; animation-name: iv-me; }
.iv-srch {
  left: 50%;
  top: 690px;
  transform: translateX(-50%);
  height: 30px;
  padding: 0 12px 0 8px;
  border-radius: 15px;
  background: rgba(255, 255, 255, .2);
  display: flex;
  gap: 4px;
  align-items: center;
  font-size: 13px;
}
.iv-srch svg { width: 15px; height: 15px; }
.iv-dock {
  left: 12px;
  right: 12px;
  top: 750px;
  height: 92px;
  border-radius: 34px;
  background: rgba(255, 255, 255, .18);
  display: flex;
  justify-content: space-around;
  align-items: center;
  padding: 0 10px;
}
.iv-dock i { display: block; width: 64px; height: 64px; border-radius: 15px; }

/* the site, as it is under Safari's chrome and again as the installed app */
.iv-safari { inset: 0; background: var(--page-bg); overflow: hidden; transform-origin: 39% 37%; animation-name: iv-safari; }
.iv-pg {
  inset: 0;
  padding: 66px 18px 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  font-family: var(--display);
  color: var(--ink);
}
.iv-pg p { margin: 0; }
.iv-wm { font: 800 17px/1 var(--display); }
.iv-wm i {
  font: 700 9px/1 var(--display);
  letter-spacing: .1em;
  color: var(--on-acid);
  background: var(--acid);
  padding: 3px 5px;
  border-radius: 4px;
  vertical-align: 3px;
  margin-left: 4px;
}
.iv-pg .iv-hero { margin: 4px 0 6px; font: 800 52px/.95 var(--display); letter-spacing: -.02em; }
.iv-card {
  position: relative;
  background: var(--card);
  border: 2px solid var(--ink);
  border-radius: 14px;
  box-shadow: 4px 4px 0 var(--ink-shadow);
  padding: 12px 16px;
}
.iv-court { font: 600 11px/1.4 var(--mono); color: var(--faint); letter-spacing: .04em; }
.iv-pair { font: 700 18px/1.25 var(--display); }
.iv-vs { font: 400 12px/1.3 var(--mono); color: var(--faint); }
.iv-bell {
  right: 12px;
  top: 12px;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--acid);
  color: var(--on-acid);
  display: grid;
  place-items: center;
}
.iv-card .iv-bell { position: absolute; }
.iv-bell svg { width: 18px; height: 18px; }

/* Safari's chrome */
.iv-dim { inset: 0; background: rgba(0, 0, 0, .45); animation-name: iv-dim; }
.iv-tb { left: 16px; right: 16px; top: 772px; height: 48px; display: flex; gap: 8px; }
.iv-tbtn, .iv-url {
  background: rgba(58, 58, 62, .92);
  border-radius: 24px;
  box-shadow: inset 0 0 0 .5px rgba(255, 255, 255, .18), 0 4px 16px rgba(0, 0, 0, .3);
}
.iv-tbtn { width: 48px; height: 48px; display: grid; place-items: center; }
.iv-url { flex: 1; display: flex; align-items: center; justify-content: center; gap: 8px; font-size: 16px; }
.iv-url b { font-size: 13px; font-weight: 600; opacity: .8; }
.iv-menu {
  right: 16px;
  top: 520px;
  width: 250px;
  border-radius: 26px;
  background: rgba(50, 50, 54, .98);
  overflow: hidden;
  box-shadow: 0 10px 40px rgba(0, 0, 0, .5);
  transform-origin: 226px 276px;
  animation-name: iv-menu;
  animation-timing-function: cubic-bezier(.3, 1.3, .5, 1);
}
.iv-mr, .iv-sr {
  display: flex;
  justify-content: space-between;
  align-items: center;
  white-space: nowrap;
}
.iv-mr { height: 48px; padding: 0 18px; border-top: .5px solid rgba(255, 255, 255, .12); }
.iv-mr:first-child, .iv-sr:first-child { border-top: 0; }
.iv-mr-share { animation-name: iv-hl-share; }
.iv-sheet {
  left: 0;
  right: 0;
  top: 360px;
  bottom: 0;
  background: #1c1c1e;
  border-radius: 36px 36px 0 0;
  transform-origin: 50% 0;
  animation-name: iv-sheet;
  animation-timing-function: cubic-bezier(.2, .9, .3, 1);
}
.iv-sheet > * { position: absolute; left: 0; right: 0; }
.iv-sh-head { top: 0; height: 86px; display: flex; gap: 12px; align-items: center; padding: 0 20px; }
.iv-sh-head img { width: 48px; height: 48px; border-radius: 11px; }
.iv-sh-head b { display: block; font-size: 17px; font-weight: 600; }
.iv-sh-head small { display: block; font-size: 13px; color: #8e8e93; }
.iv-x {
  margin-left: auto;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: #2c2c2e;
  color: #aeaeb2;
  display: grid;
  place-items: center;
}
.iv-x svg { width: 16px; height: 16px; }
.iv-sh-win { top: 86px; bottom: 0; overflow: hidden; }
.iv-sh-scroll { padding: 0 16px; animation-name: iv-scroll; animation-timing-function: cubic-bezier(.3, .2, .2, 1); }
.iv-sapps { height: 110px; display: flex; gap: 14px; padding-top: 2px; }
.iv-sa { width: 62px; display: flex; flex-direction: column; align-items: center; gap: 6px; font-size: 11px; white-space: nowrap; }
.iv-sa i { display: block; width: 62px; height: 62px; border-radius: 50%; }
.iv-grp { display: block; border-radius: 12px; background: #2c2c2e; overflow: hidden; margin-bottom: 16px; }
.iv-sr { height: 52px; padding: 0 16px; border-top: .5px solid rgba(84, 84, 88, .6); }
.iv-sr-home { animation-name: iv-hl-home; }
.iv-add {
  left: 0;
  right: 0;
  top: 60px;
  bottom: 0;
  background: #1c1c1e;
  border-radius: 36px 36px 0 0;
  padding: 0 16px;
  box-shadow: 0 -10px 30px rgba(0, 0, 0, .4);
  animation-name: iv-add;
  animation-timing-function: cubic-bezier(.2, .9, .3, 1);
}
.iv-add-nav { height: 56px; display: flex; justify-content: space-between; align-items: center; color: #0a84ff; }
.iv-add-nav b { color: #fff; font-weight: 600; }
.iv-add-btn { font-weight: 600; animation-name: iv-add-btn; }
.iv-add-card { margin-top: 14px; background: #2c2c2e; border-radius: 12px; padding: 14px; display: flex; gap: 14px; align-items: center; }
.iv-add-card img { width: 60px; height: 60px; border-radius: 14px; }
.iv-add-f { flex: 1; display: flex; flex-direction: column; gap: 8px; }
.iv-add-name { padding-bottom: 8px; border-bottom: .5px solid rgba(84, 84, 88, .8); display: flex; align-items: center; }
.iv-add-name i { width: 2px; height: 20px; background: #0a84ff; margin-left: 2px; }
.iv-add-f small { font-size: 13px; color: #8e8e93; }
.iv-add-row { justify-content: space-between; padding-block: 12px; }
.iv-tog { position: relative; width: 51px; height: 31px; border-radius: 16px; background: #30d158; }
.iv-tog::after {
  content: '';
  position: absolute;
  right: 2px;
  top: 2px;
  width: 27px;
  height: 27px;
  border-radius: 50%;
  background: #fff;
}
.iv-add-foot { margin: 8px 16px 0; font-size: 13px; color: #8e8e93; }

/* the installed app, opening out of its icon */
.iv-app { inset: 0; background: var(--page-bg); animation-name: iv-app; animation-timing-function: cubic-bezier(.3, .8, .3, 1); }
.iv-bar {
  left: 0;
  right: 0;
  bottom: 0;
  height: 106px;
  background: var(--bar-bg);
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 18px 24px;
}
.iv-cnt { font: 400 14px/1 var(--mono); color: var(--bar-dim); }
.iv-cta {
  display: grid;
  height: 50px;
  padding: 0 20px;
  border-radius: 25px;
  background: var(--acid);
  color: var(--on-acid);
  font: 800 16px/1 var(--display);
  text-transform: uppercase;
  animation-name: iv-cta-press;
}
.iv-cta > span { grid-area: 1 / 1; display: flex; align-items: center; justify-content: center; gap: 8px; }
.iv-cta svg { width: 20px; height: 20px; }
.iv-cta-a { animation-name: iv-cta-a; }
.iv-cta-b { animation-name: iv-cta-b; }

/* status bar, home indicator and the finger, over everything */
.iv-sb {
  left: 0;
  right: 0;
  top: 0;
  height: 54px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 30px 0 44px;
  font-size: 17px;
}
.iv-sb b { font-weight: 600; }
.iv-isl { position: absolute; left: 50%; top: 11px; width: 125px; height: 37px; border-radius: 19px; background: #000; transform: translateX(-50%); }
.iv-sig { display: flex; align-items: flex-end; gap: 2px; }
.iv-sig i { display: block; width: 4px; background: #fff; border-radius: 1px; }
.iv-sig i:nth-child(1) { height: 5px; }
.iv-sig i:nth-child(2) { height: 7px; }
.iv-sig i:nth-child(3) { height: 9px; }
.iv-sig i:nth-child(4) { height: 11px; }
.iv-sig em { display: block; width: 25px; height: 12px; border-radius: 4px; background: #fff; margin-left: 6px; }
.iv-hi { left: 50%; bottom: 8px; width: 134px; height: 5px; border-radius: 3px; background: #fff; transform: translateX(-50%); }
.iv-finger {
  left: 0;
  top: 0;
  width: 54px;
  height: 54px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .38);
  border: 2px solid rgba(255, 255, 255, .9);
  box-shadow: 0 3px 14px rgba(0, 0, 0, .45);
  animation-name: iv-f-move;
}

/* the bar and the captions under the phone */
.iv-prog {
  position: relative;
  display: block;
  align-self: stretch;
  height: 4px;
  border-radius: 2px;
  background: var(--acid-dim);
  overflow: hidden;
}
.iv-prog > span {
  position: absolute;
  inset: 0;
  background: var(--acid);
  transform-origin: left;
  animation-name: iv-fill;
  animation-timing-function: linear;
}
.iv-caps { align-self: stretch; list-style: none; margin: 0; padding: 0; display: grid; min-height: 40px; }
.iv-cap {
  grid-area: 1 / 1;
  display: flex;
  gap: 10px;
  align-items: flex-start;
  font: 400 13px/1.45 var(--mono);
  color: var(--ink);
  animation-timing-function: linear;
}
.iv-cap:nth-child(1) { animation-name: iv-cap0; }
.iv-cap:nth-child(2) { animation-name: iv-cap1; }
.iv-cap:nth-child(3) { animation-name: iv-cap2; }
.iv-cap .ios-n { background: var(--acid); border-color: var(--acid); color: var(--on-acid); }

@keyframes iv-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes iv-f-move {
  0.000% { transform: translate(183px, 613px) scale(1); opacity: 0; }
  3.889% { transform: translate(183px, 613px) scale(1); opacity: 0; }
  5.556% { transform: translate(203px, 613px) scale(1); opacity: 1; }
  10.556% { transform: translate(326px, 769px) scale(1); opacity: 1; }
  11.667% { transform: translate(326px, 769px) scale(0.72); opacity: 1; }
  13.333% { transform: translate(326px, 769px) scale(1); opacity: 1; }
  17.222% { transform: translate(223px, 517px) scale(1); opacity: 1; }
  18.333% { transform: translate(223px, 517px) scale(0.72); opacity: 1; }
  20.000% { transform: translate(223px, 517px) scale(1); opacity: 1; }
  27.222% { transform: translate(203px, 693px) scale(1); opacity: 1; }
  28.889% { transform: translate(203px, 693px) scale(0.8); opacity: 1; }
  33.333% { transform: translate(203px, 383px) scale(0.8); opacity: 1; }
  34.444% { transform: translate(203px, 383px) scale(1); opacity: 1; }
  38.333% { transform: translate(173px, 573px) scale(1); opacity: 1; }
  39.444% { transform: translate(173px, 573px) scale(0.72); opacity: 1; }
  41.111% { transform: translate(173px, 573px) scale(1); opacity: 1; }
  47.222% { transform: translate(331px, 61px) scale(1); opacity: 1; }
  48.333% { transform: translate(331px, 61px) scale(0.72); opacity: 1; }
  50.000% { transform: translate(331px, 61px) scale(1); opacity: 1; }
  52.778% { transform: translate(303px, 133px) scale(1); opacity: 0; }
  62.222% { transform: translate(203px, 443px) scale(1); opacity: 0; }
  65.000% { transform: translate(183px, 413px) scale(1); opacity: 1; }
  70.000% { transform: translate(126px, 285px) scale(1); opacity: 1; }
  71.111% { transform: translate(126px, 285px) scale(0.72); opacity: 1; }
  72.778% { transform: translate(126px, 285px) scale(1); opacity: 1; }
  75.000% { transform: translate(126px, 285px) scale(1); opacity: 0; }
  80.000% { transform: translate(233px, 613px) scale(1); opacity: 0; }
  81.667% { transform: translate(223px, 633px) scale(1); opacity: 1; }
  85.556% { transform: translate(263px, 763px) scale(1); opacity: 1; }
  86.667% { transform: translate(263px, 763px) scale(0.72); opacity: 1; }
  88.333% { transform: translate(263px, 763px) scale(1); opacity: 1; }
  91.667% { transform: translate(263px, 763px) scale(1); opacity: 0; }
  100.000% { transform: translate(183px, 613px) scale(1); opacity: 0; }
}
@keyframes iv-menu {
  0.000% { opacity: 0; transform: scale(.25); }
  11.667% { opacity: 0; transform: scale(.25); }
  14.444% { opacity: 1; transform: scale(1); }
  21.111% { opacity: 1; transform: scale(1); }
  23.333% { opacity: 0; transform: scale(.92); }
  100.000% { opacity: 0; transform: scale(.92); }
}
@keyframes iv-hl-share {
  0.000% { background: transparent; }
  18.222% { background: transparent; }
  18.889% { background: rgba(255,255,255,.16); }
  23.333% { background: rgba(255,255,255,.16); }
  24.444% { background: transparent; }
  100.000% { background: transparent; }
}
@keyframes iv-dim {
  0.000% { opacity: 0; }
  21.667% { opacity: 0; }
  26.111% { opacity: 1; }
  100.000% { opacity: 1; }
}
@keyframes iv-sheet {
  0.000% { transform: translateY(100%); }
  21.667% { transform: translateY(100%); }
  26.667% { transform: none; }
  41.111% { transform: none; }
  45.556% { transform: scale(.93) translateY(-14px); filter: brightness(.6); }
  100.000% { transform: scale(.93) translateY(-14px); filter: brightness(.6); }
}
@keyframes iv-scroll {
  0.000% { transform: none; }
  28.889% { transform: none; }
  33.333% { transform: translateY(-310px); }
  100.000% { transform: translateY(-310px); }
}
@keyframes iv-hl-home {
  0.000% { background: transparent; }
  39.333% { background: transparent; }
  40.000% { background: rgba(255,255,255,.16); }
  43.889% { background: rgba(255,255,255,.16); }
  45.556% { background: transparent; }
  100.000% { background: transparent; }
}
@keyframes iv-add {
  0.000% { transform: translateY(100%); }
  40.556% { transform: translateY(100%); }
  45.000% { transform: none; }
  100.000% { transform: none; }
}
@keyframes iv-add-btn {
  0.000% { opacity: 1; }
  48.222% { opacity: 1; }
  48.667% { opacity: .35; }
  50.000% { opacity: 1; }
  100.000% { opacity: 1; }
}
@keyframes iv-safari {
  0.000% { opacity: 1; transform: none; border-radius: 0; }
  50.556% { opacity: 1; transform: none; border-radius: 0; }
  55.000% { opacity: 0; transform: scale(.22); border-radius: 120px; }
  100.000% { opacity: 0; transform: scale(.22); }
}
@keyframes iv-grid {
  0.000% { transform: scale(1.12); opacity: .3; }
  51.111% { transform: scale(1.12); opacity: .3; }
  55.556% { transform: none; opacity: 1; }
  100.000% { transform: none; opacity: 1; }
}
@keyframes iv-me {
  0.000% { transform: scale(0); opacity: 0; }
  56.111% { transform: scale(0); opacity: 0; }
  59.444% { transform: scale(1.18); opacity: 1; }
  61.667% { transform: none; opacity: 1; }
  71.000% { transform: none; filter: none; }
  71.333% { transform: scale(.9); filter: brightness(.6); }
  72.778% { transform: none; filter: none; }
  100.000% { transform: none; opacity: 1; }
}
@keyframes iv-app {
  0.000% { opacity: 1; clip-path: inset(0px 0px 0px 0px round 0px); }
  3.889% { opacity: 0; clip-path: inset(0px 0px 0px 0px round 0px); }
  4.000% { opacity: 0; clip-path: inset(280px 208px 508px 121px round 15px); }
  71.667% { opacity: 0; clip-path: inset(280px 208px 508px 121px round 15px); }
  72.222% { opacity: 1; clip-path: inset(280px 208px 508px 121px round 15px); }
  76.667% { opacity: 1; clip-path: inset(0px 0px 0px 0px round 0px); }
  100.000% { opacity: 1; clip-path: inset(0px 0px 0px 0px round 0px); }
}
@keyframes iv-cta-a {
  0.000% { opacity: 1; }
  86.667% { opacity: 1; }
  87.778% { opacity: 0; }
  100.000% { opacity: 0; }
}
@keyframes iv-cta-b {
  0.000% { opacity: 0; }
  86.667% { opacity: 0; }
  87.778% { opacity: 1; }
  100.000% { opacity: 1; }
}
@keyframes iv-cta-press {
  0.000% { transform: none; }
  86.444% { transform: none; }
  87.111% { transform: scale(.94); }
  88.333% { transform: none; }
  100.000% { transform: none; }
}
@keyframes iv-cap0 {
  0.000% { opacity: 1; transform: none; }
  22.111% { opacity: 1; transform: none; }
  23.889% { opacity: 0; transform: translateY(-6px); }
  100.000% { opacity: 0; }
}
@keyframes iv-cap1 {
  0.000% { opacity: 0; transform: translateY(6px); }
  22.222% { opacity: 0; transform: translateY(6px); }
  24.444% { opacity: 1; transform: none; }
  51.000% { opacity: 1; transform: none; }
  52.778% { opacity: 0; transform: translateY(-6px); }
  100.000% { opacity: 0; }
}
@keyframes iv-cap2 {
  0.000% { opacity: 0; transform: translateY(6px); }
  51.111% { opacity: 0; transform: translateY(6px); }
  53.333% { opacity: 1; transform: none; }
  97.778% { opacity: 1; transform: none; }
  100.000% { opacity: 0; transform: translateY(-6px); }
}

/* Reduced motion: no phone, no clock - the captions as a plain numbered list,
   which is what the card was before the drawing. */
@media (prefers-reduced-motion: reduce) {
  .iv-device, .iv-prog { display: none; }
  .iv-caps { gap: 9px; }
  .iv-cap { grid-area: auto; font-size: 12.5px; color: var(--faint); }
}

.board-note { color: var(--faint); font: 400 11.5px/1.7 var(--mono); max-width: 46ch; }
.page-note { text-align: center; color: var(--faintest); padding: 4px 0 0; }
.page-note a { color: inherit; text-decoration: underline; }

/* ---------- skeleton ---------- */
.skel { background: var(--card); border: 2.5px solid var(--line); border-radius: 18px; height: 118px; }
.skel-head { height: 33px; border-bottom: 2.5px solid var(--line); }

/* ---------- fixed footer bar ---------- */
.rail-action {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 5;
  background: var(--bar-bg);
  color: var(--bar-ink);
  border-top: 2.5px solid #000;
  padding: 13px 20px calc(13px + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.action-status,
.action-row { width: 100%; max-width: var(--shell-w); }
.action-status { color: var(--bar-dim); }
.action-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.count-label { color: var(--bar-dim); flex: none; }
.cta {
  font: 800 15px/1 var(--display);
  letter-spacing: .01em;
  text-transform: uppercase;
  background: var(--acid);
  color: var(--on-acid);
  border: 2.5px solid #000;
  border-radius: 999px;
  padding: 15px 22px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  cursor: pointer;
  box-shadow: 4px 4px 0 #000;
}
.cta:disabled { opacity: .45; cursor: not-allowed; box-shadow: none; }
.cta-ghost {
  background: transparent;
  color: var(--bar-ink);
  border-color: var(--bar-ink);
  box-shadow: none;
}

/* ---------- motion: a few things, once each ---------- */
@keyframes pulse { 0%, 100% { opacity: 1 } 50% { opacity: .25 } }
@keyframes rise { from { opacity: 0; transform: translateY(14px) } to { opacity: 1; transform: none } }

.dot, .chip[data-live="true"] .cdot, .badge-live .dot-live { animation: pulse 1.9s ease-in-out infinite; }
.col { animation: rise .6s cubic-bezier(.2,.7,.3,1) both; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ---------- phone ---------- */
@media (max-width: 480px) {
  .shell { padding: 0 16px 148px; gap: 20px; }
  .search-dock[data-stuck="true"] { margin: 0 -16px; }
  .index { margin: 0 -16px; padding-left: 16px; padding-right: 16px; }
  .columns { margin: 0 -16px; }
  .col { padding: 0 16px; scroll-margin-left: 16px; }
  .hero { font-size: 54px; }
  .hero-note { font-size: 13px; top: 2px; }

  /* tighten the court a notch for the narrowest screens */
  .court { padding: 10px; }
  .halves { column-gap: 24px; }
  .half { min-height: 84px; }
  .pname { font-size: 13px; letter-spacing: -.01em; }
  .court-board { gap: 3px 6px; padding: 5px 7px; font-size: 11.5px; }
  .bcell { min-width: 8px; }
}

/* ---------- the view switch and the players list ----------
   The board has two views: the schedule (courts and cards) and the field
   (every player in the tournament, with a Follow per row). The switch is the
   only chrome the second view adds; the row itself reuses the board's own
   bell-pill as its Follow control, so Follow and Alert me are visibly the
   same promise. */
.view-switch {
  display: flex;
  padding: 3px;
  gap: 3px;
  border: 2px solid var(--ink);
  border-radius: 999px;
  background: var(--card);
  box-shadow: 3px 3px 0 var(--ink-shadow);
}
.view-switch button {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  border-radius: 999px;
  padding: 10px 0;
  background: none;
  color: var(--dim);
  cursor: pointer;
  font: 800 12px/1 var(--display);
  letter-spacing: .06em;
  text-transform: uppercase;
}
.view-switch button[aria-selected="true"] { background: var(--ink); color: var(--page-bg); }
.view-switch .tab-count { font: 700 10px/1 var(--mono); opacity: .75; }
.view-switch button:focus-visible { outline: 2px solid var(--acid); outline-offset: 2px; }

.field { display: flex; flex-direction: column; gap: 2px; }
.field-group { display: flex; flex-direction: column; }
.field-head { display: flex; align-items: baseline; gap: 8px; padding: 14px 2px 2px; }
.field-head .lbl { margin: 0; }
.field-tail { font: 400 10.5px/1 var(--mono); color: var(--faint); }
.prow {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 2px;
  border-bottom: 1.5px solid var(--line-soft);
}
.prow:last-child { border-bottom: 0; }
.prow-av {
  width: 40px;
  height: 40px;
  flex: none;
  border-radius: 50%;
  overflow: hidden;
  border: 1.5px solid var(--line);
  background: var(--court-mono);
  display: grid;
  place-items: center;
}
.prow-av img { display: block; width: 100%; height: 100%; object-fit: cover; }
.prow-av .m-mono { font: 800 13px/1 var(--display); color: var(--court-mono-fg); }
.prow.is-following .prow-av { border-color: var(--acid); }
/* A row is a pair, so it carries both faces: the partner's tucked behind, the
   way the court card stacks a team. Slightly smaller than a lone headshot was,
   which keeps the pair row exactly as tall as the player row it replaced. */
.prow-duo { display: flex; flex: none; }
.prow-duo .prow-av { width: 38px; height: 38px; }
.prow-duo .prow-av + .prow-av { margin-left: -13px; }
.prow-duo .prow-av:first-child { z-index: 1; }
.prow-txt { flex: 1; min-width: 0; }
.prow-name {
  display: flex;
  align-items: center;
  gap: 6px;
  font: 800 14.5px/1.15 var(--display);
  letter-spacing: -.01em;
  color: var(--ink);
}
.prow-name .flag { border-radius: 2px; }
/* Both names on one line, each with its own flag. They wrap as units - a flag
   stranded at the end of a line reads as a third player. */
.pair-name { flex-wrap: wrap; gap: 5px; row-gap: 1px; font-size: 14px; }
.pair-one { display: inline-flex; align-items: center; gap: 5px; }
.pair-sep { color: var(--faint); font-weight: 400; }
.prow-meta {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-top: 3px;
  font: 400 10.5px/1.25 var(--mono);
  color: var(--faint);
}
.prow-meta .dot-live { width: 6px; height: 6px; }
/* The seed belongs to the pair, not to either player, so it leads the status
   line rather than trailing a name in parentheses - trailing it wrapped onto a
   line of its own the moment two long surnames met a phone. */
.seed-chip {
  flex: none;
  font: 700 9.5px/1 var(--mono);
  color: var(--dim);
  border: 1.5px solid var(--line);
  border-radius: 999px;
  padding: 3px 6px;
}
.prow.is-following .seed-chip { color: var(--acid-text); border-color: var(--acid-dim); }
/* Out of the tournament: the row stays (it is the answer to "where did they
   go") and loses its control - there is nothing left to follow. */
.prow.is-out { opacity: .55; }
.prow-none { font: 400 10.5px/1 var(--mono); color: var(--faint); padding-right: 6px; }

/* The line the schedule prints when a follow is what ticked a card. */
.follow-note {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  border: 1.5px dashed var(--acid-dim);
  border-radius: 10px;
  font: 400 11px/1.4 var(--mono);
  color: var(--faint);
}
.follow-note b { color: var(--acid-text); font-weight: 700; }
.follow-note .ico { flex: none; color: var(--acid-text); }

/* A followed player's name, wherever it is printed on a court: the alert
   pill's bell, shrunk to a glyph and trailing the name. Raw --acid, not
   --acid-text - the court is green in both themes, so the light theme's olive
   would sink into it. The mark is a .pline child, not part of .pname, so the
   name is what the ellipsis eats; -3px against the row's 8px gap leaves 5px,
   tighter than the flag's, so it reads as attached to the name it follows. */
.nm-bell { flex: none; color: var(--acid); margin-left: -3px; }
.half-r .nm-bell { margin-left: 0; margin-right: -3px; }


/* ---------- desktop: split the shell - a fixed left rail, courts fill the rest ----------
   Below 1000px the board is one court per screen, swiped between. On a wide
   screen the brand, headline, search and court list move into a rail that
   never scrolls, and the courts take the width that frees up - as many as
   fit at full card size, the rest a horizontal scroll away, each column
   scrolling its own cards under a sticky header. */
@media (min-width: 1000px) {
  :root { --shell-w: 1140px; }

  /* the intro screen keeps its single centred column and oversized headline */
  .shell[data-mode="onboarding"] { gap: 26px; }
  .shell[data-mode="onboarding"] .hero { font-size: 78px; }
  .shell[data-mode="onboarding"] .hero-wrap { align-self: flex-start; }
  /* the intro column runs the full 1140px, which would stretch the install
     card into a banner; hold it to a readable measure under the headline.
     It stays visible here (unlike the board below) because onboarding has no
     action bar whose reason line could carry the message instead - and an
     iPad in landscape lands on this screen at this width. */
  .shell[data-mode="onboarding"] .ios-install {
    align-self: flex-start;
    width: 100%;
    max-width: 520px;
  }

  /* --- the split: the board, and a search that narrows it in place --- */
  .shell[data-mode="board"],
  .shell[data-mode="filtered"] {
    max-width: 2200px;
    height: 100vh;
    min-height: 0;
    padding: 22px 32px;
    overflow: hidden;
    display: grid;
    grid-template-columns: 312px minmax(0, 1fr);
    grid-template-rows: auto auto auto auto auto minmax(0, 1fr) auto auto;
    grid-template-areas:
      "brand   board"
      "badge   board"
      "hero    board"
      "switch  board"
      "search  board"
      "nav     board"
      "action  board"
      "note    board";
    column-gap: 42px;
    row-gap: 14px;
    align-items: start;
  }

  .shell[data-mode="board"] > .topbar,
  .shell[data-mode="filtered"] > .topbar { grid-area: brand; padding-top: 0; }
  .shell[data-mode="board"] > .view-switch,
  .shell[data-mode="filtered"] > .view-switch { grid-area: switch; }
  .shell[data-mode="board"] > .now-badge,
  .shell[data-mode="filtered"] > .now-badge { grid-area: badge; justify-self: start; }
  .shell[data-mode="board"] > .hero-wrap,
  .shell[data-mode="filtered"] > .hero-wrap { grid-area: hero; }
  .shell[data-mode="board"] > .search-dock,
  .shell[data-mode="filtered"] > .search-dock { grid-area: search; }
  .shell[data-mode="board"] > .index,
  .shell[data-mode="filtered"] > .index { grid-area: nav; align-self: stretch; min-height: 0; }
  .shell[data-mode="board"] > .rail-action,
  .shell[data-mode="filtered"] > .rail-action { grid-area: action; }
  .shell[data-mode="board"] > .board,
  .shell[data-mode="filtered"] > .board { grid-area: board; align-self: stretch; min-height: 0; }
  /* The field takes the board's place, two groups to a row, scrolling as one.
     A grid rather than CSS columns: inside a scroll container a multi-column
     box lays its overflow out sideways, where it is clipped - the tail of a
     sixty-player draw would simply not be there. */
  .shell[data-mode="board"] > .field,
  .shell[data-mode="filtered"] > .field {
    grid-area: board;
    align-self: stretch;
    min-height: 0;
    overflow-y: auto;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-content: start;
    align-items: start;
    column-gap: 42px;
    row-gap: 8px;
  }
  .shell[data-mode="board"] > .field > .follow-note,
  .shell[data-mode="filtered"] > .field > .follow-note { grid-column: 1 / -1; }
  /* the credit line sits at the foot of the rail, under the action button */
  .shell[data-mode="board"] > .page-note,
  .shell[data-mode="filtered"] > .page-note {
    grid-area: note;
    align-self: end;
    justify-self: start;
    text-align: left;
    padding: 2px 0 0;
  }
  /* the first-run nudge would be a full-width banner over the courts; on
     desktop the bell buttons still carry their "Alert me" word, which is
     enough of a cue */
  .shell[data-mode="board"] .board-hint { display: none; }
  /* the install card is a mobile concern - the only desktop route to it is an
     iPad in this layout, and the action-bar reason line already says it */
  .shell[data-mode="board"] .ios-install,
  .shell[data-mode="filtered"] .ios-install { display: none; }

  /* rail headline: a fraction of the phone size so it fits the column, its
     hand-note dropping below it instead of floating off to the side */
  .shell[data-mode="board"] .hero,
  .shell[data-mode="filtered"] .hero { font-size: 44px; }
  .shell[data-mode="board"] .hero-note,
  .shell[data-mode="filtered"] .hero-note {
    position: static;
    transform: none;
    margin: 10px 0 0;
    max-width: none;
    white-space: normal;
    text-align: left;
  }

  /* search sits in the rail flow - drop the sticky band treatment */
  .shell[data-mode="board"] > .search-dock[data-stuck="true"],
  .shell[data-mode="filtered"] > .search-dock[data-stuck="true"] {
    position: static;
    margin: 0;
    padding: 0;
    background: none;
  }

  /* court index: a vertical list filling the rail width, the current court
     filled, scrolling inside the rail if the day runs long */
  .shell[data-mode="board"] > .index,
  .shell[data-mode="filtered"] > .index {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 3px;
    overflow-x: visible;
    overflow-y: auto;
    padding: 2px 0;
    margin: 0;
  }
  .shell[data-mode="board"] .chip,
  .shell[data-mode="filtered"] .chip {
    width: 100%;
    justify-content: flex-start;
    border-color: transparent;
    background: none;
    box-shadow: none;
    min-height: 34px;
    padding: 7px 12px;
  }
  .shell[data-mode="board"] .chip:hover,
  .shell[data-mode="filtered"] .chip:hover { background: var(--card-pick); }
  .shell[data-mode="board"] .chip .lbl,
  .shell[data-mode="filtered"] .chip .lbl { flex: 1 1 auto; }
  .shell[data-mode="board"] .chip[aria-current="true"],
  .shell[data-mode="filtered"] .chip[aria-current="true"] { background: var(--ink); }

  /* the action bar stops spanning the viewport - it becomes a dark slab
     pinned to the foot of the rail: same tokens, same single anchor */
  .shell[data-mode="board"] > .rail-action,
  .shell[data-mode="filtered"] > .rail-action {
    position: static;
    inset: auto;
    align-self: end;
    align-items: stretch;
    gap: 10px;
    border: 2.5px solid #000;
    border-radius: 16px;
    box-shadow: 4px 4px 0 #000;
    padding: 14px 15px 15px;
  }
  .shell[data-mode="board"] .action-status,
  .shell[data-mode="board"] .action-row,
  .shell[data-mode="filtered"] .action-status,
  .shell[data-mode="filtered"] .action-row { max-width: none; }
  /* a 312px rail can't sit the count beside the button - stack them */
  .shell[data-mode="board"] .rail-action .action-row,
  .shell[data-mode="filtered"] .rail-action .action-row {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
  }
  .shell[data-mode="board"] .rail-action .cta,
  .shell[data-mode="filtered"] .rail-action .cta { width: 100%; }

  /* the courts: one horizontal row of columns at the phone's card width
     (--col-w). The row itself is the single scroll region - wheel runs
     down the day, trackpad or shift-wheel moves across courts - so there
     are no per-column scrollers to swallow a vertical wheel. Column
     headers stick to the top of that one scrollport as their court
     scrolls past. */
  .shell[data-mode="board"] > .board,
  .shell[data-mode="filtered"] > .board {
    display: flex;
    flex-direction: column;
    gap: 12px;
    height: 100%;
    overflow: hidden;
  }
  .shell[data-mode="board"] .columns,
  .shell[data-mode="filtered"] .columns {
    flex: 1 1 auto;
    flex-direction: row;
    align-items: flex-start;
    min-height: 0;
    gap: 18px;
    margin: 0;
    padding: 0 4px 4px 0;
    overflow: auto;
    scroll-snap-type: none;
    scrollbar-width: thin;
  }
  .shell[data-mode="board"] .columns::-webkit-scrollbar,
  .shell[data-mode="filtered"] .columns::-webkit-scrollbar { display: block; width: 8px; height: 8px; }
  /* full card width, never crushed - as many as the screen holds show at
     once, the rest a scroll away (a wide monitor shows the whole day) */
  .shell[data-mode="board"] .col,
  .shell[data-mode="filtered"] .col {
    flex: 0 0 var(--col-w);
    width: var(--col-w);
    min-width: 0;
    padding: 0;
    scroll-snap-align: none;
  }
  .shell[data-mode="board"] .colhead,
  .shell[data-mode="filtered"] .colhead {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--page-bg);
    padding-top: 2px;
  }
}
