/* FPL System — shared styles. Tokens copied from the FPL System design system (tokens.json v1).
   Light theme only: the dark theme has not been designed or checked (fixture-good sits too close
   to the dark surface). */
:root {
  --surface: #f5f4ef;
  --surface-raised: #ffffff;
  --line: #d9d7ce;
  --ink: #15191a;
  --ink-muted: #545b57;
  --turf: #1d6b43;
  --turf-soft: #dcece2;
  --on-turf: #ffffff;
  /* ink for a label sitting on any saturated fill. Same value as --on-turf
     and a different job: that one names the turf green, this one covers the
     fixture bins, where the label is white because the fill is dark and not
     because it is green. */
  --on-fill: #ffffff;
  --amber: #924900;
  --amber-soft: #f8e7d3;
  --danger: #b3261e;
  --danger-soft: #f9dedb;
  /* Fixture strip: cividis, 4 bins, hardest -> easiest. Always print the word, never a value. */
  /* Fixture bins, hot -> cold, as a DIVERGING pair: intensity grows outward
     from the middle, so the two warm steps and the two cool steps each read as
     a pair. Measured with tools/check_palette.js under Vienot protan/deutan/
     tritan simulation -- worst adjacent separation 19.4 (OKLab x100) against a
     target of 8, worst normal-vision step 21.8 against a floor of 15, every
     label at or above 4.87:1 contrast. Re-run that tool before changing any of
     these four values. */
  --fixture-hard: #b35806;   /* strong orange -- hottest */
  --fixture-tough: #f1a340;  /* pale orange */
  --fixture-fair: #67a9cf;   /* pale blue */
  --fixture-good: #16436e;   /* strong blue -- coldest */
  --font-sans: system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --space-1: 4px; --space-2: 8px; --space-4: 16px; --space-6: 24px;
  --radius-sm: 4px; --radius-md: 6px; --radius-lg: 10px;
  color-scheme: light;
}
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body { margin: 0; background: var(--surface); color: var(--ink); font-family: var(--font-sans); font-size: 15px; line-height: 22px; }
a { color: var(--turf); }
a:hover { color: var(--ink); }
:focus-visible { outline: 2px solid var(--turf); outline-offset: 2px; }
button { font: inherit; }
[hidden] { display: none !important; }
@media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } }

/* Text styles */
.display { margin: 0; font-size: 32px; line-height: 36px; font-weight: 650; letter-spacing: -0.01em; }
.title { margin: 0; font-size: 20px; line-height: 28px; font-weight: 600; }
.label { font-size: 13px; line-height: 18px; font-weight: 500; color: var(--ink-muted); }
.micro { font-size: 11px; line-height: 16px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
.muted { color: var(--ink-muted); }
.data { font-family: var(--font-mono); font-size: 14px; line-height: 20px; font-weight: 500; }
.data-sm { font-family: var(--font-mono); font-size: 12px; line-height: 16px; }
.strong { font-weight: 600; }

/* Stage navigation */
.nav { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap;
  padding: var(--space-2) var(--space-6); background: var(--surface-raised); border-bottom: 1px solid var(--line); }
.nav-brand { font-weight: 650; margin-right: var(--space-4); }
.nav-left { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-1); }
.nav a, .nav span.stage { display: flex; flex-direction: column; padding: 6px 12px; border-radius: var(--radius-md);
  text-decoration: none; color: var(--ink-muted); border: 1px solid transparent; font-size: 13px; line-height: 18px; }
.nav span.stage { opacity: 0.55; }
.nav a[aria-current="page"] { color: var(--ink); background: var(--turf-soft); border-color: var(--turf); }
.nav .stage-gw { font-size: 11px; line-height: 16px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }

/* Layout */
.page { max-width: 1440px; margin: 0 auto; padding: var(--space-6) 32px 48px; display: flex; flex-direction: column; gap: 20px; }
.page-head { display: flex; justify-content: space-between; align-items: flex-end; gap: var(--space-4); flex-wrap: wrap; }
.page-head .meta .chip { white-space: normal; }
.page-head .meta { display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: center; margin-top: 6px; }
.cols { display: grid; grid-template-columns: minmax(0, 1fr) 500px; gap: 20px; align-items: start; }
.stack { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
@media (max-width: 1100px) { .cols { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 640px) { .page { padding: var(--space-4) var(--space-4) 40px; } .nav { padding: var(--space-2) var(--space-4); } }

.card { background: var(--surface-raised); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 20px; min-width: 0; }
.card.call { border-color: var(--turf); }
.card.alert { border-color: var(--danger); }
.card-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-4); flex-wrap: wrap; margin-bottom: 12px; }

/* Chips */
.chip { display: inline-block; white-space: nowrap; padding: 2px 8px; border-radius: var(--radius-sm);
  font-size: 11px; line-height: 16px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
.chip.turf { color: var(--turf); background: var(--turf-soft); }
.chip.amber { color: var(--amber); background: var(--amber-soft); }
.chip.danger { color: var(--danger); background: var(--danger-soft); }
.chip.mute { color: var(--ink-muted); background: var(--surface); border: 1px solid var(--line); }

/* Verdict tiles */
.tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
@media (max-width: 760px) { .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.tile { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius-md); }
.tile-top { display: flex; justify-content: space-between; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.tile-n { font-family: var(--font-mono); font-size: 24px; line-height: 28px; font-weight: 600; }
.tile-note { font-size: 12px; line-height: 16px; color: var(--ink-muted); }

/* Interval bar: band = 90% interval, tick = mean, dark line = no difference */
.range { position: relative; height: 24px; min-width: 120px; }
.range .track { position: absolute; left: 0; right: 0; top: 8px; height: 8px; border-radius: 4px; background: var(--line); }
.range .band { position: absolute; top: 8px; height: 8px; border-radius: 4px; background: var(--turf-soft); box-shadow: inset 0 0 0 1px var(--turf); }
.range .mid { position: absolute; top: 4px; width: 2px; height: 16px; margin-left: -1px; background: var(--turf); }
.range .zero { position: absolute; top: 0; width: 2px; height: 24px; margin-left: -1px; background: var(--ink); }
.range.small { height: 16px; }
.range.small .track, .range.small .band { top: 5px; height: 6px; }
.range.small .zero { height: 16px; }

/* Fixture strip */
.fixtures { display: flex; gap: 2px; flex-wrap: nowrap; }
/* ⚠ 2px of page background BETWEEN fills, not a border: a border adds a third
   colour to every edge and is the first thing to go muddy at this size. */
/* ⚠ A CHEVRON STRIP OVERLAPS, so the advance per cell is (width - point), not
   width. Measured before building: six 45px chevrons occupy 6*45 - 5*9 = 225px
   in the 250px column, which is WIDER than the 38px flat blocks they replace --
   the overlap buys the room back. The previous size was forced by a SILENT clip
   (six 46px blocks needing 286px while the strip still looked complete), so
   this arithmetic is the first thing checked, not the last. */
.fixtures.chev { gap: 0; }
.fx { width: 45px; height: 24px; margin-left: -9px; padding: 0 9px 0 11px;
      display: inline-flex; align-items: center; justify-content: center;
      font-size: 10px; font-weight: 700; letter-spacing: 0; border-radius: 0; }
.fx.first { margin-left: 0; }
.fx .fxo { line-height: 1; }
.fx.b0 { background: var(--fixture-hard); color: var(--on-fill); }
.fx.b1 { background: var(--fixture-tough); color: var(--ink); }
.fx.b2 { background: var(--fixture-fair); color: var(--ink); }
.fx.b3 { background: var(--fixture-good); color: var(--on-fill); }
.legend { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; font-size: 12px; line-height: 16px; color: var(--ink-muted); }

/* Plan rows */
.target { border-top: 1px solid var(--line); padding-top: 14px; margin-top: 14px; }
.target:first-of-type { border-top: 0; margin-top: 0; padding-top: 0; }
.target-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-4); flex-wrap: wrap; margin-bottom: 6px; }
.prow { display: grid; grid-template-columns: 210px 120px minmax(160px, 1fr) 170px 250px 72px; gap: 14px; align-items: center; padding: 10px 0; border-bottom: 1px solid var(--line); }
.prow-head { padding: 6px 0; color: var(--ink-muted); }
/* ⚠ THE PLAYER STAYS PUT, as on Deadline. This row is 982px of columns inside a
   390px phone, so it is read by scrolling -- and without a pinned name you
   scroll to an interval and no longer know whose it is, which makes a
   comparison unreadable exactly while it is being made. */
.prow > :first-child { position: sticky; left: 0; z-index: 2;
                       background: var(--surface-raised);
                       box-shadow: 2px 0 0 var(--line); }
.prow-head > :first-child { background: var(--surface-raised); }
.prow .num { text-align: right; display: flex; flex-direction: column; align-items: flex-end; }
.alts { padding: 4px 0 14px 224px; display: flex; flex-direction: column; gap: 6px; border-bottom: 1px solid var(--line); }
.alt { display: grid; grid-template-columns: 24px 170px minmax(120px, 1fr) 150px; gap: 14px; align-items: center; font-size: 13px; }
.scroll-x { overflow-x: auto; }
.scroll-x > .inner { min-width: 1100px; }
@media (max-width: 900px) { .alts { padding-left: 0; } }

/* Checks */
.check { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 12px; align-items: start; padding: 10px 0; border-top: 1px solid var(--line); }
.check:first-child { border-top: 0; }

/* Squad list */
.srow { display: grid; grid-template-columns: 38px minmax(0, 1fr) 56px 48px 258px; gap: 8px; align-items: center; min-height: 32px; }
/* ⚠ THE NARROW LAYOUT USED TO HIDE THE FIXTURES AND THE OPPONENT. That is the
   phone, which is where this page is actually read, so the columns most worth
   having were the ones dropped. They now scroll instead, and the player stays
   put: without a sticky name you scroll to a number and no longer know whose
   it is, which makes a comparison unreadable exactly when it is being made. */
@media (max-width: 760px) {
  .srow { overflow-x: auto; -webkit-overflow-scrolling: touch;
          grid-template-columns: 38px 150px 56px 48px 258px; }
  .srow > :nth-child(2) { position: sticky; left: 0; z-index: 2;
                          background: var(--surface-raised);
                          box-shadow: 2px 0 0 var(--line); }
}

/* Buttons */
.btn { height: 44px; padding: 0 16px; border-radius: var(--radius-md); border: 1px solid var(--line); background: var(--surface-raised); color: var(--ink); font-size: 15px; font-weight: 500; cursor: pointer; }
.btn.primary { background: var(--turf); border-color: var(--turf); color: var(--on-turf); font-weight: 600; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn.small { height: 32px; padding: 0 10px; font-size: 13px; }

/* Messages */
.banner { display: flex; gap: var(--space-4); align-items: flex-start; }
/* ⚠ STACKS ON A PHONE. Side by side, the chip takes half of a 390px row and
   the sentence beside it wraps to one word per line -- technically not an
   overflow, which is why the overflow gate passed it, and unreadable anyway.
   A layout check that only asks "does it fit" does not ask "can it be read". */
@media (max-width: 560px) {
  .banner { flex-direction: column; gap: var(--space-2); }
  .banner > :first-child { align-self: flex-start; }
}
.note { padding: 8px 10px; border-radius: var(--radius-md); font-size: 13px; line-height: 18px; }
.note.danger { background: var(--danger-soft); }
.note.amber { background: var(--amber-soft); }
.note.turf { background: var(--turf-soft); }
.confirm { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px; border-radius: var(--radius-md); border: 1px solid var(--line); background: var(--surface-raised); cursor: pointer; }
.confirm.need { border-color: var(--danger); }
.confirm input { width: 20px; height: 20px; margin: 0; flex-shrink: 0; accent-color: var(--turf); }
.fail { max-width: 720px; margin: 64px auto; }
.fail pre { background: var(--surface); padding: 10px 12px; border-radius: var(--radius-md); overflow-x: auto; font-size: 13px; }

/* ── faces and badges ──────────────────────────────────────────────────────
   ⚠ A FIXED BOX WITH A NEUTRAL FILL, so a missing photo changes nothing about
   the layout. Photos arrive late and some never arrive; a row that reflows when
   one 404s is worse than one that never had them. */
.face { width: 30px; height: 38px; flex: none; display: inline-flex;
        align-items: flex-end; justify-content: center; overflow: hidden;
        border-radius: 4px; background: var(--line); position: relative; }
.face img { width: 100%; height: 100%; object-fit: cover; object-position: top center; display: block; }
.face-fb { font: 600 11px/1 system-ui, sans-serif; color: var(--ink-muted);
           position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.face.sm { width: 22px; height: 28px; }
.face.sm .face-fb { font-size: 9px; }
.badge { width: 16px; height: 16px; object-fit: contain; vertical-align: -3px; margin-right: 3px; }
.who { display: flex; align-items: center; gap: 7px; min-width: 0; }

/* the crossover sentence spans the whole row rather than sitting in a 120px
   column -- see plan.js.

   ⚠ AND SO DOES THE FLAG NOTE, which is the same kind of thing and was
   missed. Measured 2026-09-29 by check_layout at 390px: 2.7 words per line
   against a floor of 3.0, because without a span it lands in whichever grid
   column comes next and gets a 120px measure. It had been live in that state.
   The two selectors are kept together so the next prose block added to a row
   is added to both. */
.prow .crossover,
.prow [data-flag-unmodelled] { grid-column: 1 / -1; padding-top: 2px; }

/* a value and its unit are one thing; they must not be split by a line break */
.nb { white-space: nowrap; }

/* ── the verdict, abbreviated, riding in the sticky player cell ───────────── */
.vdot { font: 700 9px/1 system-ui, sans-serif; letter-spacing: .04em;
        padding: 3px 4px; border-radius: 3px; flex: none; align-self: flex-start; }
.vdot.turf { background: var(--turf-soft); color: var(--turf); }
.vdot.amber { background: var(--amber-soft); color: var(--amber); }
.vdot.danger { background: var(--danger-soft); color: var(--danger); }
.vdot.mute { background: var(--line); color: var(--ink-muted); }

/* ⚠ THE EXPLANATION IS PROSE AND WANTS A MEASURE. Run to the full width of a
   scrolling row it becomes a single long line nobody reads to the end of. */
/* ⚠ PINNED TO THE VIEWPORT INSIDE A HORIZONTAL SCROLLER. The row grid is
   982px wide, so a full-width cell is 982px and only the first 390 are ever on
   screen -- the sentence was there, and unreadable past its first line, unless
   you thought to scroll a row sideways to read prose. Sticky-left keeps it in
   view; the max-width keeps it to a readable measure. */
.prow .crossover,
.prow [data-flag-unmodelled] { position: sticky; left: 0;
                   /* ⚠ 100vw IS NOT THE AVAILABLE WIDTH. The element sits
                      37px in from the viewport edge (page + card padding) and
                      its scroller ends at 353, leaving 316 -- so a 342px cap
                      measured "correct" and still ran 26px past the edge.
                      Measure the space the element is actually in. */
                   max-width: min(46em, calc(100vw - 80px)); }
.target-head, .target-head > * { position: sticky; left: 0;
                                max-width: calc(100vw - 80px); }

/* the alternative's identity stays put while its interval scrolls */
.altwho { position: sticky; left: 0; z-index: 1;
          background: var(--surface-raised); padding-right: 6px; }

/* ⚠ SMALLER BARS. At the phone width the interval was the widest thing in the
   row, so the number it qualifies read as the secondary item. */
.range.small { max-width: 120px; }

/* ⚠ THE TOGGLE'S TRAILING SENTENCE GETS ITS OWN LINE. In one flex row it ran
   between and around the buttons, so the control and its explanation read as
   one broken sentence. */
.meta[role="group"] { flex-wrap: wrap; align-items: center; }
.meta[role="group"] > .data-sm { flex-basis: 100%; margin-top: 4px; }

/* ── the pitch on Review ──────────────────────────────────────────────────
   ⚠ NO GREEN RECTANGLE. A drawn pitch spends its contrast on decoration and
   leaves the scores, which are the content, fighting it. The rows carry the
   shape; the shirts carry the numbers. */
.pitch { display: flex; flex-direction: column; gap: 8px; margin: 10px 0 12px; }
/* ⚠ NOWRAP, DELIBERATELY. A wrapped line is not a smaller pitch, it is a
   DIFFERENT SHAPE: a five-man midfield that folds onto two rows reads as 4-1,
   and the row is there to show the shape. With nowrap a future regression
   overflows, which the layout gate already fails on, instead of silently
   restacking into a formation nobody played. */
.pitch-row { display: flex; justify-content: center; gap: 6px; flex-wrap: nowrap; }

/* ⚠ THE FIRST VERSION SAID "SIZED FOR FIVE ACROSS A 390px PHONE" AND WAS
   WRONG -- it set 64px and five of them wrapped on the first render. MEASURED
   2026-09-29: the row is 316px inside the card at a 390px viewport, so five
   shirts and four 6px gaps leave (316 - 24) / 5 = 58.4px each. `flex: 1 1 0`
   divides the row instead of asserting a width, so the arithmetic is the
   browser's and cannot go stale; the cap stops a lone goalkeeper stretching
   to the full 316. */
.shirt { flex: 1 1 0; max-width: 72px; min-width: 0;
         display: flex; flex-direction: column;
         align-items: center; gap: 1px; padding: 5px 3px;
         border: 1px solid var(--line); border-radius: 6px;
         background: var(--surface-raised); text-align: center; }
.shirt-top { display: flex; align-items: center; gap: 3px; max-width: 100%; }
/* 10px, not 11: at a 58px shirt the name box is ~48px, and 11px ellipsised
   "Calafiori" after five characters. */
.shirt-name { font-size: 10px; font-weight: 600; overflow: hidden;
              text-overflow: ellipsis; white-space: nowrap; }
.shirt-pts { font: 700 15px/1.1 system-ui, sans-serif; }
.shirt-pred { font-size: 9px; color: var(--ink-muted); }
.shirt-c, .shirt-up { font: 700 9px/1 system-ui, sans-serif; flex: none;
                      padding: 2px 3px; border-radius: 3px; }
.shirt-c { background: var(--turf); color: var(--on-turf); }
.shirt-up { background: var(--turf-soft); color: var(--turf); }
/* ⚠ A BLANK AND A ZERO ARE DIFFERENT THINGS. A player who did not appear
   scored 0 and so did a player who played 90 minutes badly; only one of them
   tells you anything about the pick. */
.shirt.dnp { opacity: .55; border-style: dashed; }
.shirt-dnp { font-size: 9px; color: var(--ink-muted); }
.shirt.up { border-color: var(--turf); }

.bench-strip { display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
               padding-top: 10px; border-top: 1px solid var(--line); }

/* ⚠ `.num` IS SCOPED TO `.prow`, so the two spans here were inline and ran
   together as "+7points". The gain and its unit stack. */
.split-row .num { display: flex; flex-direction: column; align-items: flex-start; }
.split { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.split-row { display: grid; grid-template-columns: 76px minmax(0, 1fr);
             gap: 12px; align-items: start;
             padding-top: 10px; border-top: 1px solid var(--line); }
.split-row:first-child { border-top: 0; padding-top: 0; }

/* the decision record's provenance line spans the row rather than sharing it
   with the "Recorded" chip -- see deadline.js. `anywhere` so a longer
   filename breaks instead of pushing the column. */
.check .dec-source { grid-column: 1 / -1; overflow-wrap: anywhere; }
