/* ==========================================================================
   FPL Helper — Graphite and ultraviolet.

   The app's own identity: near-neutral graphite chrome and surfaces, with one
   electric violet doing every job a highlight does — the wordmark, the
   gameweek badge, the accent, focus. Chosen on a side-by-side of a dozen
   palettes for being the one that pops; it nods to FPL's purple without
   borrowing it.

   What is NOT ours, and stays FPL's on purpose:

     difficulty 1–5   #375523 #01FC7A #E7E7E7 #FF1B54 #80072D, text white or #37003C
     good / bad       green up, red down

   Both carry meaning the reader already knows from the official site, so they
   look identical in every theme and are never recoloured with the brand.

   Two themes, light and dark. Only the semantic layer changes between them;
   no component rule mentions a theme.

   Three layers, and a rule may only read from the one below it:

     1. PALETTE   raw values. Theme-independent.
     2. SEMANTIC  surface / on-surface / outline / accent. Remapped per theme.
     3. ALIASES   the names this app already used (--text, --muted, --line,
                  --ink-800 …), resolved from layer 2. They exist so the whole
                  stylesheet and the two inline <style> blocks in setup.html
                  and setup_progress.html keep working, and so a theme is one
                  edit rather than nine hundred.

   Tints are written rgba(var(--x-rgb), a) rather than as literals, so a colour
   and the washes derived from it cannot drift apart when one is edited.

   Structural grammar: flat surfaces (no gradients), radii from the 4/8/12/16
   scale, and emphasis carried by weight rather than by uppercase.

   Figtree is the face; PremierSans is named first only so anyone who has it
   installed gets it.
   ========================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Figtree:wght@400;500;600;700;800&display=swap');

/* ==========================================================================
   1. PALETTE — raw values. Do not use these directly in a component rule.
   ========================================================================== */
:root {
  /* Brand. The neon violet is for marks on graphite (wordmark, badge) and for
     the dark theme's accent; on white it is too light to read as text, so the
     light theme's accent is the deeper cut. */
  --uv:      #A66BFF;   --uv-rgb: 166,107,255;
  --uv-soft: #B48CFF;   --uv-soft-rgb: 180,140,255;
  --uv-deep: #6B2FD6;   --uv-deep-rgb: 107,47,214;
  --uv-ink:  #5521B3;
  --white:   #FFFFFF;

  /* Graphite ramp, darkest to lightest. Very slightly warm, so it reads as a
     chosen grey rather than a default one. */
  --g0:   #0C0D0E;   --g0-rgb: 12,13,14;
  --g1:   #17191C;   --g1-rgb: 23,25,28;
  --g2:   #1F2226;
  --g3:   #262A2E;
  --g4:   #3A3F38;
  --g5:   #5A5F57;
  --g6:   #737869;
  --g7:   #868B82;
  --g8:   #A3A89C;
  --g9:   #C9CCC4;
  --g10:  #E6E8E3;
  --g11:  #F1F2EF;
  --g12:  #F3F4F2;
  --g13:  #F7F8F6;
  --g14:  #F3F5EE;

  /* Fills for a fully saturated signal: the up-arrow green, the warning
     orange, and the yellow card. */
  --sig-green:  #00FF87;
  --sig-orange: #FF6900;
  --sig-yellow: #EBFF00;

  /* Fixture difficulty — FPL's own scale, and the one thing that must look
     identical in both themes. Background and the text colour on it, both
     checked against FPL's --ism-color-difficulty-* tokens in November 2026:
     the dark text on 2–4 is their purple, not our graphite. */
  --fdr-1: #375523;  --fdr-1-rgb: 55,85,35;    --fdr-1-on: var(--white);
  --fdr-2: #01FC7A;  --fdr-2-rgb: 1,252,122;   --fdr-2-on: #37003C;
  --fdr-3: #E7E7E7;  --fdr-3-rgb: 231,231,231; --fdr-3-on: #37003C;
  --fdr-4: #FF1B54;  --fdr-4-rgb: 255,27,84;   --fdr-4-on: #37003C;
  --fdr-5: #80072D;  --fdr-5-rgb: 128,7,45;    --fdr-5-on: var(--white);

  /* System colours. */
  --sys-error: #E60023;
  --sys-error-container: #FFF2F4;
  --sys-success: #34A853;

  /* Radius scale. */
  --radius-xs: 4px;
  --radius: 8px;
  --radius-m: 12px;
  --radius-lg: 12px;
  --radius-full: 9999px;

  /* One family. Weight carries emphasis, so there is no display face and no
     mono — numbers align on tabular figures instead, set on body. */
  --font-body: 'PremierSans-Regular', 'Figtree', Arial, 'Helvetica Neue', sans-serif;
  --font-display: 'PremierSans-Bold', 'Figtree', Arial, 'Helvetica Neue', sans-serif;
  --font-num: var(--font-body);
  /* Kept so any rule still asking for a mono gets the right family rather than
     dropping to Courier. */
  --font-mono: var(--font-num);
}

/* ==========================================================================
   2. SEMANTIC — THEME, LIGHT
   The default: white cards on a pale warm grey, graphite text, a graphite top
   bar with the violet mark and badge on it.
   ========================================================================== */
:root {
  --surface: var(--white);
  --surface-variant: var(--g11);
  --surface-sunken: var(--g13);
  --surface-raised: var(--g10);
  --page: var(--g12);

  --on-surface: var(--g1);
  --on-surface-strong: var(--g0);
  --on-surface-variant: var(--g5);
  --on-surface-faint: var(--g7);
  --on-surface-rgb: var(--g1-rgb);

  --outline: var(--g9);
  --outline-variant: var(--g10);

  /* The filled selected state: graphite box, white text. Inverts in dark. */
  --primary: var(--g1);
  --on-primary: var(--white);
  --primary-container: var(--g10);
  --on-primary-container: var(--g1);

  /* Chrome. The brand bar is graphite in both themes; the nav row under it is
     the surface colour. --primary-fixed is the pair that does NOT invert,
     which is what a control sitting on the bar needs. */
  --chrome: var(--g1);
  --chrome-2: var(--white);
  --on-chrome: var(--white);
  --on-chrome-variant: rgba(255,255,255,0.72);
  --primary-fixed: var(--g1);
  --on-primary-fixed: var(--white);

  /* The wordmark and the gameweek badge on the bar. Full neon in both themes:
     it only ever sits on graphite. */
  --badge: var(--uv);
  --on-badge: var(--g0);

  /* The accent — the one colour meaning "this is the thing in focus". On white
     the neon is too light for text, so light mode takes the deeper cut. */
  --accent: var(--uv-deep);
  --accent-rgb: var(--uv-deep-rgb);
  --accent-dim: var(--uv-ink);
  --accent-2: var(--accent);
  --accent-2-rgb: var(--accent-rgb);

  --focus: var(--uv-deep);

  /* Signals. Each is given twice: the colour for text on the page, and the
     darker or lighter cut for text sitting on a 15% wash of itself. In light
     the wash is pale, so the text on it goes DOWN; in dark it goes up. */
  --good: #17803D;       --good-rgb: 23,128,61;
  --good-text: #0F5C2B;
  --good-fill: var(--sig-green);
  --bad: #D1003C;        --bad-rgb: 209,0,60;
  --bad-text: #9E002D;
  --bad-fill: var(--fdr-4);
  --warn: #B24A00;       --warn-rgb: 178,74,0;
  --warn-text: #8A3A00;
  --warn-fill: var(--sig-orange);
  /* Text on a fully saturated signal or difficulty colour. */
  --on-bright: var(--g1);

  /* Washes. A little more of the text colour over the surface — the mechanism
     behind hover and pressed states, and the reason row hover needs no
     per-theme rule. */
  --wash-1: rgba(var(--on-surface-rgb), 0.045);
  --wash-2: rgba(var(--on-surface-rgb), 0.085);
  --wash-3: rgba(var(--on-surface-rgb), 0.14);
  /* The washes above are built from the text colour, so they lighten a dark
     page and darken a light one — "a little more of this". For the opposite
     job, a cell that should recede because there is nothing in it, the wash has
     to sink in BOTH themes, which means black in both. */
  --wash-sink: rgba(0,0,0,0.045);
  --wash-sink-2: rgba(0,0,0,0.075);

  /* Depth. Two levels, real shadows in light, tinted graphite. */
  --shadow:    0 1px 2px rgba(var(--g1-rgb), 0.07), 0 2px 8px rgba(var(--g1-rgb), 0.07);
  --shadow-lg: 0 2px 6px rgba(var(--g1-rgb), 0.10), 0 14px 34px rgba(var(--g1-rgb), 0.14);
  --ring: 0 0 0 3px rgba(var(--uv-deep-rgb), 0.22);
  /* The lit top edge that makes a flat panel read as a surface. It is a dark
     theme's trick and nothing else, so in light it is simply absent. */
  --edge: transparent;
  --scrim: rgba(var(--g1-rgb), 0.45);

  color-scheme: light;
}

/* ==========================================================================
   2. SEMANTIC — THEME, DARK
   Graphite all the way down, with the brighter violet as the accent.

   One copy, keyed off an attribute that is always present: the bootstrap
   script in base.html resolves stored choice → system preference → light and
   stamps data-theme on <html> before first paint. That is why there is no
   second copy of this block under a prefers-color-scheme media query, and
   therefore nothing here that can drift out of step with one.
   ========================================================================== */
:root[data-theme="dark"] {
  --surface: var(--g1);
  --surface-variant: var(--g2);
  --surface-sunken: var(--g0);
  --surface-raised: var(--g3);
  --page: var(--g0);

  --on-surface: var(--g14);
  --on-surface-strong: var(--white);
  --on-surface-variant: var(--g8);
  --on-surface-faint: var(--g6);
  --on-surface-rgb: 255,255,255;

  --outline: var(--g4);
  --outline-variant: var(--g3);

  --primary: var(--g14);
  --on-primary: var(--g1);
  --primary-container: var(--g3);
  --on-primary-container: var(--g14);

  --chrome: var(--g0);
  --chrome-2: var(--g1);
  --on-chrome: var(--g14);
  --on-chrome-variant: var(--g8);
  --primary-fixed: var(--g1);
  --on-primary-fixed: var(--white);

  --badge: var(--uv);
  --on-badge: var(--g0);

  --accent: var(--uv-soft);
  --accent-rgb: var(--uv-soft-rgb);
  --accent-dim: var(--uv);
  --accent-2: var(--accent);
  --accent-2-rgb: var(--accent-rgb);

  --focus: var(--uv-soft);

  --good: #3BE49E;       --good-rgb: 59,228,158;
  --good-text: #7FF0C2;
  --good-fill: var(--sig-green);
  --bad: var(--fdr-4);   --bad-rgb: var(--fdr-4-rgb);
  --bad-text: #FF8FA8;
  --bad-fill: var(--fdr-4);
  --warn: #FF8C38;       --warn-rgb: 255,140,56;
  --warn-text: #FFC08A;
  --warn-fill: var(--sig-orange);
  --on-bright: var(--g1);

  --wash-1: rgba(var(--on-surface-rgb), 0.05);
  --wash-2: rgba(var(--on-surface-rgb), 0.09);
  --wash-3: rgba(var(--on-surface-rgb), 0.15);
  --wash-sink: rgba(0,0,0,0.26);
  --wash-sink-2: rgba(0,0,0,0.34);

  --shadow:    0 1px 2px rgba(0,0,0,0.40), 0 4px 14px rgba(0,0,0,0.28);
  --shadow-lg: 0 2px 6px rgba(0,0,0,0.45), 0 16px 38px rgba(0,0,0,0.40);
  --ring: 0 0 0 3px rgba(var(--uv-soft-rgb), 0.22);
  --edge: rgba(255,255,255,0.05);
  --scrim: rgba(0,0,0,0.55);

  color-scheme: dark;
}

/* ==========================================================================
   3. ALIASES — the names the rest of this file, and the two inline <style>
   blocks in the setup templates, already use. Nothing below this point reads
   the palette or names a theme.
   ========================================================================== */
:root {
  --text:   var(--on-surface);
  --text-2: var(--on-surface-variant);
  --muted:  var(--on-surface-variant);
  --faint:  var(--on-surface-faint);
  --faint-rgb: var(--on-surface-rgb);

  --line:      var(--outline);
  --line-soft: var(--outline-variant);

  /* The old slate ramp, pointed at the new surfaces. Darkest-to-lightest in
     the dark theme, and the same order of prominence in light. */
  --ink-900: var(--page);
  --ink-850: var(--surface);
  --ink-850-rgb: var(--on-surface-rgb);
  --ink-800: var(--surface);
  --ink-750: var(--surface-sunken);
  --ink-700: var(--surface-raised);
}

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

body {
  margin: 0;
  /* Flat. FPL's pages are a single ground colour, and the ambient glow the
     previous theme used is the one thing that would still read as "not FPL"
     after everything else had been changed. */
  background: var(--page);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  /* With no mono face, this is what keeps a column of numbers in line. */
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

/* Weight, not tracking. FPL sets headings in the bold face at close to normal
   spacing and never in capitals. */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--on-surface-strong);
  margin: 0;
}
h1 { font-size: 1.72rem; letter-spacing: -0.02em; }
h2 { font-size: 1.15rem; }
h3 { font-size: 0.95rem; }

a { color: var(--text); text-decoration: none; transition: color 0.15s ease; }
a:hover { color: var(--accent-dim); }
:root[data-theme="dark"] a:hover { color: var(--accent); }

::selection { background: rgba(var(--accent-rgb), 0.26); color: var(--text); }

* { scrollbar-width: thin; scrollbar-color: var(--outline) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--outline);
  border: 2px solid transparent;
  background-clip: padding-box;
  border-radius: var(--radius);
}
::-webkit-scrollbar-thumb:hover { background: var(--on-surface-variant); background-clip: padding-box; }

/* The violet focus ring, the deeper cut in light and the brighter in dark. */
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

/* --- Layout ------------------------------------------------------------- */
.wrap { max-width: 1500px; margin: 0 auto; padding: 0 20px; }

/* --- Top bar ------------------------------------------------------------
   Two rows: the graphite brand bar, then the page navigation on the surface
   colour beneath it. The split is what lets the brand bar stay graphite in
   both themes while the nav row follows the theme. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--chrome-2);
  border-bottom: 1px solid var(--outline-variant);
  box-shadow: var(--shadow);
}
.topbar-brandrow { background: var(--chrome); color: var(--on-chrome); }
.topbar-navrow { background: var(--chrome-2); }

.topbar-inner {
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
  padding: 10px 20px;
  max-width: 1500px;
  margin: 0 auto;
}
.topbar-navrow .topbar-inner { padding: 0 20px; gap: 0; }

/* The wordmark. Plain text on the graphite bar, with the violet mark beside
   it. */
.brand {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
  color: var(--on-chrome);
  display: flex;
  align-items: center;
  gap: 9px;
  white-space: nowrap;
}
/* The neon violet at full strength only ever sits on the graphite bar. */
.brand .brand-mark { color: var(--badge); font-size: 0.9em; }
.brand .brand-gw {
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0;
  color: var(--on-badge);
  background: var(--badge);
  border-radius: var(--radius-full);
  padding: 2px 9px;
}

/* Page navigation. A bar under the current item, which is how FPL marks the
   active tab — not a filled chip. */
nav.pages { display: flex; gap: 2px; flex-wrap: wrap; }
nav.pages a {
  padding: 12px 14px;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--on-surface-variant);
  box-shadow: inset 0 -3px 0 transparent;
  transition: color 0.16s ease, background 0.16s ease, box-shadow 0.16s ease;
}
nav.pages a:hover { background: var(--wash-1); color: var(--text); }
nav.pages a.active {
  color: var(--on-surface-strong);
  box-shadow: inset 0 -3px 0 var(--primary);
}
:root[data-theme="dark"] nav.pages a.active { box-shadow: inset 0 -3px 0 var(--accent); }
nav.pages .nav-lock { width: 0.8em; height: 0.8em; margin-right: 5px; vertical-align: -0.05em; opacity: 0.7; }

.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }

/* A control sitting on the graphite bar: on-primary-fixed (white) behind
   primary-fixed (graphite), with the hover a color-mix back towards the
   graphite. */
.btn-chrome {
  background: var(--on-primary-fixed);
  color: var(--primary-fixed);
  border: 1px solid var(--on-primary-fixed);
  border-radius: var(--radius);
  font-weight: 700;
  padding: 6px 13px;
}
.btn-chrome:hover:not([disabled]) {
  background: color-mix(in srgb, var(--on-primary-fixed) 85%, var(--primary-fixed) 15%);
  color: var(--primary-fixed);
  border-color: var(--on-primary-fixed);
  box-shadow: none;
}
.btn-chrome[disabled] { opacity: 0.55; }
.btn-chrome[disabled]:hover { background: var(--on-primary-fixed); color: var(--primary-fixed); }

/* The theme switch. Circular, quiet, and on the chrome rather than in the page
   — it is a property of the whole app, not of what you are looking at. */
.theme-toggle {
  width: 30px;
  height: 30px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.95rem;
  line-height: 1;
  border-radius: var(--radius-full);
  background: rgba(255,255,255,0.12);
  border: 1px solid rgba(255,255,255,0.28);
  color: var(--on-chrome);
}
.theme-toggle:hover {
  background: rgba(255,255,255,0.22);
  border-color: rgba(255,255,255,0.5);
  color: var(--on-chrome);
  box-shadow: none;
}

/* A plain text link on the graphite bar, quieter than the Refresh button
   beside it: it is a way out of the app, not something the app does. */
.chrome-link {
  color: var(--on-chrome);
  opacity: 0.8;
  font-weight: 600;
  font-size: 0.88rem;
  text-decoration: none;
}
.chrome-link:hover, .chrome-link.active {
  opacity: 1;
  color: var(--on-chrome);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* --- Controls -----------------------------------------------------------
   FPL's fields: 8px radius, the surface colour, a p30 outline, and generous
   padding. No uppercase anywhere. */
select, input[type="search"], input[type="text"], input[type="number"], button {
  font-family: var(--font-body);
  font-size: 0.85rem;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--outline);
  border-radius: var(--radius);
  padding: 6px 10px;
  transition: border-color 0.15s ease, background 0.15s ease,
              color 0.15s ease, box-shadow 0.15s ease;
}
select:hover, input:hover { border-color: var(--on-surface-variant); }
/* The field itself picks up the accent on focus. The global :focus-visible
   outline still draws on top for keyboard users — this is the resting state of
   a field you are typing in, not a replacement for that. */
select:focus, input:focus { border-color: var(--focus); background: var(--surface); }
button { cursor: pointer; font-weight: 600; }
button:hover { border-color: var(--accent-dim); color: var(--accent-dim); box-shadow: var(--ring); }
:root[data-theme="dark"] button:hover { border-color: var(--accent); color: var(--accent); }
button[disabled] { opacity: 0.5; cursor: not-allowed; }
button[disabled]:hover { border-color: var(--outline); color: var(--text); box-shadow: none; }

/* The primary action. The primary/on-primary pair: a graphite box with white
   text in light, a light box with graphite text in dark. Flat, no gradient. */
.btn-accent {
  background: var(--primary);
  color: var(--on-primary);
  border-color: var(--primary);
  font-weight: 700;
  box-shadow: none;
}
.btn-accent:hover {
  background: color-mix(in srgb, var(--primary) 86%, var(--on-primary) 14%);
  color: var(--on-primary);
  border-color: var(--primary);
  box-shadow: var(--ring);
}

/* A field's label. Bold and small, in the variant text colour — FPL's way of
   demoting a label without shouting it in capitals. */
label.field {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--on-surface-variant);
}

/* --- Page header -------------------------------------------------------- */
.page-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  padding: 24px 0 16px;
}
.page-head .eyebrow {
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--on-surface-variant);
  margin-bottom: 4px;
}

/* --- Panels ------------------------------------------------------------- */
.panel {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-m);
  margin-bottom: 18px;
  box-shadow: var(--shadow);
  /* Clips whatever sits in the corners — a bench strip, a hovered last row —
     to the panel's own radius. Safe for the sticky table headers: their scroll
     container is .table-scroll inside this, not the panel itself. */
  overflow: hidden;
}
/* A one-pixel highlight along the top edge. In light --edge is transparent, so
   this draws nothing: a white card on grey needs no help reading as lit. */
.panel::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--edge) 15%, var(--edge) 85%, transparent);
  pointer-events: none;
}
.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 13px 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--outline-variant);
}
.panel-head h2, .panel-head h3 { margin: 0; }
.panel-body { padding: 16px; }
.panel-body.flush { padding: 0; }

.grid { display: grid; gap: 18px; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }

/* --- Stat tiles --------------------------------------------------------- */
.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 1px;
  background: var(--outline-variant);
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-m);
  overflow: hidden;
}
.stat {
  background: var(--surface);
  padding: 14px 16px;
}
.stat .k {
  font-size: 0.74rem;
  font-weight: 600;
  color: var(--on-surface-variant);
  display: block;
  margin-bottom: 3px;
}
.stat .v {
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.1;
  color: var(--on-surface-strong);
}
.stat .sub { font-size: 0.76rem; font-weight: 400; color: var(--on-surface-faint); }

/* --- Tables ------------------------------------------------------------- */
.table-scroll { overflow-x: auto; }
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
}
/* FPL's table head: the recessed surface, bold sentence-case labels in the
   full text colour. Not capitals, not tracked out, not a gradient. */
thead th {
  position: sticky;
  top: 0;
  background: var(--surface-variant);
  font-size: 0.76rem;
  font-weight: 700;
  color: var(--on-surface-strong);
  text-align: left;
  padding: 10px 9px;
  border-bottom: 1px solid var(--outline);
  white-space: nowrap;
  z-index: 2;
}
th.sortable { cursor: pointer; user-select: none; }
th.sortable:hover { color: var(--accent-dim); }
:root[data-theme="dark"] th.sortable:hover { color: var(--accent); }
th.sortable::after {
  content: '';
  display: inline-block;
  width: 0.45em;
  color: var(--on-surface-faint);
}
th.sort-asc::after  { content: '▲'; color: var(--accent-dim); font-size: 0.6em; }
th.sort-desc::after { content: '▼'; color: var(--accent-dim); font-size: 0.6em; }
:root[data-theme="dark"] th.sort-asc::after,
:root[data-theme="dark"] th.sort-desc::after { color: var(--accent); }

tbody td {
  padding: 8px 9px;
  border-bottom: 1px solid var(--outline-variant);
  white-space: nowrap;
}
/* Hover is a wash of the text colour over the surface — FPL's own mechanism,
   and the reason this needs no second rule for the dark theme. */
tbody tr { transition: background 0.12s ease; }
tbody tr:hover { background: var(--wash-1); }
tbody tr:last-child td { border-bottom: 0; }

td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
td.name { font-weight: 600; white-space: nowrap; }
.pos {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--on-surface-variant);
  border: 1px solid var(--outline);
  border-radius: var(--radius-xs);
  padding: 0 5px;
}
.team-tag {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--on-surface-variant);
}

.good { color: var(--good); }
.bad  { color: var(--bad); }
.hit  { color: var(--good); font-weight: 700; }

/* Player-explorer bands. A tint rather than coloured text alone: the point is
   to read a whole column at a glance, and text colour on its own is too thin at
   this size to pick out a block of good numbers from a block of poor ones. */
.b-good { color: var(--good-text); background: rgba(var(--good-rgb),0.14); }
.b-avg  { color: var(--warn-text); background: rgba(var(--warn-rgb),0.12); }
.b-bad  { color: var(--bad-text);  background: rgba(var(--bad-rgb),0.12); }
/* Not enough football behind the number to grade it. Deliberately the same
   neutral as a difficulty-3 fixture, so it reads as "no verdict" rather than as
   a quiet fourth opinion on the player. */
.b-thin { color: var(--muted); background: var(--wash-sink); }
tbody tr:hover .b-thin { background: var(--wash-sink-2); }

tbody tr:hover .b-good { background: rgba(var(--good-rgb),0.22); }
tbody tr:hover .b-avg  { background: rgba(var(--warn-rgb),0.20); }
tbody tr:hover .b-bad  { background: rgba(var(--bad-rgb),0.20); }

/* Legend chips in the notice above the table. */
.band-key { display: inline-flex; gap: 6px; margin-right: 8px; vertical-align: middle; }
.band-key .chip {
  padding: 1px 8px;
  border-radius: var(--radius-xs);
  font-size: 11px;
  font-weight: 700;
}
.miss { color: var(--on-surface-faint); }


/* --- The signature: fixture ticker --------------------------------------
   Six cells, one per upcoming gameweek, coloured by FPL difficulty. These are
   now FPL's exact five colours with FPL's exact five text colours, so a run
   read here and a run read on the official site are the same picture. */
.ticker { display: inline-flex; gap: 2px; vertical-align: middle; }
.tick {
  font-size: 0.66rem;
  font-weight: 700;
  min-width: 31px;
  padding: 3px 3px;
  text-align: center;
  border-radius: var(--radius-xs);
  position: relative;
}
/* Venue is stated with an icon rather than implied by italics, and drawn from
   the class so the glyph is not repeated through four thousand cells of markup.
   Kept small and slightly dimmed so it marks the cell without competing with
   the opponent code, which is what you are actually reading. */
.tick::before {
  font-size: 0.82em;
  margin-right: 1px;
  opacity: 0.85;
}
.tick.home::before { content: '🏠'; }
.tick.away::before { content: '✈️'; }
.fdr-1 { background: var(--fdr-1); color: var(--fdr-1-on); }
.fdr-2 { background: var(--fdr-2); color: var(--fdr-2-on); }
.fdr-3 { background: var(--fdr-3); color: var(--fdr-3-on); }
.fdr-4 { background: var(--fdr-4); color: var(--fdr-4-on); }
.fdr-5 { background: var(--fdr-5); color: var(--fdr-5-on); }

/* Ten cells instead of six, so each has to give up some width. Small enough
   that a full run fits beside the numbers, still wide enough for a three-letter
   club code. */
.ticker-long .tick { min-width: 26px; font-size: 0.6rem; padding: 2px; }
.ticker-long .tick::before { font-size: 0.72em; margin-right: 0; }
/* A gameweek the club does not play in. Deliberately hollow rather than a sixth
   colour: a blank is the absence of a fixture, not a difficulty. */
.tick.blank {
  background: transparent;
  border: 1px dashed var(--outline);
  color: var(--on-surface-faint);
}
/* Two matches in one gameweek. The runs stop lining up at this point, which is
   the whole reason to mark it. */
.tick.dgw { box-shadow: inset 0 -2px 0 var(--accent); }

.ticker-legend {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.74rem;
  font-weight: 600;
  color: var(--on-surface-variant);
}
.ticker-legend .swatch {
  width: 16px; height: 10px; border-radius: 2px; display: inline-block;
}

/* --- Pitch --------------------------------------------------------------
   Flat, like everything else. FPL's own pick-team pitch is a flat green field;
   here the surface stays the card colour and the shirts carry the colour, which
   is what keeps a fifteen-card grid readable in both themes. */
.pitch {
  padding: 16px;
  background: var(--surface-variant);
}

/* One band per line. The label and the rule are what separate a back four from
   a midfield three — without them the pitch is just three rows of cards and you
   have to read the names to work out the shape. */
.pitch-line {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 0;
  border-bottom: 1px solid var(--outline-variant);
}
.pitch-line:last-child { border-bottom: 0; }
.line-label {
  flex: 0 0 56px;
  display: flex;
  align-items: baseline;
  gap: 5px;
  font-size: 0.74rem;
  font-weight: 700;
  color: var(--on-surface-variant);
}
.line-count {
  font-size: 0.9em;
  font-weight: 400;
  color: var(--on-surface-faint);
}

.pitch-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
  flex: 1;
}

.shirt {
  position: relative;
  width: 118px;
  padding: 9px 8px 8px;
  background: var(--surface);
  border: 1px solid var(--outline-variant);
  border-left: 3px solid var(--fdr-3);
  border-radius: var(--radius);
  text-align: center;
  box-shadow: var(--shadow);
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
/* The pitch is the one place on the site that is looked at rather than read,
   so it is the one place a card lifts under the pointer. */
.shirt:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
.shirt.fdr-edge-1 { border-left-color: var(--fdr-1); }
.shirt.fdr-edge-2 { border-left-color: var(--fdr-2); }
.shirt.fdr-edge-3 { border-left-color: var(--fdr-3); }
.shirt.fdr-edge-4 { border-left-color: var(--fdr-4); }
.shirt.fdr-edge-5 { border-left-color: var(--fdr-5); }

/* The real FPL shirt, cached locally by the refresh job. */
.shirt .kit {
  display: block;
  margin: 0 auto 6px;
  width: 44px;
  height: auto;
  image-rendering: -webkit-optimize-contrast;
}
.shirt.bench .kit { width: 34px; }
/* Shown until the refresh has fetched the kits, so the card keeps its shape. */
.kit-missing {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin: 0 auto 6px;
  border: 1px dashed var(--outline);
  border-radius: var(--radius-xs);
  font-size: 0.68rem;
  font-weight: 600;
  color: var(--on-surface-faint);
}
.shirt.bench .kit-missing { width: 34px; height: 34px; }

.shirt .sname {
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--on-surface-strong);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
}
.shirt .smeta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 6px;
  margin-top: 3px;
  font-size: 0.7rem;
  color: var(--on-surface-variant);
}
.shirt .opp-mark { color: var(--text); font-weight: 700; }

/* Where they are playing, on the starting XI cards. Deliberately the same two
   glyphs the fixture ticker uses: a venue should look the same everywhere it is
   stated, and these are already the ones the reader has learned. */
.shirt .venue {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  color: var(--text);
  font-weight: 700;
}
.shirt .venue::before { font-size: 0.92em; opacity: 0.85; }
.shirt .venue.home::before { content: '🏠'; }
.shirt .venue.away::before { content: '✈️'; }
/* A blank gameweek: no fixture, so no venue to state. */
.shirt .venue.none { color: var(--on-surface-faint); font-weight: 400; }

/* What the player actually scored, top-right, opposite the armband. */
.gw-pts {
  position: absolute;
  top: 6px;
  right: 6px;
  min-width: 20px;
  padding: 1px 5px;
  border-radius: var(--radius-xs);
  background: var(--surface-variant);
  border: 1px solid var(--outline-variant);
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--on-surface-variant);
}
/* A gameweek being played right now. The global reduced-motion rule already
   stills the pulse, leaving a plain dot. */
.live { color: var(--warn); display: inline-flex; align-items: center; gap: 6px; }
.live-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--warn-fill);
  box-shadow: 0 0 0 0 rgba(var(--warn-rgb), 0.6);
  animation: live-pulse 2s ease-out infinite;
  flex: 0 0 7px;
}
@keyframes live-pulse {
  70%  { box-shadow: 0 0 0 6px rgba(var(--warn-rgb), 0); }
  100% { box-shadow: 0 0 0 0 rgba(var(--warn-rgb), 0); }
}
.stat .v.live { font-size: 1.05rem; }
.stat .v.miss { color: var(--on-surface-faint); }

.gw-pts.hot  { background: rgba(var(--good-rgb),0.16); border-color: rgba(var(--good-rgb),0.45); color: var(--good-text); }
.gw-pts.cold { background: rgba(var(--bad-rgb),0.13);  border-color: rgba(var(--bad-rgb),0.40);  color: var(--bad-text); }

/* The armband. The filled selected state, so it uses the primary/on-primary
   pair: a graphite disc with a white letter in light, the reverse in dark. */
.armband {
  position: absolute;
  top: 6px;
  left: 6px;
  width: 18px;
  height: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--primary);
  color: var(--on-primary);
  font-size: 0.64rem;
  font-weight: 800;
  box-shadow: 0 1px 4px rgba(var(--g1-rgb), 0.3);
}
.armband.vice {
  background: transparent;
  color: var(--primary);
  border: 1.5px solid var(--primary);
  box-shadow: none;
}

.bench-strip {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 13px 16px;
  border-top: 1px solid var(--outline);
  background: var(--surface-sunken);
}
.bench-strip .bench-label {
  display: flex;
  align-items: baseline;
  gap: 5px;
  flex: 0 0 56px;
  font-size: 0.74rem;
  font-weight: 700;
  color: var(--on-surface-variant);
}
.shirt.bench { width: 104px; padding: 8px 6px 7px; }
.bench-order {
  font-size: 0.64rem;
  font-weight: 600;
  color: var(--on-surface-faint);
  border: 1px solid var(--outline-variant);
  border-radius: 3px;
  padding: 0 3px;
  margin-right: 4px;
}

.badge {
  display: inline-block;
  font-size: 0.68rem;
  font-weight: 700;
  padding: 1px 6px;
  border-radius: var(--radius-xs);
  border: 1px solid var(--outline);
  color: var(--on-surface-variant);
}
.badge.ok      { color: var(--good); border-color: rgba(var(--good-rgb),0.45); }
.badge.warn    { color: var(--warn); border-color: rgba(var(--warn-rgb),0.45); }
.badge.danger  { color: var(--bad);  border-color: rgba(var(--bad-rgb),0.45); }
.badge.accent  { color: var(--accent-dim); border-color: rgba(var(--accent-rgb),0.5); }
:root[data-theme="dark"] .badge.accent { color: var(--accent); }

/* Availability in one glyph, for the explorer. Colour carries severity, the
   glyph the reason, and the tooltip the news text and chance of playing. The
   amber is the Premier League's own brand orange — FPL marks a doubt in amber
   and an injury in red, and these are those two colours. */
.stat-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 17px;
  height: 17px;
  border-radius: 50%;
  font-size: 0.68rem;
  font-weight: 700;
  line-height: 1;
  vertical-align: middle;
  cursor: help;
}
.stat-icon + .stat-icon { margin-left: 3px; }
.stat-icon.d { background: rgba(var(--warn-rgb),0.16); color: var(--warn); border: 1px solid rgba(var(--warn-rgb),0.45); }
.stat-icon.i,
.stat-icon.s,
.stat-icon.u,
.stat-icon.n { background: rgba(var(--bad-rgb),0.14); color: var(--bad); border: 1px solid rgba(var(--bad-rgb),0.45); }
.stat-icon.fit { background: rgba(var(--good-rgb),0.13); color: var(--good); border: 1px solid rgba(var(--good-rgb),0.40); }
.stat-icon.crowd {
  background: transparent;
  color: var(--accent-dim);
  border: 1px solid rgba(var(--accent-rgb),0.5);
  font-size: 0.57rem;
}
:root[data-theme="dark"] .stat-icon.crowd { color: var(--accent); }
.status-cell { text-align: center; }

.flag-dot {
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: 50%;
  margin-right: 4px;
  vertical-align: middle;
}
.flag-dot.d { background: var(--warn-fill); }
.flag-dot.i, .flag-dot.s, .flag-dot.u, .flag-dot.n { background: var(--bad-fill); }

/* Small print under a table cell or panel. */
.plan-note { font-size: 0.78rem; color: var(--on-surface-variant); }

/* --- Team selector ------------------------------------------------------
   On every page, directly under its heading. A segmented control rather than a
   dropdown: with a handful of entries the current team should be legible
   without opening anything. The pills are FPL-shaped — fully rounded, and the
   selected one is a filled primary rather than a tinted outline. */
.team-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin: 0 0 18px;
  padding: 10px 14px;
  background: var(--surface);
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-m);
  box-shadow: var(--shadow);
}
.team-bar-key {
  font-size: 0.76rem;
  font-weight: 700;
  color: var(--on-surface-variant);
}
.team-pills { display: inline-flex; gap: 5px; flex-wrap: wrap; }
.team-pill {
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  padding: 6px 14px;
  background: transparent;
  border: 1px solid var(--outline);
  border-radius: var(--radius-full);
  color: var(--on-surface-variant);
  font-weight: 600;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.team-pill:hover { background: var(--wash-1); color: var(--text); border-color: var(--on-surface-variant); box-shadow: none; }
.team-pill .tp-name { font-weight: 700; }
.team-pill .tp-id {
  font-size: 0.7rem;
  font-weight: 400;
  color: var(--on-surface-faint);
}
/* The selected team is a filled primary — the same device as the armband and
   the active nav bar. One language for "this is the thing in focus". */
.team-pill.on {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-primary);
  box-shadow: none;
}
.team-pill.on:hover { background: var(--primary); color: var(--on-primary); border-color: var(--primary); }
.team-pill.on .tp-id { color: var(--on-primary); opacity: 0.7; }
.team-bar-note { font-size: 0.76rem; color: var(--on-surface-faint); margin-left: auto; }

/* --- The player panel ----------------------------------------------------
   Clicking a name opens a panel from the right: the club's recent results
   against each of the next six opponents, then the player's own record in
   those games. The name is the control, so there is one fewer thing in a
   crowded first column and a target the width of a word. */
.caret-key {
  color: var(--on-surface-variant);
  background: var(--surface-variant);
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-xs);
  padding: 0 5px;
  font-size: 0.74rem;
  font-weight: 600;
}

.drawer {
  position: fixed;
  top: 0;
  right: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  width: min(680px, 94vw);
  height: 100vh;
  background: var(--surface);
  border-left: 1px solid var(--outline);
  box-shadow: var(--shadow-lg);
  transform: translateX(100%);
  transition: transform 0.2s ease;
}
.drawer.open { transform: translateX(0); }
.drawer[hidden] { display: none; }
.drawer:focus { outline: none; }
/* The panel's own head takes the graphite chrome, which tells you at a
   glance that this is a layer over the page rather than part of it. */
.drawer-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  flex: 0 0 auto;
  padding: 15px 18px 13px;
  border-bottom: 1px solid var(--outline-variant);
  background: var(--chrome);
  color: var(--on-chrome);
}
.drawer-heading { min-width: 0; }
.drawer-heading h3 { margin: 2px 0 0; font-size: 1.1rem; color: var(--on-chrome); }
.drawer-head .opp-gw, .drawer-head .detail-note { color: var(--on-chrome-variant); }
.drawer-close {
  flex: 0 0 auto;
  width: 29px;
  height: 29px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(255,255,255,0.12);
  border: 1px solid rgba(255,255,255,0.28);
  border-radius: var(--radius-full);
  color: var(--on-chrome);
  font-size: 1.1rem;
  line-height: 1;
}
.drawer-close:hover {
  color: var(--on-chrome);
  border-color: rgba(255,255,255,0.5);
  background: rgba(255,255,255,0.22);
  box-shadow: none;
}
/* The panel scrolls, not the page behind it: the season log alone can run to
   forty rows, and a panel that scrolls the document would drag the table out
   from under the row you opened. */
.drawer-body { flex: 1 1 auto; overflow: auto; overscroll-behavior: contain; }
/* One column. The grid exists to put the two opponent blocks side by side in
   the full width of a table; down here there is only ever one column of room,
   and its top border belongs to the head above instead. */
.drawer-body .detail { grid-template-columns: 1fr; border-top: 0; }

/* Only where the panel covers the page rather than sitting beside it — see
   the breakpoint below, which is where it goes full width. */
.drawer-scrim {
  position: fixed;
  inset: 0;
  z-index: 59;
  display: none;
  background: var(--scrim);
  opacity: 0;
  transition: opacity 0.2s ease;
}
.drawer-scrim.open { opacity: 1; }
.drawer-scrim[hidden] { display: none; }
.detail {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(430px, 1fr));
  gap: 1px;
  background: var(--outline-variant);
  border-top: 1px solid var(--outline-variant);
}
.detail.is-loading {
  display: block;
  padding: 16px 18px;
  color: var(--on-surface-variant);
  font-size: 0.85rem;
  background: var(--surface);
}
.detail-block { background: var(--surface); padding: 14px 16px 16px; }
.detail-title {
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--on-surface-strong);
  margin: 0 0 12px;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
}
.detail-note { color: var(--on-surface-faint); font-weight: 400; }

.opp-gw {
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--on-surface-faint);
}
/* The fixture chips in the panel can afford to be bigger than the ones in the
   table: the panel has a row's worth of width to play with, where the Next 6
   column is squeezed between nineteen others. Scoped so bumping one does not
   resize the other. */
.detail .tick {
  font-size: 0.76rem;
  min-width: 37px;
  padding: 4px 5px;
}

/* An opponent with nothing to report: never met, or one of the two clubs is
   new to the league. */
.meet.none {
  font-size: 0.86rem;
  color: var(--on-surface-faint);
  font-style: italic;
  padding-left: 0;
}

/* --- The two opponent blocks ---------------------------------------------
   Both are `.gwlog` tables, so they inherit the season log's cell padding,
   rules, hover and number alignment and the three value states (.on / .dim /
   .off) without restating any of it. Only what is particular to them is here. */
.rec-table { width: 100%; }
/* The opponent, held down the left across its whole run of meetings. Top
   aligned so it sits against the first row of the group rather than floating
   in the middle of five. */
.rec-table td.rec-opp {
  vertical-align: top;
  white-space: nowrap;
  border-right: 1px solid var(--outline-variant);
  background: var(--surface-variant);
  font-weight: 700;
}
/* The rule between opponents is the group's, not the row's: heavier than the
   line between two meetings of the same club, so the groups read as groups. */
.rec-table tbody tr.rec-first > td { border-top: 1px solid var(--outline); }
.rec-table tbody tr:first-child > td { border-top: 0; }
.rec-table .rec-when { color: var(--on-surface-faint); white-space: nowrap; }
.rec-table .rec-venue { color: var(--on-surface-variant); }
/* W/D/L in the same three colours the fixture bars use, without the chip: a
   single letter in a column of single letters needs no box around it. */
.rec-table .res { font-weight: 800; }
.rec-table .res.win  { color: var(--good); }
.rec-table .res.draw { color: var(--on-surface-variant); }
.rec-table .res.loss { color: var(--bad); }
/* What the match was worth. Three bands, not a gradient: a haul, a useful
   return, and a blank. */
.rec-table .pts { color: var(--on-surface-variant); font-weight: 700; }
.rec-table .pts.ok {
  color: var(--good-text);
  background: rgba(var(--good-rgb), 0.14);
  border-radius: var(--radius-xs);
  padding: 1px 6px;
}
.rec-table .pts.big {
  color: var(--on-bright);
  background: var(--good-fill);
  border-radius: var(--radius-xs);
  padding: 1px 6px;
}

/* --- This season, game by game -------------------------------------------
   An ordinary grid item, deliberately. The panel's `auto-fit` track list
   collapses the tracks nothing lands in, so a block spanning `1 / -1` would
   hold every track open and strand the two opponent blocks in half the width.
   `min-width: 0` is what lets the scroller below keep to its track — a grid
   item's automatic minimum is its content, which a wide log would push past. */
.season-block { min-width: 0; }
/* Shrink-wrapped rather than stretched: ten narrow columns spread across a
   twenty-column table would put half a screen of nothing between Pts and the
   edge. */
.gwlog-wrap {
  display: inline-block;
  max-width: 100%;
  max-height: 400px;
  overflow: auto;
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius);
  background: var(--surface);
}
table.gwlog { width: auto; font-size: 0.86rem; }
.gwlog thead th {
  background: var(--surface-variant);
  padding: 8px 11px;
  font-size: 0.76rem;
  border-bottom: 1px solid var(--outline);
  /* The opponent chips are positioned, and a positioned element later in the
     document paints over a sticky one that never claimed a layer. Without this
     a scrolled row slides over its own header rather than under it. */
  z-index: 2;
}
.gwlog tbody td {
  padding: 7px 11px;
  border-bottom: 1px solid var(--outline-variant);
  color: var(--text);
}
.gwlog tbody tr:last-child td { border-bottom: 0; }
.gwlog tbody tr:hover { background: var(--wash-1); }
.gwlog td.num, .gwlog th.num { text-align: right; font-variant-numeric: tabular-nums; }
.gwlog .gw-cell { padding-right: 4px; }
.gwlog .gw-marks { padding-left: 6px; }

/* Three states: something returned, nothing to report, and a cost. */
.gwlog .on  { color: var(--good); font-weight: 700; }
.gwlog .dim { color: var(--on-surface-faint); }
.gwlog .off { color: var(--bad); }
.gwlog .tot { color: var(--on-surface-strong); font-weight: 700; }
/* A game watched from the bench. Dimmed whole rather than dropped: a run of
   them is the clearest thing on the page about whether a player is nailed on. */
.gwlog tr.dnp td { color: var(--on-surface-faint); background: var(--wash-sink); }
/* A gameweek whose summary has not been fetched. Deliberately NOT dimmed like
   a blank: the goals and assists on this row are real and complete, and only
   the columns that carry a `?` are waiting. The hatch marks the row as pending
   without arguing that nothing happened in it. */
.gwlog tr.partial td {
  background: repeating-linear-gradient(
    -45deg,
    transparent 0 5px,
    rgba(var(--on-surface-rgb), 0.06) 5px 10px
  );
}

.gwlog tfoot td {
  position: sticky;
  bottom: 0;
  z-index: 2;
  background: var(--surface-variant);
  border-top: 1px solid var(--outline);
  padding: 8px 11px;
  font-weight: 700;
  color: var(--on-surface-strong);
}
.gwlog .gw-total-label {
  font-weight: 400;
  font-size: 0.84rem;
  color: var(--on-surface-faint);
  text-align: right;
}

/* Cards and misses. Only drawn when they happened, so an empty cell means a
   clean afternoon rather than an unknown one. The yellow is the Premier
   League's own yellow, which for a yellow card is simply correct. */
.mk {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 800;
  line-height: 1.5;
  padding: 1px 5px;
  border-radius: 3px;
  margin-right: 3px;
}
.mk.yellow { background: var(--sig-yellow); color: var(--on-bright); }
.mk.red    { background: var(--fdr-5); color: var(--fdr-5-on); }
.mk.good   { background: rgba(var(--good-rgb), 0.2); color: var(--good-text); }

.season-empty {
  margin: 0;
  color: var(--on-surface-variant);
  font-size: 0.88rem;
  line-height: 1.45;
  max-width: 60ch;
}

/* --- Sub-tabs ------------------------------------------------------------
   Alternative answers to one question, so they sit inside the panel rather
   than up with the page navigation. Marked by a bar under the current tab,
   like the page nav. */
.tabs {
  display: flex;
  gap: 2px;
  padding: 0 14px;
  border-bottom: 1px solid var(--outline);
  flex-wrap: wrap;
}
.tab {
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  padding: 12px 14px;
  background: transparent;
  border: 0;
  border-radius: 0;
  color: var(--on-surface-variant);
  font-size: 0.88rem;
  font-weight: 600;
  box-shadow: inset 0 -3px 0 transparent;
}
.tab:hover { color: var(--text); background: var(--wash-1); box-shadow: inset 0 -3px 0 transparent; }
.tab.on {
  color: var(--on-surface-strong);
  background: transparent;
  box-shadow: inset 0 -3px 0 var(--primary);
}
.tab .tab-note {
  font-size: 0.74rem;
  font-weight: 400;
  color: var(--on-surface-faint);
}
.tab.on .tab-note { color: var(--on-surface-variant); }
.tab-panel[hidden] { display: none; }
/* A set that switches the whole page rather than one panel: no panel to sit
   in, so it sits on the page and keeps clear of the first panel below. */
.tabs.page-tabs { padding: 0; margin-bottom: 16px; }

/* --- Notices ------------------------------------------------------------ */
.notice {
  border: 1px solid var(--outline-variant);
  border-left: 4px solid var(--primary);
  background: var(--surface);
  border-radius: var(--radius);
  padding: 12px 15px;
  margin-bottom: 16px;
  font-size: 0.86rem;
  color: var(--text);
  box-shadow: var(--shadow);
}
.notice.danger {
  border-left-color: var(--bad-fill);
  background: rgba(var(--bad-rgb), 0.05);
}
.notice strong { color: var(--on-surface-strong); }
.notice code {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 0.85em;
  background: var(--surface-variant);
  padding: 1px 5px;
  border-radius: 3px;
  color: var(--accent-dim);
}
:root[data-theme="dark"] .notice code { color: var(--accent); }

.empty {
  text-align: center;
  padding: 44px 20px;
  color: var(--on-surface-variant);
  font-size: 0.9rem;
}
.empty h3 { color: var(--on-surface-strong); margin-bottom: 6px; }

/* --- Filter bar --------------------------------------------------------- */
.filters {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  align-items: center;
  padding: 12px 16px;
  border-bottom: 1px solid var(--outline-variant);
}
.filters .spacer { flex: 1; }
.result-count {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--on-surface-variant);
}

/* --- Footer ------------------------------------------------------------- */
footer.foot {
  border-top: 1px solid var(--outline-variant);
  margin-top: 30px;
  padding: 18px 0 32px;
  font-size: 0.78rem;
  color: var(--on-surface-faint);
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
  align-items: center;
}
/* Pushed to the far end of the row: it belongs to the build, not to the data
   freshness everything else in this footer reports on. */
footer.foot .foot-version { margin-left: auto; color: var(--on-surface-faint); }

/* --- Contact page ------------------------------------------------------- */
.contact-address { user-select: all; }
.contact-hint { color: var(--on-surface-variant); font-size: 0.85rem; margin-bottom: 0; }
footer.foot .fresh-ok { color: var(--good); font-weight: 600; }
footer.foot .fresh-stale { color: var(--warn); font-weight: 600; }
footer.foot .fresh-fail { color: var(--bad); font-weight: 600; }

/* --- Flash messages ----------------------------------------------------- */
.flashes { list-style: none; margin: 14px 0 0; padding: 0; }
.flashes li {
  border: 1px solid var(--outline-variant);
  border-left: 4px solid var(--primary);
  background: var(--surface);
  box-shadow: var(--shadow);
  padding: 10px 14px;
  border-radius: var(--radius);
  margin-bottom: 7px;
  font-size: 0.85rem;
}

/* --- Squad Builder ------------------------------------------------------ */
/* Rank column on the shortlists. Quiet on purpose: the
   order is already the order the rows are in, so the number only has to confirm
   it, not announce it. */
td.rank { color: var(--on-surface-faint); font-size: 0.78rem; width: 1%; }

/* A single FPL difficulty figure, used on the match list. The text colour is
   set per step by .fdr-N. */
.fdr-pill {
  display: inline-block;
  min-width: 31px;
  padding: 2px 6px;
  border-radius: var(--radius-xs);
  font-size: 0.74rem;
  font-weight: 700;
  text-align: center;
}

/* What a creator's line amounts to. Colour carries the direction — green for
   the calls that put a player in your team, red for the ones that take him out,
   neutral for "they talked about him and did not commit" — so the column can
   be scanned without reading a word of it. */
.verdict {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--radius-xs);
  font-size: 0.72rem;
  font-weight: 700;
  white-space: nowrap;
  border: 1px solid transparent;
}
.verdict.captain { background: var(--primary); color: var(--on-primary); border-color: var(--primary); }
.verdict.buy     { background: rgba(var(--good-rgb),0.18); color: var(--good-text); border-color: rgba(var(--good-rgb),0.42); }
.verdict.start   { background: rgba(var(--good-rgb),0.12); color: var(--good-text); border-color: rgba(var(--good-rgb),0.26); }
.verdict.keep    { background: rgba(var(--fdr-2-rgb),0.14); color: var(--good-text); border-color: rgba(var(--fdr-2-rgb),0.38); }
.verdict.sell    { background: rgba(var(--bad-rgb),0.16); color: var(--bad-text); border-color: rgba(var(--bad-rgb),0.42); }
.verdict.out     { background: rgba(var(--fdr-5-rgb),0.18); color: var(--bad-text); border-color: rgba(var(--fdr-5-rgb),0.5); }
.verdict.risk,
.verdict.doubt   { background: rgba(var(--warn-rgb),0.14); color: var(--warn-text); border-color: rgba(var(--warn-rgb),0.38); }
.verdict.mention { background: var(--wash-sink); color: var(--on-surface-variant); border-color: var(--outline-variant); }

/* The creator's own sentence. The one cell on the page allowed to wrap — it is
   prose, and truncating it would hide the thing the row exists to show. */
td.said {
  white-space: normal;
  max-width: 360px;
  min-width: 220px;
  color: var(--text);
  font-size: 0.82rem;
  line-height: 1.4;
}

/* One creator's sentence among several pooled on a player's row. */
.said-line { display: block; }
.said-line + .said-line {
  margin-top: 4px;
  padding-top: 4px;
  border-top: 1px dotted var(--outline-variant);
}

/* The player-level note under the creators' sentences. Set apart on its own line
   because it did not come from them. */
.said-note {
  display: block;
  margin-top: 3px;
  font-size: 0.72rem;
  color: var(--on-surface-variant);
}

/* An inline attribution or aside inside running text. */
.cite {
  font-size: 0.82em;
  font-weight: 600;
  color: var(--on-surface-variant);
  border-bottom: 1px dotted var(--outline);
}

/* --- Matches ------------------------------------------------------------ */
/* A fixture is a symmetrical thing — two teams either side of a score — so it
   is laid out that way rather than as a table row. The grid is the same before
   and after kickoff; only what sits in the middle column changes. */
.match {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 14px;
  padding: 11px 16px;
  border-bottom: 1px solid var(--outline-variant);
}
.match:last-child { border-bottom: 0; }
.match:hover { background: var(--wash-1); }

.m-side { display: flex; align-items: center; gap: 9px; min-width: 0; }

/* The club crest. Fixed box so a tall badge and a wide one occupy the same
   space and the score column stays on the centre line down the whole list. */
.crest {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  object-fit: contain;
}
/* Until the refresh has fetched one. Same footprint, so the row does not move
   when the images arrive. */
.crest-missing {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-xs);
  font-size: 0.54rem;
  font-weight: 700;
  color: var(--on-surface-faint);
}
.m-side.home { justify-content: flex-end; }
.m-side.away { justify-content: flex-start; }
.m-team {
  font-size: 0.92rem;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The winner, marked once. Not colour — a result is not good or bad, it just
   went one way, and the difficulty scale already owns green and red here. */
.m-team.won { font-weight: 800; color: var(--on-surface-strong); }
.m-side.home .m-team { text-align: right; }

.m-mid {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-width: 108px;
}
.m-kick {
  font-size: 0.76rem;
  color: var(--on-surface-variant);
  white-space: nowrap;
}
.m-score {
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 800;
  color: var(--on-surface-strong);
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
}
.m-score i { color: var(--on-surface-faint); font-style: normal; font-weight: 400; }
.m-state {
  font-size: 0.68rem;
  font-weight: 700;
  color: var(--on-surface-faint);
}
.m-state.live { color: var(--warn); display: inline-flex; align-items: center; gap: 5px; }

/* Who did what, under the score: a row per kind — goals, then assists, then
   cards — under each team's name and set against the centre line. */
.m-detail {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 1fr 1px 1fr;
  gap: 14px;
  align-items: start;
  margin-top: 8px;
}
.m-rule { background: var(--outline-variant); }
.m-returns { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.ret-row { display: flex; flex-wrap: wrap; gap: 4px 10px; min-height: 18px; }
.m-returns.right .ret-row { justify-content: flex-end; }
.m-returns.left .ret-row { justify-content: flex-start; }

.ret {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.78rem;
  color: var(--text);
  white-space: nowrap;
}
.ret-icon { font-size: 0.82em; opacity: 0.85; }
/* Assists get a drawn badge rather than an emoji: the squared-A character is
   missing from enough fonts to render as tofu, and a letter cannot fail. */
.assist-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  border: 1px solid var(--outline);
  border-radius: 3px;
  font-size: 0.6rem;
  font-weight: 700;
  line-height: 1;
  color: var(--on-surface-variant);
}
.ret.assist { color: var(--on-surface-variant); }
.ret.card { color: var(--on-surface-faint); font-size: 0.74rem; }
/* An own goal counted for the other side, so it is listed there — dimmed and
   labelled, because it is not the scorer's achievement. */
.ret.goal.og { color: var(--on-surface-variant); }
.ret-og {
  font-size: 0.85em;
  font-weight: 700;
  color: var(--on-surface-faint);
}

/* One gameweek's worth of fixtures, headed so a long list stays navigable. */
.match-group { border-bottom: 1px solid var(--outline); }
.match-group:last-child { border-bottom: 0; }
.mg-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 16px;
  background: var(--surface-variant);
  border-bottom: 1px solid var(--outline-variant);
}
.mg-gw {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--on-surface-strong);
}
.mg-note { font-size: 0.76rem; color: var(--on-surface-faint); }

/* --- Matchweek navigation ------------------------------------------------
   A step either side, every matchweek between them. The strip scrolls rather
   than wraps, so the bar stays one line tall at any window width and the season
   reads as a single timeline instead of a paragraph of numbers. */
.mw-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 18px;
  padding: 8px 9px;
  background: var(--surface);
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-m);
  box-shadow: var(--shadow);
}
.mw-step {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 31px;
  height: 31px;
  border: 1px solid var(--outline);
  border-radius: var(--radius-full);
  background: var(--surface);
  color: var(--on-surface-variant);
  font-size: 1.1rem;
  line-height: 1;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.mw-step:hover { border-color: var(--primary); color: var(--on-surface-strong); background: var(--wash-1); }
.mw-step.off { opacity: 0.25; pointer-events: none; }

.mw-strip {
  display: flex;
  gap: 3px;
  overflow-x: auto;
  scroll-behavior: smooth;
  flex: 1;
  padding: 1px;
  scrollbar-width: none;
}
.mw-strip::-webkit-scrollbar { display: none; }
.mw-pill {
  flex: 0 0 auto;
  min-width: 31px;
  padding: 6px 8px;
  text-align: center;
  border: 1px solid transparent;
  border-radius: var(--radius-full);
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--on-surface-variant);
  transition: background 0.14s ease, color 0.14s ease, border-color 0.14s ease;
}
.mw-pill:hover { background: var(--wash-1); color: var(--text); }
/* Three states, and the distinction that matters is played vs still to come —
   the boundary between them is where you are in the season. */
.mw-pill.s-played { color: var(--on-surface-faint); }
.mw-pill.s-live { color: var(--warn); border-color: rgba(var(--warn-rgb), 0.4); }
/* The current matchweek is a filled primary, the same as a selected team pill. */
.mw-pill.on {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-primary);
  font-weight: 700;
}
.mw-pill.on:hover { background: var(--primary); color: var(--on-primary); }

/* The same two steps again at the foot, named — so reaching the end of a
   matchweek does not mean scrolling back up to see the next one. */
.mw-feet {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 18px;
}
.mw-foot {
  display: inline-flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 15px;
  background: var(--surface);
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-m);
  box-shadow: var(--shadow);
  transition: border-color 0.15s ease;
}
.mw-foot:hover { border-color: var(--primary); }
.mw-foot.next { text-align: right; margin-left: auto; }
.mw-foot-dir {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--on-surface-variant);
}
.mw-foot-name { font-size: 0.88rem; font-weight: 600; color: var(--text); }
.mw-foot:hover .mw-foot-name { color: var(--on-surface-strong); }

/* Both sides' difficulty, under an unplayed kickoff time. */
.m-fdr { display: inline-flex; gap: 3px; }

@media (max-width: 720px) {
  .mw-foot-name { font-size: 0.8rem; }
  .match { grid-template-columns: 1fr auto 1fr; gap: 8px; padding: 10px 12px; }
  .m-mid { min-width: 72px; }
  .m-team { font-size: 0.82rem; }
  .m-side { gap: 6px; }
  .crest { width: 19px; height: 19px; }
  .m-detail { gap: 8px; }
}

/* --- Side by side comparison -------------------------------------------
   The explorer answers "who is out there"; this answers "which of these two".
   Ticking a row stands it up in a panel alongside the table, turned on its
   side — a column per player, a row per stat — so a decision is read across a
   line rather than carried between two rows forty apart. */
.compare-layout { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 18px; }
.compare-layout > .panel { margin-bottom: 0; }
/* min-width: 0 is what lets the table half shrink at all — without it a flex
   item refuses to go below the width of its content, and a 20-column table
   would simply push the panel off the page. */
.compare-layout > .players-panel { flex: 1 1 auto; min-width: 0; }
/* Nothing here shrinks. Sharing the overflow between the two halves squeezed
   a column of the comparison off its own edge while the table — which has a
   scroll box of its own to absorb it — kept width it did not need. The panel
   takes exactly what its columns ask for, up to the cap, and the table lives
   on what is left. */
.compare-panel {
  flex: 0 0 auto;
  min-width: 232px;
  max-width: min(620px, 46%);
  border-color: var(--outline);
  /* The list is the page's own scroll, so the panel keeps itself in view
     beside it rather than being left at the top of six hundred cards. Clear of
     the two-row top bar, which is sticky too. */
  position: sticky;
  top: 108px;
  max-height: calc(100vh - 120px);
  display: flex;
  flex-direction: column;
}
.compare-scroll { overflow: auto; min-height: 0; }
.compare-panel[hidden] { display: none; }
.cmp-title { display: flex; align-items: baseline; gap: 8px; }
.cmp-clear { padding: 4px 10px; font-size: 0.76rem; }

.cmp { width: max-content; min-width: 100%; font-size: 0.82rem; }
/* Beside only: with the stats down the side, every value is a number under
   a name and reads right-aligned. Below, the cells keep the alignment they
   have in the table above — a name left, a number right. */
.cmp-beside tbody td { text-align: right; }

/* The stat names, down the left. Sticky so they survive scrolling sideways
   through a long comparison, and therefore opaque: values pass underneath. */
.cmp .cmp-stat {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--surface);
  border-bottom: 1px solid var(--outline-variant);
  padding: 8px 10px 8px 13px;
  text-align: left;
  font-size: 0.76rem;
  font-weight: 700;
  color: var(--on-surface-variant);
  white-space: nowrap;
}
/* Corner of both sticky axes at once, so it is never crossed by either. */
.cmp .cmp-corner {
  left: 0;
  z-index: 3;
  background: var(--surface-variant);
}

.cmp .cmp-head {
  position: relative;
  min-width: 118px;
  padding: 8px 10px 9px 22px;
  text-align: right;
  vertical-align: bottom;
}
.cmp-name {
  display: block;
  font-family: var(--font-display);
  font-size: 0.88rem;
  font-weight: 700;
  color: var(--on-surface-strong);
}
.cmp-sub {
  display: block;
  font-size: 0.7rem;
  font-weight: 400;
  color: var(--on-surface-faint);
}
/* Dropping one player is the common edit — far more common than clearing the
   lot — so it lives on the column itself. Quiet until the column is pointed at.*/
.cmp-drop {
  position: absolute;
  top: 4px;
  left: 4px;
  width: 17px;
  height: 17px;
  padding: 0;
  line-height: 1;
  font-size: 12px;
  border-radius: 50%;
  border: 1px solid transparent;
  background: transparent;
  color: transparent;
}
.cmp-head:hover .cmp-drop { color: var(--on-surface-variant); border-color: var(--outline); }
.cmp-drop:focus-visible { color: var(--on-surface-variant); }
.cmp-drop:hover { color: var(--bad); border-color: var(--bad); box-shadow: none; }

/* The leading number in a row. A ring rather than another tint: the cell is
   very likely already carrying a band colour, and a second colour on top of it
   would be two opinions competing in one square. The ring says "best of these
   three", the fill still says "good for a defender". */
.cmp .is-best { box-shadow: inset 0 0 0 2px var(--primary); font-weight: 700; }

/* Six fixtures do not fit across a column this narrow, so the ticker folds to
   two rows of three instead of setting the width of the whole panel. */
.cmp .ticker { flex-wrap: wrap; justify-content: flex-end; max-width: 104px; }
.cmp .tick { min-width: 31px; font-size: 0.6rem; padding: 2px; }
.cmp .tick::before { font-size: 0.72em; margin-right: 0; }

/* --- The tick box in the explorer --------------------------------------- */
.cmp-cell { width: 1%; padding-right: 2px; text-align: center; }
.cmp-box {
  width: 14px;
  height: 14px;
  margin: 0;
  vertical-align: middle;
  accent-color: var(--primary);
  cursor: pointer;
}
/* --- Below: docked along the foot of the window --------------------------
   The same picks read the other way up — a row per player, a column per stat.
   Sticky to the bottom of the window, so the strip stays in view while you
   scroll the list for the next player to add. */
.compare-layout.stacked { flex-direction: column; }
.compare-layout.stacked > .players-panel,
.compare-layout.stacked > .compare-panel {
  width: 100%;
  max-width: none;
  min-width: 0;
}
/* Deep enough for a shortlist, never so deep that it crowds out the table it
   is meant to be read against. */
.compare-layout.stacked > .compare-panel {
  top: auto;
  bottom: 0;
  max-height: none;
  box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.12);
}
.compare-layout.stacked .compare-scroll { max-height: 42vh; }

.cmp-below { font-size: 0.84rem; }
.cmp-below .cmp-cell { text-align: center; }
/* A dropped pick is the common edit here too, and with no column heading to
   hang it off it takes the place of the tick box it came from. */
.cmp-below .cmp-drop { position: static; color: var(--on-surface-faint); }
.cmp-below tr:hover .cmp-drop { color: var(--on-surface-variant); border-color: var(--outline); }
.cmp-below .is-best { box-shadow: inset 0 0 0 2px var(--primary); }

/* Which way up. A segmented pair rather than a checkbox because neither is a
   mode of the other — they are two readings of the same picks. */
.cmp-tools { display: flex; align-items: center; gap: 8px; }
.cmp-orient { display: inline-flex; }
.cmp-orient button {
  padding: 4px 11px;
  font-size: 0.74rem;
  font-weight: 600;
  border-radius: 0;
}
.cmp-orient button:first-child { border-radius: var(--radius) 0 0 var(--radius); }
.cmp-orient button:last-child { border-radius: 0 var(--radius) var(--radius) 0; margin-left: -1px; }
.cmp-orient button.on {
  position: relative;
  z-index: 1;
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-primary);
  box-shadow: none;
}
.cmp-orient button.on:hover { background: var(--primary); color: var(--on-primary); border-color: var(--primary); }

/* Below this the two halves stop being a split and become a stack: the table
   needs most of a narrow window on its own. */
@media (max-width: 1080px) {
  .compare-layout { flex-direction: column; }
  .compare-layout > .players-panel,
  .compare-panel { width: 100%; max-width: none; }
  /* Forced to stack, the beside panel is what you want in front of you rather
     than below 650 rows. The below layout is already in its intended order. */
  .compare-layout:not(.stacked) > .compare-panel { order: -1; position: static; max-height: none; }

  /* Wide enough to sit beside the table above this width; below it the panel
     is over the page, so it says so — a scrim, and a tap anywhere off it
     closes. That is the one place this behaves modally. */
  .drawer { width: min(680px, 100vw); }
  .drawer-scrim { display: block; }
}

/* --- Squad Builder shortlist cards ---------------------------------------
   One card per player, laid out like a broadcast graphic: crest, the shirt on
   a tile with the name plate under it, then the figures as label-over-value
   pairs. The figures the current ranking is built from carry the accent, so the
   past/upcoming toggle visibly changes what the card is about. */
.basis-bar { display: inline-flex; align-items: center; gap: 10px; }
.pcards {
  list-style: none;
  margin: 0;
  padding: 12px;
  display: grid;
  gap: 10px;
}
.pcard {
  display: grid;
  grid-template-columns: 20px 52px 96px 1fr;
  align-items: center;
  gap: 16px;
  padding: 12px 18px 12px 12px;
  background: var(--surface-variant);
  border: 1px solid var(--outline);
  border-radius: var(--radius-m);
}
.pc-rank {
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--on-surface-faint);
  text-align: center;
  cursor: help;
}
.pc-crest { width: 52px; height: 52px; object-fit: contain; }
.pc-crest.crest-missing { font-size: 0.72rem; }
.pc-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 8px;
  background: rgba(var(--accent-rgb), 0.16);
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius);
  overflow: hidden;
}
.pc-kit { display: block; width: 56px; height: auto; margin-bottom: 6px; }
.pc-tile .kit-missing { width: 56px; height: 56px; margin: 0 0 6px; }
.pc-name {
  align-self: stretch;
  padding: 3px 4px;
  background: var(--surface);
  color: var(--on-surface-strong);
  font-size: 0.74rem;
  font-weight: 700;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The shirt tile and, under it, availability in words. Kept out of the tile so
   no kit colour can swallow it. */
.pc-who { display: flex; flex-direction: column; align-items: stretch; gap: 5px; }
.pc-status { display: flex; flex-direction: column; align-items: center; gap: 3px; }
.pc-status .badge {
  max-width: 100%;
  background: var(--surface);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: help;
}
.pc-stats {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 10px 24px;
}
.pc-stat { min-width: 44px; text-align: center; }
.pc-stat dt {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--on-surface-variant);
  margin-bottom: 2px;
}
.pc-stat dd {
  margin: 0;
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--on-surface-strong);
}
.pc-stat dd.miss { color: var(--on-surface-faint); }
/* PPG's move with the latest game, in its own cell after the figure and at
   the same size, pulled in close so it still reads as belonging to PPG. */
.trend {
  font-weight: 700;
  white-space: nowrap;
  cursor: help;
}
/* Fixed width, so "▼" and "▲+10" take the same room and the figures after
   them stay in line from one card to the next. */
.pc-stat.pc-trend { min-width: 0; width: 3.6em; margin-left: -14px; text-align: left; }
td.trend-cell { padding-left: 0; }
.trend.up   { color: var(--good); }
.trend.down { color: var(--bad); }
.pc-stat.on dt { color: var(--accent); }
.pc-fixtures { text-align: left; }
.pc-fixtures dd { font-size: inherit; padding-top: 4px; }
/* The cards have the room the tables do not, so the run gets bigger cells. */
.pc-fixtures .ticker-long { gap: 3px; }
.pc-fixtures .ticker-long .tick { min-width: 36px; font-size: 0.7rem; padding: 4px 5px; }
.pc-fixtures .ticker-long .tick::before { font-size: 0.8em; margin-right: 2px; }

/* --- The same cards on Player Stats --------------------------------------
   The explorer lists every player on the Squad Builder's card. What it adds:
   a tick box under the place number, the position on the shirt tile, the name
   plate as the button that opens the player panel, and each figure carrying
   its colour band as a pill. The figure the list is sorted by takes the
   accent, as the ranking figures do on the builder. */
.pcard[hidden] { display: none; }
.pc-side {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
/* Places are counted rather than written, so they follow the sort and skip
   whatever the filters have hidden. */
.pcards.counted { counter-reset: place; }
.pcards.counted > .pcard:not([hidden]) { counter-increment: place; }
.pcards.counted .pc-rank::before { content: counter(place); }
.pc-pos {
  position: absolute;
  top: 4px;
  left: 4px;
  padding: 0 4px;
  font-size: 0.62rem;
  background: var(--surface);
  cursor: help;
}
button.pc-name {
  display: block;
  width: 100%;
  border: 0;
  border-radius: 0;
  font-family: inherit;
  cursor: pointer;
}
button.pc-name:hover { color: var(--accent-dim); box-shadow: none; }
:root[data-theme="dark"] button.pc-name:hover { color: var(--accent); }
button.pc-name:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
/* The selected team's players wear a filled name plate. */
.pcard.owned .pc-name { background: var(--primary); color: var(--on-primary); }
.pcard.owned button.pc-name:hover { color: var(--on-primary); text-decoration: underline; }

.pc-stat dd.b-good, .pc-stat dd.b-avg, .pc-stat dd.b-bad, .pc-stat dd.b-thin {
  padding: 0 6px;
  border-radius: var(--radius-xs);
}
.pc-stat dd.b-good { color: var(--good-text); }
.pc-stat dd.b-avg  { color: var(--warn-text); }
.pc-stat dd.b-bad  { color: var(--bad-text); }
.pc-stat dd.b-thin { color: var(--muted); }
.pc-stat dd .miss { color: var(--on-surface-faint); }
.pc-sample { font-size: 0.62em; font-weight: 600; opacity: 0.7; }
/* The run gets a line of its own, starting under Pts rather than under £m:
   £m is held to a fixed width so that offset is the same on every card. */
.pcards-explorer .pc-stat[data-stat="price"] { width: 52px; }
.pcards-explorer .pc-fixtures { flex-basis: 100%; padding-left: calc(52px + 24px); }

/* A ticked card carries the selected wash and a bar down its edge; the card
   whose panel is open gets a ring, so both can be true at once. */
.pcard.picked {
  background: var(--primary-container);
  color: var(--on-primary-container);
  box-shadow: inset 3px 0 0 var(--primary);
}
.pcard.viewing { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary); }
.pcard.picked.viewing { box-shadow: inset 3px 0 0 var(--primary), 0 0 0 1px var(--primary); }

/* --- My FPL Stats ----------------------------------------------------------
   Any team by its FPL id. Everything here is built from the components above
   (panels, stat tiles, sub-tabs, the pitch, the team pills); what is new is
   the id form, the brief lists and the two season charts. */
.page-sub { margin-top: 4px; font-size: 0.86rem; color: var(--on-surface-variant); }

.lookup-form { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; }
.lookup-form input { width: 12ch; font-size: 1rem; padding: 8px 12px; letter-spacing: 0.02em; }
.lookup-form .btn-accent { padding: 8px 14px; }
.lookup-form.compact input { font-size: 0.9rem; padding: 6px 10px; }
.lookup-form.compact .btn-accent { padding: 6px 12px; }
.lookup-error { flex-basis: 100%; margin: 0; font-size: 0.8rem; font-weight: 600; color: var(--bad); }
.lookup-error[hidden] { display: none; }

.lookup-start { display: grid; gap: 22px; padding: 22px 20px; }
.lookup-help h3 { margin-bottom: 10px; }
.lookup-help h4 { font-size: 0.86rem; margin-bottom: 6px; }
.lookup-ways { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px 28px; }
.lookup-ways ol { margin: 0; padding-left: 20px; color: var(--text); font-size: 0.88rem; }
.lookup-ways li { margin-bottom: 5px; }
.lookup-ways li::marker { font-weight: 700; color: var(--on-surface-variant); }
.lookup-example {
  margin: 14px 0 0;
  padding: 9px 12px;
  border-radius: var(--radius);
  background: var(--surface-variant);
  font-size: 0.84rem;
  color: var(--on-surface-variant);
  overflow-wrap: anywhere;
}
.lookup-example mark {
  background: rgba(var(--accent-rgb), 0.16);
  color: var(--on-surface-strong);
  font-weight: 700;
  border-radius: var(--radius-xs);
  padding: 0 3px;
}
.lookup-more { padding: 0; }
.lookup-more summary {
  cursor: pointer;
  padding: 13px 16px;
  font-weight: 700;
  color: var(--on-surface-variant);
}
.lookup-more[open] summary { border-bottom: 1px solid var(--outline-variant); }

/* A line per point, the point's name held in a column on the left. */
.brief-list { list-style: none; margin: 0; padding: 6px 16px 10px; }
.brief-list li {
  display: flex;
  gap: 14px;
  padding: 8px 0;
  border-bottom: 1px solid var(--outline-variant);
  font-size: 0.9rem;
  line-height: 1.45;
}
.brief-list li:last-child { border-bottom: 0; }
/* Running sentences rather than key and value: one line of text, wrapping. */
.brief-list.plain { padding: 4px 0 0; }
.brief-list.plain li { display: block; }
.brief-k {
  flex: 0 0 104px;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--on-surface-variant);
  padding-top: 2px;
}
/* An explanation on hover or focus, where a sentence would crowd the line. */
.info {
  display: inline-grid;
  place-items: center;
  width: 15px;
  height: 15px;
  margin-left: 3px;
  border: 1px solid var(--outline);
  border-radius: 50%;
  font-size: 0.62rem;
  font-weight: 700;
  font-style: italic;
  color: var(--on-surface-faint);
  cursor: help;
  vertical-align: 1px;
}
.info:hover, .info:focus { color: var(--on-surface-strong); border-color: var(--on-surface-variant); }

.lookup-gwbar {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--outline-variant);
}
.lookup-gw { display: flex; flex-direction: column; min-width: 130px; text-align: center; }
.lookup-gw strong { font-size: 1rem; color: var(--on-surface-strong); }
.lookup-gw span { font-size: 0.82rem; color: var(--on-surface-variant); }
.lookup-gw .live { justify-content: center; }

.lookup-tiles { margin: 16px; }
.lookup-tiles .stat .v, .lookup-tiles .stat .sub { display: block; }
.lookup-tiles.flat { margin-top: 4px; }
.lookup-h { margin-bottom: 2px; }
.lookup-subhead { border-top: 1px solid var(--outline-variant); }
.gw-diff { margin-left: 4px; font-size: 0.78rem; font-weight: 600; }

/* --- The two season charts ------------------------------------------------ */
.lookup-charts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  border-top: 1px solid var(--outline-variant);
}
.lookup-chart { padding: 16px; min-width: 0; }
.lookup-chart + .lookup-chart { border-left: 1px solid var(--outline-variant); }

/* Week by week: you and the average manager as two dots on one track. Shape
   tells them apart as well as fill, so the pair never rests on colour alone. */
.db-legend { display: flex; gap: 16px; margin: 10px 0 4px; font-size: 0.78rem; color: var(--on-surface-variant); }
.db-legend > span { display: inline-flex; align-items: center; gap: 6px; }
.db-legend .db-dot { position: static; transform: none; }
.db-list { list-style: none; margin: 0; padding: 0; }
.db-row { padding: 6px 0 8px; border-bottom: 1px solid var(--outline-variant); }
.db-row:last-child { border-bottom: 0; }
.db-head { display: flex; justify-content: space-between; font-size: 0.82rem; color: var(--on-surface-variant); }
.db-head span:first-child { font-weight: 700; color: var(--text); }
.db-head strong { color: var(--on-surface-strong); }
.db-track { position: relative; height: 14px; margin: 4px 6px 0; }
.db-track::before {
  content: '';
  position: absolute;
  inset: 6px 0 auto;
  height: 2px;
  background: var(--outline-variant);
}
.db-link { position: absolute; top: 6px; height: 2px; background: var(--on-surface-faint); }
.db-link.up { background: var(--good); }
.db-link.down { background: var(--bad); }
.db-dot {
  position: absolute;
  top: 7px;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  box-shadow: 0 0 0 2px var(--surface);
}
.db-dot.you { background: var(--primary); }
.db-dot.avg { background: var(--surface); border: 2px solid var(--on-surface-variant); }

/* Overall rank as places gained or lost: green up from the line, red down
   from it — FPL's own rank arrows, drawn to size. The sign is printed on every
   label, so the direction never rests on colour alone. */
.rank-headline { margin: 6px 0 2px; font-size: 0.95rem; color: var(--on-surface-variant); }
.rank-headline strong { font-size: 1.15rem; color: var(--on-surface-strong); }
.rm-chart { display: flex; gap: 4px; margin-top: 12px; }
.rm-col { flex: 1 1 0; min-width: 0; max-width: 56px; display: flex; flex-direction: column; }
.rm-half { position: relative; height: 104px; display: flex; justify-content: center; }
.rm-half.up { align-items: flex-end; padding-top: 18px; border-bottom: 1px solid var(--outline); }
.rm-half.down { align-items: flex-start; padding-bottom: 18px; }
.rm-bar { position: relative; display: block; width: 72%; max-width: 28px; min-height: 2px; }
.rm-half.up .rm-bar { background: var(--good); border-radius: var(--radius-xs) var(--radius-xs) 0 0; }
.rm-half.down .rm-bar { background: var(--bad); border-radius: 0 0 var(--radius-xs) var(--radius-xs); }
/* Cut off to keep the other bars readable: a break across the far end. */
.rm-bar.capped::after {
  content: '';
  position: absolute;
  left: -2px;
  right: -2px;
  height: 3px;
  background: var(--surface);
  transform: skewY(-12deg);
}
.rm-half.up .rm-bar.capped::after { top: 8px; }
.rm-half.down .rm-bar.capped::after { bottom: 8px; }
.rm-label {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.68rem;
  font-weight: 700;
  color: var(--text);
  white-space: nowrap;
}
.rm-half.up .rm-label { bottom: calc(100% + 2px); }
.rm-half.down .rm-label { top: calc(100% + 2px); }
.rm-gw { text-align: center; font-size: 0.68rem; color: var(--on-surface-variant); min-height: 1em; }

/* --- League ----------------------------------------------------------------- */
.lookup-leagues { margin: 0; padding: 12px 16px; border-bottom: 1px solid var(--outline-variant); border-radius: 0; box-shadow: none; border-left: 0; border-right: 0; border-top: 0; }
.lookup-league { min-height: 120px; }
.lookup-loading { display: flex; align-items: center; justify-content: center; gap: 10px; padding: 44px 20px; color: var(--on-surface-variant); }

.lookup-block { padding: 16px; border-top: 1px solid var(--outline-variant); min-width: 0; }
.lookup-block h4 { font-size: 0.88rem; margin: 12px 0 2px; }
.lookup-block p { margin: 2px 0 0; font-size: 0.88rem; }
.lookup-block .plan-note { margin-bottom: 6px; }
.lookup-block-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 2px; }
.lookup-copy { margin-left: auto; padding: 4px 10px; font-size: 0.76rem; }
.lookup-swap { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 6px !important; }
.swap-out, .swap-in {
  padding: 2px 9px;
  border: 1px solid var(--outline);
  border-radius: var(--radius-full);
  font-weight: 600;
  font-size: 0.82rem;
}
.swap-in { border-color: rgba(var(--good-rgb), 0.5); background: rgba(var(--good-rgb), 0.08); }

.lookup-cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  border-top: 1px solid var(--outline-variant);
}
.lookup-cards .lookup-block { border-top: 0; border-bottom: 1px solid var(--outline-variant); }
.lookup-cards .lookup-block:nth-child(odd) { border-right: 1px solid var(--outline-variant); }

.plist { list-style: none; margin: 4px 0 0; padding: 0; }
.plist li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--outline-variant);
  font-size: 0.88rem;
}
.plist li:last-child { border-bottom: 0; }
.plist.tight li { padding: 4px 0; border-bottom: 0; }
.plist-sub { display: block; font-size: 0.74rem; font-weight: 400; color: var(--on-surface-variant); }
.plist-v { text-align: right; flex-shrink: 0; }

/* One series, so one colour: the accent, as on the rank line. */
.hbars { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 9px; }
.hbars li { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 2px 10px; }
.hbar-name { grid-column: 1 / -1; font-size: 0.86rem; font-weight: 600; }
.hbar { height: 6px; border-radius: var(--radius-full); background: var(--surface-variant); overflow: hidden; }
.hbar-fill { display: block; height: 100%; border-radius: var(--radius-full); background: var(--accent); }
.hbar-v { font-size: 0.8rem; color: var(--on-surface-variant); min-width: 52px; text-align: right; }

.versus { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.versus h4 { margin-top: 6px; font-size: 0.8rem; color: var(--on-surface-variant); }

.lookup-standings th:first-child { width: 56px; }
.lookup-standings td.move { width: 1%; padding-left: 0; font-weight: 700; }
.lookup-standings td.name { width: 70%; }
.lookup-standings tr.me td { background: rgba(var(--accent-rgb), 0.08); }
.lookup-standings tr.me td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
.lookup-standings tr.viewing td { background: var(--wash-2); }
.lookup-standings tr.gap td {
  padding: 2px 9px;
  text-align: center;
  color: var(--on-surface-faint);
  background: var(--surface-sunken);
  letter-spacing: 0.2em;
}
.lookup-standings tr.gap:hover { background: none; }
/* The manager's name opens their team. A plain line of text until hovered,
   the same as a player's name on Player Stats. */
.lookup-mgr {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
  font-weight: 600;
  color: var(--on-surface-strong);
}
/* On Private Stats only some league names open details; those say so. */
.lookup-mgr[data-league-url] {
  text-decoration: underline dotted var(--on-surface-faint);
  text-underline-offset: 3px;
}
.lookup-mgr.on { color: var(--accent-dim); }
:root[data-theme="dark"] .lookup-mgr.on { color: var(--accent); }
.lookup-mgr:hover { border: 0; box-shadow: none; color: var(--accent-dim); text-decoration: underline; text-underline-offset: 3px; }
:root[data-theme="dark"] .lookup-mgr:hover { color: var(--accent); }

/* --- The manager panel ------------------------------------------------------ */
.mgr { display: grid; gap: 1px; background: var(--outline-variant); }
.mgr-summary { display: grid; grid-template-columns: 1fr 210px; gap: 16px 22px; }
.mgr-story p { margin: 0 0 8px; font-size: 0.9rem; line-height: 1.5; }
.mgr-story p:last-child { margin-bottom: 0; }
.mgr-facts { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 16px; margin: 0; align-content: start; }
.mgr-facts dt { font-size: 0.74rem; font-weight: 600; color: var(--on-surface-variant); }
.mgr-facts dd { margin: 2px 0 0; font-weight: 700; color: var(--on-surface-strong); font-size: 0.88rem; }
.mgr-facts dd .miss { display: block; font-weight: 400; font-size: 0.78rem; }
/* The pitch at panel width: four or five to a line still fit side by side. */
.mgr-squad .pitch { margin: 10px -16px 0; padding: 10px 12px; }
.mgr-squad .bench-strip { margin: 0 -16px -16px; }
.mgr-squad .line-label { display: none; }
.mgr-squad .shirt { width: 104px; }
.mgr-squad .shirt.bench { width: 96px; }
.mgr-squad .bench-label { flex-basis: auto; }

/* --- Responsive --------------------------------------------------------- */
@media (max-width: 720px) {
  .pcard { grid-template-columns: 16px 36px 84px 1fr; gap: 10px; padding: 10px; }
  .pc-crest { width: 36px; height: 36px; }
  .pc-stats { grid-column: 1 / -1; gap: 8px 16px; }
  .pc-stat dd { font-size: 1rem; }
  .pcards-explorer .pc-fixtures { padding-left: calc(52px + 16px); }
  .wrap { padding: 0 12px; }
  .topbar-inner { padding: 9px 12px; gap: 12px; }
  .topbar-navrow .topbar-inner { padding: 0 6px; }
  nav.pages a { padding: 11px 10px; font-size: 0.82rem; }
  .topbar-right { margin-left: auto; }
  .shirt { width: calc(50% - 6px); }
  .page-head { padding: 18px 0 12px; }
  .detail { grid-template-columns: 1fr; }
  .team-bar-note { margin-left: 0; width: 100%; }
  .brief-list li { flex-direction: column; gap: 2px; }
  .brief-k { flex-basis: auto; }
  .lookup-chart + .lookup-chart { border-left: 0; border-top: 1px solid var(--outline-variant); }
  .lookup-cards, .lookup-charts { grid-template-columns: 1fr; }
  .lookup-cards .lookup-block:nth-child(odd) { border-right: 0; }
  .lookup-tiles { margin: 12px; grid-template-columns: 1fr 1fr; }
  .lookup-form input { flex: 1; min-width: 0; }
  .mgr-summary { grid-template-columns: 1fr; }
  .mgr-squad .shirt { width: calc(33% - 6px); }
}

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

/* Gameweek recap on Private Stats: a share card in the style of FPL's own, drawn in
   the brand's ultraviolet. Its colours are fixed rather than themed, since it
   is a picture of the gameweek and should look the same to everyone. */
.recap {
  max-width: 420px;
  margin: 0 auto;
  padding: 26px 22px 18px;
  border-radius: var(--radius-m);
  background:
    radial-gradient(120% 60% at 0% 100%, rgba(0,255,135,0.28), transparent 60%),
    linear-gradient(160deg, var(--uv-soft) 0%, var(--uv) 45%, var(--uv-deep) 100%);
  color: var(--g0);
  box-shadow: var(--shadow);
}
.recap-head { text-align: center; margin-bottom: 18px; }
.recap-score, .recap-team { font-size: 1.7rem; font-weight: 800; line-height: 1.15; color: var(--g0); }
.recap-gw { font-size: 1.05rem; font-weight: 700; margin-top: 4px; }
.recap-meta { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; margin-top: 10px; font-size: 0.78rem; font-weight: 600; }
.recap-meta span { background: rgba(255,255,255,0.55); border-radius: 999px; padding: 2px 9px; }
.recap-meta .bad { color: #B00020; }
.recap-meta .recap-chip { background: var(--g0); color: var(--sig-green); }

.recap-card { background: var(--white); border-radius: 18px; padding: 6px; }
.recap-top {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 16px; border-radius: 14px;
  background: linear-gradient(135deg, var(--g12), rgba(var(--uv-rgb),0.35));
}
.recap-label { font-size: 0.8rem; font-weight: 700; }
.recap-top-pts { font-size: 1.5rem; font-weight: 800; margin: 6px 0 4px; }
.recap-top-name { font-weight: 700; }
.recap-top-pos { font-size: 0.75rem; color: var(--g5); }
.recap-kit { width: 96px; height: auto; flex-shrink: 0; }

.recap-armbands { display: grid; grid-template-columns: 1fr 1fr; padding: 12px 0; text-align: center; font-weight: 700; color: var(--g0); }
.recap-armbands > div { display: flex; flex-direction: column; align-items: center; gap: 3px; }
.recap-armbands > div + div { border-left: 1px solid var(--g10); }
.recap-badge {
  display: inline-grid; place-items: center; width: 18px; height: 18px; border-radius: 50%;
  background: var(--g0); color: var(--white); font-size: 0.65rem; font-weight: 800;
}
.recap-badge.small { width: auto; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px; font-size: 0.6rem; margin-left: 4px; background: var(--uv-deep); }
.recap-note { margin: -4px 12px 10px; font-size: 0.75rem; color: var(--g5); text-align: center; }

.recap-lineup { list-style: none; margin: 0; padding: 12px 16px; border-radius: 14px; background: var(--g1); color: var(--white); }
.recap-lineup li { display: grid; grid-template-columns: 64px 1fr auto; align-items: center; gap: 10px; padding: 4px 0; }
.recap-pts { color: var(--g8); font-size: 0.85rem; }
.recap-pts strong { color: var(--white); font-size: 0.95rem; }
.recap-name { font-weight: 700; }
.recap-pos { font-size: 0.68rem; font-weight: 600; color: var(--g9); background: rgba(255,255,255,0.08); border-radius: 999px; padding: 2px 8px; }
.recap-sub { margin-left: 4px; font-size: 0.62rem; font-weight: 700; color: var(--sig-green); text-transform: uppercase; }
.recap-bench { margin-top: 12px; text-align: center; font-size: 0.78rem; color: var(--g1); }

/* The recap opens as a modal over Private Stats. <dialog> brings Esc to close, a
   focus trap and the backdrop for free; this only strips the browser's frame
   so the card itself is the window. */
.recap-dialog {
  width: min(420px, calc(100vw - 24px));
  max-height: calc(100dvh - 24px);
  padding: 0;
  border: 0;
  border-radius: var(--radius-m);
  background: transparent;
  overflow: auto;
}
.recap-dialog::backdrop { background: rgba(var(--g0-rgb), 0.6); }
.recap-dialog .recap { box-shadow: none; }
.recap-close {
  position: absolute; top: 10px; right: 10px; z-index: 1;
  width: 32px; height: 32px; padding: 0; border: 0; border-radius: 50%;
  background: rgba(255,255,255,0.6); color: var(--g0);
  font-size: 1.3rem; line-height: 1; cursor: pointer;
}
.recap-close:hover { background: var(--white); }


/* --- Fixture grid (Squad Builder) ---------------------------------------
   Every club down the side, every gameweek across the top, one FPL-coloured
   block per match. A double stacks two blocks in the cell; a blank is hollow,
   as it is on the ticker. The club column stays put when the weeks scroll. */
.fdr-grid td.fg-cell { padding: 3px; vertical-align: middle; }
.fdr-grid th:first-child,
.fdr-grid td:first-child {
  position: sticky;
  left: 0;
  background: var(--surface);
  z-index: 1;
}
.fdr-grid thead th:first-child { background: var(--surface-variant); z-index: 3; }
/* The club name opens its season in the drawer, so it is a button dressed
   as the name it replaces. */
.fg-club {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--on-surface-strong);
  cursor: pointer;
}
.fg-club:hover { border: 0; box-shadow: none; color: var(--accent-dim); text-decoration: underline; text-underline-offset: 3px; }
:root[data-theme="dark"] .fg-club:hover { color: var(--accent); }
.fdr-grid tr.viewing td { background: var(--wash-2); }
.fdr-grid tr.viewing td:first-child { box-shadow: inset 3px 0 0 var(--primary); }

/* A club's season in the drawer: home and away side by side while the
   drawer has the room, one above the other once it does not. */
.detail.club-split { grid-template-columns: 1fr 1fr; }
.club-split .gwlog-wrap { display: block; max-height: none; }
.club-split table.gwlog { width: 100%; }
.club-split .tick { margin-right: 4px; }
@media (max-width: 720px) {
  .detail.club-split { grid-template-columns: 1fr; }
}
.fg-fx {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  min-width: 76px;
  padding: 5px 7px;
  border-radius: var(--radius-xs);
  font-size: 0.74rem;
  font-weight: 700;
}
.fg-fx + .fg-fx { margin-top: 2px; }
.fg-n { font-variant-numeric: tabular-nums; opacity: 0.8; }
.fg-fx.blank {
  justify-content: center;
  background: transparent;
  border: 1px dashed var(--outline);
  color: var(--on-surface-faint);
}
