/* Kisco Football — platform design system.
   Consumed as plain CSS by Jinja2 templates. No build step, no CDN.
   Per-league identity is ONE variable: --accent (+ its on/soft derivations).

   Copied from docs/research/design-package/styles.css. Four changes were made
   on the way in, each one carrying out a decision the package itself states:
   the @font-face rules below (the package left them out while the type was
   still being chosen), one --sans/--display/--mono declaration instead of two
   (see the type block), and the removal of the probability dots and the
   superscript-dagger note, both of which decisions.html records as discarded
   alternates. */

/* ---------- fonts ----------
   Self-hosted, subset to latin + latin-ext because La Liga club names need the
   accents. The build copies them next to this file (see kisco.render.write);
   nothing is fetched from a font CDN, so a page renders the same whether or not
   Google is reachable and no reader's address leaves the box. */
@font-face{font-family:"IBM Plex Sans";src:url("/static/fonts/IBMPlexSans-Variable.woff2") format("woff2-variations");font-weight:400 700;font-display:swap}
@font-face{font-family:"IBM Plex Mono";src:url("/static/fonts/IBMPlexMono-Regular.woff2") format("woff2");font-weight:400;font-display:swap}
@font-face{font-family:"IBM Plex Mono";src:url("/static/fonts/IBMPlexMono-SemiBold.woff2") format("woff2");font-weight:600;font-display:swap}

/* ---------- tokens: neutrals, light ---------- */
:root{
  --ink:oklch(0.22 0.012 265);--ink-2:oklch(0.42 0.014 265);--dim:oklch(0.56 0.013 265);
  --line:oklch(0.90 0.008 265);--line-2:oklch(0.84 0.010 265);
  --bg:oklch(0.99 0.003 265);--surface:oklch(0.975 0.004 265);--raise:oklch(1 0 0);
  /* The default accent is the neutral family, deliberately no competition's
     hue: a surface without a data-league slug — the hub pages, or a card that
     forgot its slug — reads as the plain page, never as some other
     competition's colours (home.html leans on exactly this). */
  --accent:oklch(0.52 0.014 265);--accent-ink:oklch(0.40 0.014 265);--accent-soft:oklch(0.95 0.008 265);--on-accent:oklch(0.99 0.005 265);
  --live:oklch(0.47 0.12 152);--live-soft:oklch(0.965 0.03 152);
  --note:oklch(0.52 0.075 75);--note-soft:oklch(0.965 0.035 75);
  --up:oklch(0.52 0.11 152);--down:oklch(0.55 0.14 25);
  /* The card glyphs' own colours: a real card's vivid yellow and red, not the
     muted amber and red of the attention pair (--note, --down) above. */
  --card-yellow:oklch(0.84 0.17 95);--card-red:oklch(0.58 0.21 27);
  --fantasy:oklch(0.50 0.15 340);
  --shadow:0 1px 2px oklch(0.22 0.012 265/.06),0 4px 14px oklch(0.22 0.012 265/.05);
  --lede:1.02rem;--dens:1;
  /* per-competition accent pairs — the whole of per-competition identity.
     Hues are budgeted by country, so a column of the hub reads as one family:
     England takes the blues (234–272), Spain the golds (42–58), the UEFA club
     competitions the violets (287–313, minted ahead of their editions), and
     the pre-season friendlies keep teal 196, and Germany takes crimson 10
     (the Bundesliga; its column's flagship, so the "More football" header
     wears it). International football takes the olive greens (107–133):
     pitch green, one wedge for the whole Internationals column, its top
     shade 19° short of the live green, and its tint a step quieter than
     the live wash, so a Nations League card never reads as a match in play
     and its nav pill never as a live row. The semantic colours own green
     152 (live/up), red 25 (down) and amber 75 (note) — no competition may
     look like any of the three; crimson 10 sits a wedge below the down red
     and is a darker, fuller colour than it. Italy takes cyan 176 (Serie
     A), a wedge short of the friendlies' teal it sits beside, and MLS
     magenta 335 — a fresh wedge of its own, 22° past the Europa League's
     violet and 35° short of the Bundesliga's crimson; the fantasy tint at
     340 is a link colour, never a surface, so the two do not meet. The
     wheel is now spoken for family by family; whoever arrives next shares
     one, or takes a wedge between two. */
  --epl-a:oklch(0.52 0.12 248);--epl-ai:oklch(0.40 0.13 248);--epl-as:oklch(0.95 0.028 248);
  --chm-a:oklch(0.46 0.115 261);--chm-ai:oklch(0.37 0.12 261);--chm-as:oklch(0.95 0.028 261);
  --fac-a:oklch(0.41 0.11 272);--fac-ai:oklch(0.36 0.12 272);--fac-as:oklch(0.945 0.03 272);
  --efl-a:oklch(0.52 0.095 234);--efl-ai:oklch(0.42 0.11 234);--efl-as:oklch(0.95 0.028 234);
  --lal-a:oklch(0.54 0.13 42);--lal-ai:oklch(0.42 0.14 42);--lal-as:oklch(0.955 0.03 42);
  --cdr-a:oklch(0.50 0.115 58);--cdr-ai:oklch(0.40 0.12 58);--cdr-as:oklch(0.955 0.032 58);
  --ucl-a:oklch(0.42 0.125 300);--ucl-ai:oklch(0.36 0.13 300);--ucl-as:oklch(0.95 0.03 300);
  --uel-a:oklch(0.52 0.13 313);--uel-ai:oklch(0.41 0.14 313);--uel-as:oklch(0.95 0.03 313);
  --uecl-a:oklch(0.53 0.11 287);--uecl-ai:oklch(0.43 0.12 287);--uecl-as:oklch(0.95 0.028 287);
  --frd-a:oklch(0.50 0.09 196);--frd-ai:oklch(0.39 0.10 196);--frd-as:oklch(0.95 0.028 196);
  --bul-a:oklch(0.50 0.15 10);--bul-ai:oklch(0.40 0.15 10);--bul-as:oklch(0.955 0.03 10);
  --sea-a:oklch(0.50 0.11 176);--sea-ai:oklch(0.40 0.12 176);--sea-as:oklch(0.95 0.03 176);
  --inf-a:oklch(0.52 0.10 107);--inf-ai:oklch(0.41 0.11 107);--inf-as:oklch(0.95 0.03 107);
  --unl-a:oklch(0.50 0.12 133);--unl-ai:oklch(0.40 0.13 133);--unl-as:oklch(0.95 0.024 133);
  --cnl-a:oklch(0.52 0.115 120);--cnl-ai:oklch(0.41 0.12 120);--cnl-as:oklch(0.95 0.03 120);
  --mls-a:oklch(0.50 0.13 335);--mls-ai:oklch(0.40 0.14 335);--mls-as:oklch(0.955 0.03 335);
  color-scheme:light;
}
/* ---------- tokens: dark ---------- */
:root[data-theme="dark"]{
  /* The secondary inks are measured, not eyeballed: --dim carries the
     smallest type (10.5px tags, 11px mono provenance and minutes) and at
     L 0.64 sat at 4.8:1 on a card (--raise) — a reader called it hard to
     read. 0.75 clears 7:1 on every neutral surface; --ink-2 steps up with
     it so the three inks keep their order. The light palette is untouched. */
  --ink:oklch(0.94 0.008 265);--ink-2:oklch(0.86 0.010 265);--dim:oklch(0.75 0.012 265);
  --line:oklch(0.31 0.011 265);--line-2:oklch(0.38 0.013 265);
  --bg:oklch(0.175 0.010 265);--surface:oklch(0.215 0.012 265);--raise:oklch(0.245 0.013 265);
  --accent:oklch(0.72 0.012 265);--accent-ink:oklch(0.80 0.010 265);--accent-soft:oklch(0.29 0.011 265);--on-accent:oklch(0.16 0.02 265);
  /* Live and note are small text too (the PENS chip, the 10.5px tags on
     their own tints): lifted until each clears 7:1 on its soft surface. */
  --live:oklch(0.76 0.14 152);--live-soft:oklch(0.28 0.045 152);
  --note:oklch(0.79 0.075 75);--note-soft:oklch(0.29 0.035 75);
  --up:oklch(0.72 0.11 152);--down:oklch(0.68 0.14 25);
  --card-yellow:oklch(0.86 0.17 95);--card-red:oklch(0.64 0.21 27);
  --fantasy:oklch(0.80 0.11 340);
  --shadow:0 1px 2px oklch(0 0 0/.3),0 6px 18px oklch(0 0 0/.28);
  /* Same hue budget as light; the golds shift a little warmer (42→52, 58→65)
     because dark surfaces cool a hue and the pair has to keep reading Spanish. */
  --epl-a:oklch(0.74 0.11 248);--epl-ai:oklch(0.82 0.10 248);--epl-as:oklch(0.29 0.045 248);
  --chm-a:oklch(0.71 0.11 261);--chm-ai:oklch(0.80 0.10 261);--chm-as:oklch(0.29 0.045 261);
  --fac-a:oklch(0.68 0.10 272);--fac-ai:oklch(0.78 0.10 272);--fac-as:oklch(0.29 0.05 272);
  --efl-a:oklch(0.78 0.09 234);--efl-ai:oklch(0.85 0.08 234);--efl-as:oklch(0.29 0.04 234);
  --lal-a:oklch(0.76 0.12 52);--lal-ai:oklch(0.84 0.11 52);--lal-as:oklch(0.30 0.05 52);
  --cdr-a:oklch(0.78 0.10 65);--cdr-ai:oklch(0.85 0.09 65);--cdr-as:oklch(0.30 0.045 65);
  --ucl-a:oklch(0.70 0.11 300);--ucl-ai:oklch(0.79 0.10 300);--ucl-as:oklch(0.29 0.05 300);
  --uel-a:oklch(0.74 0.12 313);--uel-ai:oklch(0.82 0.11 313);--uel-as:oklch(0.29 0.05 313);
  --uecl-a:oklch(0.77 0.10 287);--uecl-ai:oklch(0.84 0.09 287);--uecl-as:oklch(0.29 0.045 287);
  --frd-a:oklch(0.74 0.09 196);--frd-ai:oklch(0.82 0.08 196);--frd-as:oklch(0.29 0.04 196);
  --bul-a:oklch(0.74 0.13 12);--bul-ai:oklch(0.82 0.11 12);--bul-as:oklch(0.30 0.05 12);
  --sea-a:oklch(0.74 0.10 176);--sea-ai:oklch(0.82 0.09 176);--sea-as:oklch(0.29 0.045 176);
  --inf-a:oklch(0.76 0.09 107);--inf-ai:oklch(0.84 0.08 107);--inf-as:oklch(0.29 0.04 107);
  --unl-a:oklch(0.72 0.11 133);--unl-ai:oklch(0.80 0.10 133);--unl-as:oklch(0.29 0.035 133);
  --cnl-a:oklch(0.75 0.10 120);--cnl-ai:oklch(0.83 0.09 120);--cnl-as:oklch(0.29 0.045 120);
  --mls-a:oklch(0.74 0.12 335);--mls-ai:oklch(0.82 0.11 335);--mls-as:oklch(0.30 0.05 335);
  color-scheme:dark;
}
/* ---------- type: IBM Plex, chosen ----------
   Plex Sans for everything, Plex Mono for every number that has to line up.
   The package declared Public Sans and Newsreader above and overrode them
   here, so the cascade carried the decision. Declared once instead: the two
   families are gone from the repo, and a rule that only holds because a later
   rule outranks it is one deletion away from silently changing the type. */
:root{
  --sans:"IBM Plex Sans","Segoe UI",Helvetica,Arial,sans-serif;
  --display:"IBM Plex Sans","Segoe UI",Helvetica,Arial,sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
  --r:3px;--r-lg:3px;
  /* The sticky masthead's height — its 56px row plus the 3px accent rule.
     One value, so what pins under it (the ticker) and what scrolls under it
     (anchor targets) cannot drift apart when the masthead changes. */
  --masthead-h:59px;
}
/* The masthead switch carries both symbols and CSS shows the one the reader is
   moving TO, so the theme is applied before first paint with no script that
   has to find the button and retype it. */
:root[data-theme="light"] .t-light,:root[data-theme="dark"] .t-dark{display:none}

/* ---------- competition accents (the whole per-competition contract) ---------- */
[data-league="epl"]{--accent:var(--epl-a);--accent-ink:var(--epl-ai);--accent-soft:var(--epl-as)}
[data-league="championship"]{--accent:var(--chm-a);--accent-ink:var(--chm-ai);--accent-soft:var(--chm-as)}
[data-league="fa-cup"]{--accent:var(--fac-a);--accent-ink:var(--fac-ai);--accent-soft:var(--fac-as)}
[data-league="efl-cup"]{--accent:var(--efl-a);--accent-ink:var(--efl-ai);--accent-soft:var(--efl-as)}
[data-league="laliga"]{--accent:var(--lal-a);--accent-ink:var(--lal-ai);--accent-soft:var(--lal-as)}
[data-league="copa-del-rey"]{--accent:var(--cdr-a);--accent-ink:var(--cdr-ai);--accent-soft:var(--cdr-as)}
[data-league="ucl"]{--accent:var(--ucl-a);--accent-ink:var(--ucl-ai);--accent-soft:var(--ucl-as)}
[data-league="uel"]{--accent:var(--uel-a);--accent-ink:var(--uel-ai);--accent-soft:var(--uel-as)}
[data-league="uecl"]{--accent:var(--uecl-a);--accent-ink:var(--uecl-ai);--accent-soft:var(--uecl-as)}
[data-league="friendlies"]{--accent:var(--frd-a);--accent-ink:var(--frd-ai);--accent-soft:var(--frd-as)}
[data-league="bundesliga"]{--accent:var(--bul-a);--accent-ink:var(--bul-ai);--accent-soft:var(--bul-as)}
[data-league="serie-a"]{--accent:var(--sea-a);--accent-ink:var(--sea-ai);--accent-soft:var(--sea-as)}
[data-league="international-friendlies"]{--accent:var(--inf-a);--accent-ink:var(--inf-ai);--accent-soft:var(--inf-as)}
[data-league="nations-league"]{--accent:var(--unl-a);--accent-ink:var(--unl-ai);--accent-soft:var(--unl-as)}
[data-league="concacaf-nations-league"]{--accent:var(--cnl-a);--accent-ink:var(--cnl-ai);--accent-soft:var(--cnl-as)}
[data-league="mls"]{--accent:var(--mls-a);--accent-ink:var(--mls-ai);--accent-soft:var(--mls-as)}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--sans);font-size:15px;line-height:1.5;text-wrap:pretty;-webkit-font-smoothing:antialiased}
a{color:var(--accent-ink);text-decoration:none}
a:hover{color:var(--ink);text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px}
h1,h2,h3{font-family:var(--display);font-weight:500;letter-spacing:-.015em;margin:0;line-height:1.15}
.wrap{max-width:1180px;margin:0 auto;padding:0 20px}
.wrap-tight{max-width:900px;margin:0 auto;padding:0 20px}
.dim{color:var(--dim)}
.mono{font-family:var(--mono);font-variant-numeric:tabular-nums}
.tnum{font-variant-numeric:tabular-nums}
.rule{border:0;border-top:1px solid var(--line);margin:0}
.caps{font-size:11px;letter-spacing:.09em;text-transform:uppercase;font-weight:600;color:var(--accent-ink);font-family:var(--sans)}
/* The counterpart of .hide-sm (mobile block below): a shorter form of a fact
   that only phones see. A fact may be shortened for the small screen, never
   deleted — .hide-sm is only for detail that has another home on the page. */
.sm-only{display:none}

/* ---------- header + nav ---------- */
.site-hd{position:sticky;top:0;z-index:40;background:color-mix(in oklab,var(--bg) 88%,transparent);backdrop-filter:blur(10px);border-bottom:3px solid var(--accent)}
.site-hd .row{display:flex;align-items:center;gap:22px;height:56px;position:relative}
.brand{display:flex;align-items:baseline;gap:8px;font-family:var(--display);font-size:19px;font-weight:600;color:var(--ink);letter-spacing:-.02em}
.brand em{font-style:normal;color:var(--accent)}
/* The sister site's link, beside the brand: italic and in its own plum,
   so it reads as a door out rather than one more competition. Pulled a
   little closer to the brand than the row's gap would set it. */
.fantasy{font-family:var(--display);font-style:italic;font-size:15px;font-weight:500;letter-spacing:-.01em;color:var(--fantasy);padding:5px 9px;margin-left:-10px;border-radius:var(--r);white-space:nowrap}
.fantasy:hover{color:var(--fantasy);background:color-mix(in oklab,var(--fantasy) 11%,transparent);text-decoration:none}
.nav{display:flex;gap:4px;margin-left:auto;align-items:center}
.nav a,.nav summary{padding:6px 11px;border-radius:var(--r);font-size:13.5px;font-weight:500;color:var(--ink-2);white-space:nowrap}
.nav a:hover{background:var(--surface);text-decoration:none;color:var(--ink)}
.nav a[aria-current]{background:var(--accent-soft);color:var(--accent-ink)}
/* The competitions menu: the strip names only where the reader is, and the
   rest of the roster waits behind this one <details> — open/close is the
   browser's, no script needed on the way in. */
.nav summary{list-style:none;cursor:pointer;-webkit-user-select:none;user-select:none}
.nav summary::-webkit-details-marker{display:none}
.nav summary::after{content:"▾";margin-left:6px;font-size:9px;color:var(--dim)}
.nav summary:hover{background:var(--surface);color:var(--ink)}
.nav details[open]>summary{background:var(--surface);color:var(--ink)}
.nav details[open]>summary::after{content:"▴"}
/* The panel hangs from the masthead row itself, below the 3px accent rule, so
   the translucent header never clips it; its columns are the hub board's. The
   side offsets mirror .wrap's 20px padding, keeping it on the content edge. */
/* The height cap: the panel hangs from the sticky masthead, so page scroll
   can never reach rows a short viewport cuts off — it must scroll itself.
   On the base rule because the short viewport is a LANDSCAPE phone, wider
   than any phone-width media guard. */
.menu{position:absolute;top:calc(100% + 3px);right:20px;z-index:1;display:flex;gap:8px 26px;padding:14px 16px 16px;background:var(--bg);border:1px solid var(--line-2);border-radius:var(--r);box-shadow:var(--shadow);max-height:calc(100dvh - 110px);overflow-y:auto;overscroll-behavior:contain}
.menu>div{display:flex;flex-direction:column;gap:2px;min-width:12ch}
/* The labels' horizontal padding tracks the links' under them — the
   breakpoints below re-pad both together, keeping the columns flush. */
.menu .caps{padding:2px 11px 5px}
.menu .rule{margin:5px 0}
.crumb{display:flex;gap:8px;align-items:center;font-size:13px;color:var(--dim);padding:14px 0 0}

/* ---------- site search (search.js) ---------- */
/* Hidden — not removed — until search.js stamps it ready, so the row keeps
   its room and nothing shifts when the script lands; a page with no
   JavaScript shows no box that answers nothing. */
.search{position:relative;visibility:hidden}
.search.ready{visibility:visible}
.search input{appearance:none;font:inherit;font-size:13.5px;width:15ch;padding:6px 10px;border:1px solid var(--line-2);border-radius:var(--r);background:var(--surface);color:var(--ink)}
.search input::placeholder{color:var(--dim)}
.search input:focus{outline:2px solid var(--accent);outline-offset:-1px}
/* Narrow from a tablet down: the longest competition name, a signed-in
   email and this box share one row until it wraps at 800px. */
@media (max-width:960px){.search input{width:9ch;padding:6px 8px}}
/* The dropdown hangs from the box the way the menu hangs from the row: at
   least the box's width, at most what the viewport leaves. */
.results{position:absolute;top:calc(100% + 6px);left:0;z-index:2;min-width:100%;width:max-content;max-width:min(440px,calc(100vw - 40px));display:flex;flex-direction:column;padding:5px;background:var(--bg);border:1px solid var(--line-2);border-radius:var(--r);box-shadow:var(--shadow)}
.results[hidden]{display:none}
/* The results page's list: the same rows in a panel, as many as a hundred. */
.search-list{display:flex;flex-direction:column;padding:5px;background:var(--raise);border:1px solid var(--line-2);border-radius:var(--r-lg)}
.search-list:empty{display:none}
/* One result row, in the dropdown and on the page alike. */
.hit{display:flex;align-items:baseline;gap:8px;min-width:0;padding:7px 10px;border-radius:var(--r);font-size:13.5px;color:var(--ink);white-space:nowrap}
.hit:hover,.hit[aria-selected="true"]{background:var(--surface);text-decoration:none}
/* Flex items refuse to shrink below their content unless told (min-width:0), so the name and the other name give way to the ellipsis and the hint keeps its place. */
.hit .nm,.hit .alt{min-width:0;overflow:hidden;text-overflow:ellipsis}
.hit .alt{color:var(--dim);font-size:12.5px}
.hit .hint{margin-left:auto;padding-left:10px;color:var(--dim);font-size:12px;flex:none}

/* ---------- who is signed in (account.js sets data-auth on <html>) ---------- */
/* Everything auth-aware is visibility, decided here. `.auth-only` never shows
   until Access has vouched for the reader, so a signed-out page — and a page
   whose script failed — is exactly the public page. The real locks are Access
   path policies at the edge; these rules are only the furniture. */
/* min-width — overflow:hidden zeroes the flex minimum, which would make this
   the one item an over-full row takes its whole deficit from, crushing a
   signed-in email to a letter. A 9ch floor keeps it readable while still
   letting it shrink first, so the row never pushes the page sideways; the
   max-width bounds a long address. */
.acct{min-width:9ch;padding:6px 11px;border-radius:var(--r);font-size:12.5px;font-weight:600;color:var(--ink-2);border:1px solid var(--line-2);max-width:24ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.acct:hover{background:var(--surface);text-decoration:none;color:var(--ink)}
/* Signed in, the link holds the address and its initial; a desktop shows
   the address, a phone (below) only the initial. */
.acct .me{display:none}
/* The theme switch: one symbol, the same box as the account link beside it. */
.theme{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;margin-left:10px;border-radius:var(--r);border:1px solid var(--line-2);background:transparent;color:var(--ink-2);cursor:pointer;flex:none}
.theme:hover{background:var(--surface);color:var(--ink)}
.theme span{display:flex}
html:not([data-auth="in"]) .auth-only{display:none!important}
html:not([data-tier="trusted"]) .trusted-only{display:none!important}
html:not([data-tier="member"]):not([data-tier="trusted"]) .member-only{display:none!important}
html:not([data-fantasy="in"]) .fantasy-only{display:none!important}
html:not([data-operator="in"]) .operator-only{display:none!important}
html[data-auth="in"] .signed-out{display:none!important}
/* Notes only a URL fragment reveals: the sign-in page's "that area needs
   advanced access" note (the edge's deny URL carries #denied), its "signed
   out" note (account.js's own log-out lands on #out), and the feedback
   page's no-script acknowledgement (the collector's 303 carries #sent).
   Nobody arriving by the front door ever sees any of them. */
.denied-note,.sent-note,.out-note{display:none;margin:14px 0 0;padding:12px 16px;border:1px solid var(--line-2);border-left:3px solid var(--accent);border-radius:var(--r);max-width:52ch}
.denied-note:target,.sent-note:target,.out-note:target{display:block}

/* ---------- live ticker (sticky, league + match pages) ---------- */
/* Sits directly under the masthead (--masthead-h). Under 700px the masthead
   wraps to two lines and has no fixed height, so the ticker stops being
   sticky rather than guessing an offset — on a phone it scrolls away, and
   the same scores are in the fixture list below. */
.ticker{position:sticky;top:var(--masthead-h);z-index:30;background:color-mix(in oklab,var(--bg) 92%,transparent);backdrop-filter:blur(10px);border-bottom:1px solid var(--line);overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}
.ticker::-webkit-scrollbar{display:none}
.ticker .row{display:flex;gap:0;align-items:stretch;min-height:42px}
.tick{display:flex;gap:10px;align-items:center;padding:0 15px;border-right:1px solid var(--line);white-space:nowrap;font-size:13px;color:var(--ink)}
.tick:first-child{border-left:1px solid var(--line)}
.tick:hover{background:var(--raise);text-decoration:none}
.tick b{font-weight:600;font-variant-numeric:tabular-nums}
/* The minute is the live colour only while a match IS live. Before kickoff it
   holds the kickoff time and after it the result, and neither is live — so dim
   is the default and live the exception, rather than three rules where two say
   the same thing. `is-live` is one of the two classes live.js toggles on any
   [data-match], so a match going live re-colours without a rebuild. */
.tick .min{font-family:var(--mono);font-size:11px;color:var(--dim);font-weight:600}
.tick.is-live .min{color:var(--live)}
.tick .min.pens{color:var(--live)}
.tick .won{font-weight:600}

/* ---------- live pip ---------- */
.pip{width:7px;height:7px;border-radius:50%;background:var(--live);flex:none;animation:pip 2s ease-in-out infinite}
@keyframes pip{0%,100%{opacity:1}50%{opacity:.35}}
@media (prefers-reduced-motion:reduce){.pip{animation:none}}

/* ---------- live now: the in-play card grid ---------- */
/* A class rather than an inline style because the phone cap below has to
   override it. `.live-more` is the door out of a capped grid; it shows only
   inside `.live-cap` (the home page) on phones, where the cap applies. */
.live-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px}
.live-more{display:none;padding:10px 2px 0;font-size:13px}

/* ---------- results crosstable: the season as one image ---------- */
.xt i.d{background:color-mix(in oklab,var(--ink) 24%,transparent);color:var(--ink)}
.xt i.l{background:color-mix(in oklab,var(--down) 78%,transparent)}
.xt i.n{background:var(--surface);color:var(--dim)}

/* ---------- week selector ---------- */
.wk{display:flex;align-items:center;gap:6px}

/* ---------- league season page: the matchday accordion ---------- */
.mds{border:1px solid var(--line-2);border-radius:var(--r-lg);background:var(--raise);overflow:hidden}
.md+.md,.md+.md-mon{border-top:1px solid var(--line)}
/* The month kickers are the calendar's rhythm: a caps label with its own
   hairline wherever the month turns, so the season's shape — December's
   congestion — survives forty near-identical rows. */
.md-mon{display:flex;align-items:center;gap:12px;padding:14px 14px 5px;color:var(--dim)}
.md-mon::after{content:"";flex:1;height:1px;background:var(--line)}
.md>summary{display:grid;grid-template-columns:13ch auto 1fr auto 1.1em;gap:14px;align-items:baseline;padding:10px 14px;cursor:pointer;list-style:none;-webkit-user-select:none;user-select:none}
.md>summary::-webkit-details-marker{display:none}
.md>summary:hover{background:var(--surface)}
.md>summary:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.md-l{grid-column:1;font-size:13.5px;font-weight:600;color:var(--ink);white-space:nowrap}
.md-when{grid-column:2;font-family:var(--mono);font-size:11.5px;color:var(--dim);font-variant-numeric:tabular-nums;white-space:nowrap}
.md-tag{grid-column:3;justify-self:start;align-self:center;font-size:10px;letter-spacing:.08em;text-transform:uppercase;font-weight:600;color:var(--accent-ink);background:var(--accent-soft);border-radius:99px;padding:2px 8px;white-space:nowrap}
.md-ct{grid-column:4;font-family:var(--mono);font-size:11px;color:var(--dim);font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap}
.md>summary::after{content:"▾";grid-column:5;font-size:9px;color:var(--dim);text-align:right;align-self:center}
.md[open]>summary::after{content:"▴"}
.md[open]>summary{background:var(--surface)}
.md-body{padding:4px 14px 18px;background:var(--surface);border-top:1px solid var(--line)}
/* The past recedes whole: label to the second ink, count faded with it. */
.md.past>summary .md-l{color:var(--ink-2);font-weight:500}
.md.past>summary .md-ct{opacity:.6}
/* The row a reader means by now: an accent keyline — swapped for the live
   colour, the fixture rows' own keyline grammar, while any of it is in play,
   as is the "N in play" half of the count. */
.md.now>summary{box-shadow:inset 3px 0 0 var(--accent)}
.md.live>summary{box-shadow:inset 3px 0 0 var(--live)}
.md-ct .md-live{color:var(--live);font-weight:600}
/* An anchored row clears the masthead, the possible ticker and a breath.
   Before the client strip arrives, on an empty day, or under 700px where it
   is not sticky, the row lands lower than necessary — the safe side. */
.md{scroll-margin-top:calc(var(--masthead-h) + 53px)}
@media (max-width:640px){
  .md>summary{grid-template-columns:1fr auto auto 1.1em;row-gap:2px;padding:9px 12px}
  .md-l{grid-column:1}
  .md-tag{grid-column:2;justify-self:end}
  .md-ct{grid-column:3}
  .md>summary::after{grid-column:4}
  .md-when{grid-column:1/-1;grid-row:2}
  .md-body{padding:2px 10px 14px}
}

/* ---------- imagery placeholders (real material drops in later) ---------- */

/* ---------- home: quiet, large, mostly empty ---------- */
.hero{padding-top:70px;padding-bottom:54px}
.hero h1{font-size:clamp(38px,6.2vw,80px);line-height:.98;letter-spacing:-.045em;max-width:19ch}
.hero p{font-size:clamp(15px,1.6vw,19px);color:var(--ink-2);max-width:44ch;margin:22px 0 0;line-height:1.45}
/* The competition board: a column per country, cards touching (2px — the
   family), columns apart (18px — the border). Column heights are honest:
   England is simply taller than Spain. */
.big{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:26px 18px;align-items:start}
.grp{display:flex;flex-direction:column;gap:2px}
/* The rule between a column's sections — with room either side, so an
   empty upper section still reads as a section and not a double underline. */
.grp .rule{margin:10px 0}
.ghd{display:flex;align-items:center;gap:9px;padding-bottom:8px}
.ghd::before{content:"";width:26px;height:2px;background:var(--accent)}
.big a{display:flex;flex-direction:column;justify-content:space-between;min-height:150px;padding:18px 20px;color:var(--on-accent);background:var(--accent);text-decoration:none}
.big a:hover{text-decoration:none;filter:brightness(1.06)}
.big a .n{font-family:var(--display);font-size:clamp(20px,2.3vw,27px);letter-spacing:-.025em;line-height:1.05;font-weight:600}
/* Meta opacity is a measured number, not a taste: at .82 the 11.5px line on
   the lightest accents sat under the 4.5:1 small-text floor; .9 clears it on
   every filled card in both themes, and .85 does the same for the record
   cards' ink-on-soft line below. The rule for a new accent is the test, not
   a lightness ceiling — OKLCH L is not comparable across hues (a gold at
   0.54 outshines a blue at 0.55): measure white-on-accent at .9 and clear
   4.5:1. Today's tightest cases are La Liga 4.53 and EFL Cup 4.59. */
.big a .m{font-family:var(--mono);font-size:11.5px;opacity:.9;padding-top:9px}
.big a .b{display:flex;justify-content:space-between;align-items:flex-end;font-size:13px;opacity:.92;gap:10px;padding-top:14px}
/* A season that is only a record: the same doorway, kept quiet. */
.big a.rec{flex-direction:row;align-items:center;justify-content:space-between;gap:12px;min-height:0;padding:12px 20px;background:var(--accent-soft);color:var(--accent-ink)}
.big a.rec:hover{filter:none;box-shadow:inset 0 0 0 1px var(--accent)}
.big a.rec .n{font-size:15.5px;letter-spacing:-.01em}
.big a.rec .m{opacity:.85;padding-top:3px;font-size:10.5px}
.soon{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:2px}
.soon>*{border:1px solid var(--line);padding:16px;min-height:118px;display:flex;flex-direction:column;gap:6px;color:var(--ink)}
.soon>a:hover{text-decoration:none;border-color:var(--accent)}
.soon h3{font-size:16px}
.soon p{margin:0;font-size:12.5px;color:var(--dim);line-height:1.45}
.soon .go{margin-top:auto;padding-top:8px;font-size:13px;color:var(--accent-ink)}
@media (max-width:860px){.big{grid-template-columns:1fr}.hero{padding-top:44px;padding-bottom:34px}}

/* ---------- league accent, made visible ---------- */
.accrule{height:9px;background:var(--accent);width:100%}

/* ---------- section header ---------- */
.sec{display:flex;align-items:center;gap:14px;padding:calc(30px*var(--dens)) 0 calc(12px*var(--dens))}
.sec h2{font-size:26px;letter-spacing:-.035em}
.sec .aside{margin-left:auto;font-size:13px;color:var(--dim)}

/* ---------- round pages: the chain's nav, meta line, and foot pager ---------- */
/* Generic prev/next links share the section header (orientation, at the top);
   the named doorway cards sit at the foot of the tie list, where there is
   room for names. */
.rnav,.daynav{display:flex;flex-wrap:wrap;align-items:center;gap:4px 12px;font-size:13px}
.rnav{margin-left:auto}
/* The day pages' prev/next/calendar row, under the hero. */
.daynav{margin-top:14px}
.rnav a,.daynav a{color:var(--accent-ink);white-space:nowrap}
.rnav i,.daynav i{width:1px;height:12px;background:var(--line-2)}
/* The calendar: month grids, a cell a doorway when its day has football. */
.cal{display:grid;grid-template-columns:repeat(auto-fill,minmax(270px,1fr));gap:28px 32px;padding:24px 0 30px}
.cal-g{display:grid;grid-template-columns:repeat(7,1fr);gap:3px}
.cal-g .h{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--dim);text-align:center;padding-bottom:4px;font-family:var(--sans)}
.cal-g a,.cal-g .d{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;aspect-ratio:1;border-radius:var(--r);font-size:13px;min-width:0}
.cal-g .d{color:var(--dim)}
.cal-g a{background:var(--raise);border:1px solid var(--line-2);color:var(--ink)}
.cal-g a:hover{text-decoration:none;border-color:var(--accent)}
.cal-g a b{font-weight:600}
.cal-g .n{font-family:var(--mono);font-size:10.5px;color:var(--dim)}
.cal-g .today{box-shadow:inset 0 0 0 2px var(--accent)}
.sec.rsec{flex-wrap:wrap;row-gap:4px}
/* Heading ink wherever a round link appears — a .sec h2, or leading a
   fold summary, where following the link must not read as toggling. */
a.rlink{color:inherit}
a.rlink:hover{color:var(--accent-ink);text-decoration:none}
.rmeta{font-family:var(--mono);font-size:11px;color:var(--dim);letter-spacing:.01em;padding:2px 0 16px 2px}
.rpager{display:grid;grid-template-columns:1fr 1fr;gap:2px;margin-top:28px}
.rpager a{display:flex;flex-direction:column;gap:5px;border:1px solid var(--line-2);border-radius:var(--r-lg);background:var(--raise);padding:14px 16px;min-width:0}
.rpager a:hover{text-decoration:none;border-color:var(--accent)}
.rpager .next{text-align:right;align-items:flex-end;grid-column:2}
.rpager .lbl{font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;font-weight:600;color:var(--dim)}
.rpager a:hover .lbl{color:var(--accent-ink)}
.rpager .nm{font-size:15.5px;font-weight:500;color:var(--ink);letter-spacing:-.01em}
.rpager .meta{font-family:var(--mono);font-size:11px;color:var(--dim)}
@media (max-width:640px){.rpager{grid-template-columns:1fr}.rpager .next{grid-column:1;text-align:left;align-items:flex-start}}
/* The nav may wrap (each link kept whole, above) because prev + next + the
   ↑ back-out link outgrow a phone's width together. Under the masthead's own
   wrap point it also takes its own line, left-aligned, so the date aside and
   the links never fight over one row. */
@media (max-width:700px){.rnav{flex-basis:100%;margin-left:0;justify-content:flex-start}}

/* ---------- already through (season ladder, undrawn rungs) ---------- */
/* A fifth line of the structure panel, hidden until asked: who is already
   through this rung, and how. Native <details>; the open body sits behind a
   2px keyline so its caps header does not flatten into the panel's own. */
.qual{border-top:1px solid var(--line);margin-top:12px}
.qual summary{list-style:none;cursor:pointer;-webkit-user-select:none;user-select:none;padding:11px 0 1px;font-size:13.5px;color:var(--ink-2)}
.qual summary::-webkit-details-marker{display:none}
.qual summary:hover .qual-more{color:var(--ink)}
.qual summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:var(--r)}
.qual-more{color:var(--dim)}
.qual-more::after{content:"▸";display:inline-block;margin-left:5px;font-size:9px;vertical-align:1px}
.qual[open] .qual-more::after{content:"▾"}
.qual-body{padding:2px 0 3px 14px;margin:12px 0 2px;border-left:2px solid var(--line)}
.qual-h .qual-n{font-family:var(--mono);text-transform:none;letter-spacing:.01em;font-weight:400;font-size:11px;color:var(--dim);margin-left:6px}
/* Known clubs are bare marks in a wrapping row — the same club mark as
   everywhere else — each with its mono how-note beside it. */
.qual-marks{display:flex;flex-wrap:wrap;gap:8px 18px;padding-top:8px}
.qual-club{display:inline-flex;align-items:center;gap:9px;font-size:13px;color:var(--ink)}
.qual-club .how{font-family:var(--mono);font-size:11px;color:var(--dim);white-space:nowrap}
.qual-note{margin:8px 0 0;font-size:12.5px;color:var(--dim);max-width:62ch;line-height:1.45}
/* A phone gives up less width: the inset shrinks, the keyline stays. */
@media (max-width:480px){.qual-body{padding-left:8px}}

/* ---------- club identity: colour bar + name; crest at the trusted tier ---------- */
/* max-width — inline-flex sizes to fit-content, whose floor is the full
   nowrap name; without the cap a long name overruns a narrow grid cell and
   the .nm ellipsis below never engages. */
.club{display:inline-flex;align-items:center;gap:9px;min-width:0;max-width:100%}
/* Linked, the mark keeps its ink — the bar is the colour — and takes the
   site's hover underline. */
a.club{color:inherit}
/* Each bar carries BOTH of its club's colours and CSS picks the one that reads
   on the current surface, because the choice has to survive the reader
   flipping the theme — and the only script this site ships runs once, before
   paint. A club with no colours on record falls back to the rule colour: the
   bar is identity, so it is better absent-looking than borrowed from another
   club. */
.club .bar{width:4px;height:15px;border-radius:1px;flex:none;box-shadow:inset 0 0 0 1px oklch(0.5 0 0/.22);background:var(--bar-l,var(--line-2))}
:root[data-theme="dark"] .club .bar{background:var(--bar-d,var(--line-2))}
.club .nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The crest joins the mark for Trusted readers only. It ships unarmed —
   data-crest, no src, so the public never requests the Access-policed path —
   and account.js arms it once the tier is vouched; :not([src]) keeps the
   unarmed box out of the row until then. A missing file 404s and the img is
   removed: the mark is bar + name again. */
.crest{width:18px;height:18px;flex:none;object-fit:contain}
.crest:not([src]){display:none}
/* On a dark surface a navy crest melts into the row, so dark theme alone sits
   each one on a small near-white chip — the same move the bar's ring makes
   for a light bar on a light surface. */
:root[data-theme="dark"] .crest{background:oklch(0.95 0.005 265/.92);border-radius:3px;padding:2px}
.mhead .club .crest{width:32px;height:32px}
:root[data-theme="dark"] .mhead .club .crest{border-radius:4px;padding:3px}

/* ---------- fixture rows ---------- */
.fx{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--raise);overflow:hidden;border-color:var(--line-2)}
.fx-row{display:grid;grid-template-columns:96px 1fr 62px 1fr 26px;align-items:center;gap:12px;padding:calc(10px*var(--dens)) 14px;border-top:1px solid var(--line);color:var(--ink)}
.fx-row:first-child{border-top:0}
.fx-row:hover{background:var(--surface);text-decoration:none}
/* Grid items keep min-width:auto, so a 1fr club track would refuse to shrink
   below the full nowrap name — the row then overflows the card and .fx's
   overflow:hidden clips names mid-glyph instead of ellipsizing them. */
.fx-row>span{min-width:0}
.fx-when{font-family:var(--mono);font-size:11.5px;color:var(--dim);line-height:1.35}
.fx-when b{display:block;color:var(--ink-2);font-weight:600;font-size:12px}
/* Flex-end inside a stretched item, not justify-self:end — an end-aligned
   grid item takes its fit-content width, which floors at the full nowrap
   name and overflows LEFT across the kickoff cell. Stretched, the club caps
   at the track and the home name ellipsizes like the away name does. */
.fx-h{display:flex;justify-content:flex-end}.fx-h .club{flex-direction:row-reverse}
.fx-sc{text-align:center;font-variant-numeric:tabular-nums;font-weight:600;font-size:16px;letter-spacing:.01em}
.fx-sc .v{color:var(--dim);font-weight:400;font-size:13px}
.fx-st{font-size:10.5px;letter-spacing:.05em;text-transform:uppercase;font-weight:600;color:var(--dim);text-align:center;display:block;margin-top:1px}
.fx-st[hidden]{display:none}
/* The shootout's kicks under the status word: the live green, the PENS
   chip's colour, because they are the verdict a drawn score cannot give. The
   drawn score itself mutes under them. */
.fx-st.pens{color:var(--live)}
.fx-sc .drawn{color:var(--dim);font-weight:500}
/* The winner's name, bold: whoever scored more, or on a second leg whoever
   the aggregate (or the kicks) sent through. A draw and a first leg bold
   nobody. The class sits on the side's wrapper so live.js can toggle it
   without knowing how a club mark is built. */
.fx-row .won .nm{font-weight:600}
.fx-row.is-live{background:color-mix(in oklab,var(--live) 6%,transparent);box-shadow:inset 3px 0 0 var(--live)}
.fx-row.is-done{background:color-mix(in oklab,var(--note) 5%,var(--surface))}
.fx-row.is-done .fx-sc{color:var(--ink-2)}
.fx-row.is-live .fx-sc{color:var(--live)}
/* A player page's day on the bench: the row and its line under it, dimmed. */
.fx-row.bench,.fx-row.bench+.prov{opacity:.55}
.fx-row.is-live .fx-st{color:var(--live)}
.fx-go{color:var(--dim);text-align:right;font-size:14px}
.fx-row:hover .fx-go{color:var(--accent)}
.fx-note{grid-column:1/-1;padding-top:6px}

/* ---------- Football Today: the folded sections (Earlier today, The rest of the day) ---------- */
/* The day's concluded matches, collapsed above Live now. The summary IS a
   standard section header — same .sec, same accent tick — so closed it reads
   as one more section of the page, not a widget; open, it is the ordinary
   finished fixture rows. The hero's "N finished" links here, so the target
   clears the sticky masthead. */
.etoday{scroll-margin-top:calc(var(--masthead-h) + 11px)}
/* The floor is already cleared by the .sec padding inside; stated so the tap
   target cannot quietly regress — on phones the caret is the one affordance. */
.etoday>summary{list-style:none;cursor:pointer;-webkit-user-select:none;user-select:none;min-height:44px}
.etoday>summary::-webkit-details-marker{display:none}
/* The final scores while closed: a dim mono digest the open state retires by
   visibility, not display — the count must not jump sideways. Ellipsis, never
   a wrap; the padding keeps the trim off the count. */
.etoday .digest{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--mono);font-size:12px;color:var(--dim);padding-right:10px}
.etoday[open] .digest{visibility:hidden}
/* The nav menu's own caret: the one disclosure mark the site already has. */
.etoday .aside{white-space:nowrap}
.etoday .aside::after{content:"▾";margin-left:7px;font-size:9px;color:var(--dim)}
.etoday[open] .aside::after{content:"▴"}
.etoday>summary:hover h2{color:var(--accent-ink)}
.etoday>summary:hover .aside{color:var(--ink)}
.etoday>summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:var(--r)}

/* ---------- played-day folds (the front page's current round or matchday) ---------- */
/* A day whose every row is settled and comfortably past collapses to one
   strip — day name, count, state — so the page opens at what is live or next.
   <details>, no script; view.day_sections decides which days fold. Strips
   touch at 2px (the hub board's cards-touching move) so the played block
   reads as one quiet unit; an open fold takes back its breathing room. */
.dayfold{margin:0 0 2px}
.dayfold[open]{margin:10px 0 18px}
.dayfold summary{list-style:none;-webkit-user-select:none;user-select:none;cursor:pointer;display:flex;align-items:baseline;gap:14px;padding:10px 14px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface)}
.dayfold summary::-webkit-details-marker{display:none}
.dayfold summary:hover{background:var(--raise);border-color:var(--line-2)}
.dayfold summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* Played days are quiet: neutral ink where the open day headings keep the accent. */
.dayfold summary .caps{color:var(--ink-2)}
.dayfold .sum{font-family:var(--mono);font-size:11px;color:var(--dim)}
.dayfold .notes{font-family:var(--mono);font-size:11px;color:var(--note);display:inline-flex;align-items:center;gap:5px}
.dayfold .notes::before{content:"";width:5px;height:5px;border-radius:50%;background:var(--note)}
.dayfold summary::after{content:"▸";margin-left:auto;font-size:10px;color:var(--dim);align-self:center}
.dayfold[open] summary{border-radius:var(--r-lg) var(--r-lg) 0 0;border-bottom:0}
.dayfold[open] summary::after{content:"▾"}
.dayfold[open]>.fx{border-radius:0 0 var(--r-lg) var(--r-lg)}
/* On a wide page the day names column-align the counts, newspaper-style. */
@media (min-width:861px){.dayfold summary .caps{min-width:172px}}
/* The season ladder's tie folds (season.html tie_fold): the same strip,
   under a rung's heading and prose instead of between day groups. Open, the
   strip keeps its whole border — what unfolds is day-grouped sections, not
   the joined .fx card the front page's fold sits flush on — and the first
   day heading gets its room. */
.rfold:not([open]){margin:2px 0 0}
.dayfold.rfold[open] summary{border-radius:var(--r-lg);border-bottom:1px solid var(--line);margin-bottom:14px}
/* Today's heading: the caps label plus its marker and what is still to come.
   The strip's count and state live in one string, so no width hides one. */
.dayhead{display:flex;align-items:baseline;gap:10px;padding:16px 0 8px 2px;flex-wrap:wrap}
.dayhead .live-note{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;font-weight:600;color:var(--live)}
.dayhead .todo{font-size:11.5px;color:var(--dim)}

/* ---------- disagreement and data notes ---------- */
.dis{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;color:var(--note);border:1px solid color-mix(in oklab,var(--note) 40%,var(--line));background:var(--note-soft);border-radius:99px;padding:1px 9px 2px;cursor:pointer;font-family:var(--sans)}
.dis::before{content:"";width:5px;height:5px;border-radius:50%;background:var(--note)}
.match-issues{display:flex;gap:7px;flex-wrap:wrap;padding:0 0 4px;position:relative;z-index:3}
.match-issue{position:relative}
.match-issue summary{display:inline-flex;align-items:center;gap:6px;list-style:none;cursor:pointer;font-size:11.5px;color:var(--dim);border:1px solid var(--line-2);background:var(--surface);border-radius:99px;padding:2px 9px 3px}
.match-issue summary::-webkit-details-marker{display:none}
.match-issue summary::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor}
.match-issue.important summary{color:var(--note);border-color:color-mix(in oklab,var(--note) 40%,var(--line));background:var(--note-soft)}
.match-issue summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.match-issue-detail{display:none;position:absolute;z-index:5;top:calc(100% + 6px);left:0;width:max-content;max-width:min(380px,calc(100vw - 36px));padding:10px 12px;border:1px solid var(--line-2);border-radius:var(--r);background:var(--raise);box-shadow:var(--shadow);font-size:12.5px;line-height:1.45;color:var(--ink-2)}
.match-issue-detail a{white-space:nowrap;color:var(--accent-ink)}
.match-issue[open] .match-issue-detail,.match-issue:not([open]):hover .match-issue-detail,.match-issue:focus-within .match-issue-detail{display:block}
.match-issue:not([open]):hover::details-content,.match-issue:focus-within::details-content{content-visibility:visible}

/* ---------- league table ---------- */
.tbl{width:100%;border-collapse:collapse;font-size:13.5px}
.tbl th{text-align:right;font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--accent-ink);font-weight:600;padding:0 6px calc(7px*var(--dens));border-bottom:1px solid var(--line-2);white-space:nowrap}
.tbl th.l{text-align:left}
.tbl td{text-align:right;padding:calc(7px*var(--dens)) 6px;border-bottom:1px solid var(--line);font-variant-numeric:tabular-nums;color:var(--ink-2)}
.tbl td.l{text-align:left;color:var(--ink)}
.tbl td.pts{font-weight:700;color:var(--ink)}
.tbl tbody tr:hover td{background:var(--surface)}
.tbl .pos{width:30px;color:var(--dim);position:relative;padding-left:12px}
/* Zones: a left keyline, not a fill — club colour must never carry meaning.
   Three bands, matching edition.ZONE_KINDS: the top one takes the accent, the
   second a mixed accent, relegation the down colour. There is no European band
   until somebody settles how many places the season carries. */
tr.z-up .pos::before,tr.z-playoff .pos::before,tr.z-down .pos::before{content:"";position:absolute;left:0;top:-1px;bottom:-1px;width:3px}
tr.z-up .pos::before{background:var(--accent)}
tr.z-playoff .pos::before{background:color-mix(in oklab,var(--accent) 65%,var(--line))}
tr.z-down .pos::before{background:var(--down)}
.legend{display:flex;flex-wrap:wrap;gap:6px 16px;font-size:11.5px;color:var(--dim);padding:10px 0 0}
.legend span{display:flex;align-items:center;gap:6px}
.zkey{width:3px;height:12px;border-radius:1px;flex:none;background:var(--line-2)}
.zkey.z-up{background:var(--accent)}
.zkey.z-playoff{background:color-mix(in oklab,var(--accent) 65%,var(--line))}
.zkey.z-down{background:var(--down)}

/* ---------- probability language (three treatments, one is chosen) ---------- */
.p{display:inline-flex;align-items:center;gap:7px;font-size:12px;color:var(--ink-2)}
.p .n{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:12px;min-width:33px;text-align:right}
.pbar{position:relative;height:5px;border-radius:99px;background:color-mix(in oklab,var(--ink) 10%,transparent);flex:1;min-width:26px;overflow:hidden}
.pbar>i{position:absolute;inset:0 auto 0 0;background:var(--accent);border-radius:99px}
.pbar.mut>i{background:color-mix(in oklab,var(--ink) 38%,transparent)}
/* The odds identities, defined once (issue #582): home and away wear the
   clubs' own colours — the marks' --home-l/d and --away-l/d pairs, the same
   registry colours with the same per-surface fallback the club bar wears —
   and the accent palette the split always had stands in where a club has
   none. The 1X2 split and the odds-movement chart both read these two
   tokens, so the split above the chart and the lines inside it cannot drift
   apart. The draw stays neutral ink at two deliberate strengths: the
   split's fill carries its own inline 26%, and --line-d (45%) is the
   chart's alone — the one token here the split never reads. Colour is
   identity only and never alone — the labels carry the names, the split
   keeps the club bar's inset ring (a pale kit stays visible, two similar
   kits keep an edge), and on the chart the draw is dashed and the away
   line dotted, so even two same-family kits stay tellable apart. */
.p-split,.oddschart,.keyline,.chartbox{
  --line-h:var(--home-l,var(--accent));
  --line-d:color-mix(in oklab,var(--ink) 45%,transparent);
  --line-a:var(--away-l,color-mix(in oklab,var(--accent) 42%,var(--line)));
}
:root[data-theme="dark"] .p-split,:root[data-theme="dark"] .oddschart,:root[data-theme="dark"] .keyline,:root[data-theme="dark"] .chartbox{
  --line-h:var(--home-d,var(--accent));
  --line-a:var(--away-d,color-mix(in oklab,var(--accent) 42%,var(--line)));
}
.p-split{display:flex;height:5px;border-radius:99px;overflow:hidden;flex:1;min-width:80px;gap:1px}
.p-split i{display:block;height:100%;box-shadow:inset 0 0 0 1px oklch(0.5 0 0/.22)}
.p-split i:nth-child(1){background:var(--line-h)}
.p-split i:nth-child(2){background:color-mix(in oklab,var(--ink) 26%,transparent)}
.p-split i:nth-child(3){background:var(--line-a)}

/* ---------- the odds-movement chart (match page) ----------
   The split's identity tokens, as lines: solid home, dashed draw, dotted
   away, each key swatch repeating its line's style. Strokes hold their
   width under preserveAspectRatio=none via vector-effect, and the dash
   patterns ride with it: non-scaling-stroke computes the whole stroke in
   the host coordinate space, so the stretch never lengthens a dot. */
.oddschart{display:block}
.oddschart .mid{stroke:var(--line);stroke-width:1;vector-effect:non-scaling-stroke}
.oddschart polyline{fill:none;stroke-width:2;stroke-linejoin:round;stroke-linecap:round;vector-effect:non-scaling-stroke}
.oddschart .ln-h{stroke:var(--line-h)}
.oddschart .ln-d{stroke:var(--line-d);stroke-dasharray:5 4}
.oddschart .ln-a{stroke:var(--line-a);stroke-dasharray:1 4}
.keyline{display:inline-block;width:15px;border-top:2px solid;border-radius:99px;vertical-align:middle}
.keyline.ln-h{border-color:var(--line-h)}
.keyline.ln-d{border-color:var(--line-d);border-top-style:dashed}
.keyline.ln-a{border-color:var(--line-a);border-top-style:dotted}

/* The chartbox is the overlays' coordinate frame — exactly the SVG's box.
   Markers (kickoff / low / high / final-or-now on the home line) are each a
   zero-size anchor pinned to the exact line point: the dot centres on the
   point so it sits ON the polyline, and only the label floats off it —
   worldcup's rule, which is what keeps dots from drifting off the line.
   The label sits above its dot, aligns inward at the box's edges and
   ducks under the dot at the top edge — placement the view spells in each
   marker's cls, since only it knows where the points are. Phones read the
   key row instead (hide-sm). The crosshair and its readout appear
   together while the pointer is on the chart; both are placed by the
   script in the same box percents the view printed. */
.chartbox{position:relative}
.chart-marks{position:absolute;inset:0;pointer-events:none}
.cm{position:absolute}
.cm-dot{position:absolute;left:0;top:0;width:7px;height:7px;margin:-3.5px 0 0 -3.5px;border-radius:50%;background:var(--line-h);box-shadow:0 0 0 2px var(--raise)}
.cm-lab{position:absolute;left:0;bottom:7px;transform:translateX(-50%);text-align:center;line-height:1.05;white-space:nowrap}
.cm-lab b{font-size:12px}
.cm-cap{display:block;margin-top:1px;font-size:9px;text-transform:uppercase;letter-spacing:.05em;color:var(--dim)}
.cm-left .cm-lab{transform:translateX(0);text-align:left}
.cm-right .cm-lab{transform:translateX(-100%);text-align:right}
.cm-under .cm-lab{bottom:auto;top:7px}
.cross{position:absolute;top:0;bottom:0;border-left:1px solid var(--line-2);pointer-events:none}
.cross,.chart-tip{visibility:hidden}
.chartbox.hov .cross,.chartbox.hov .chart-tip{visibility:visible}
.chart-tip{position:absolute;top:4px;display:flex;gap:10px;align-items:baseline;padding:3px 9px;background:var(--raise);border:1px solid var(--line-2);border-radius:var(--r);font-size:12.5px;white-space:nowrap;pointer-events:none;box-shadow:var(--shadow)}
.chart-tip [data-tip-t]{color:var(--dim);font-weight:600}

/* ---------- staleness: the honest dimming ----------
   All four hooks are written by live.js and nothing else. A surface whose
   data-fresh-for threshold has passed without a render confirmation is dimmed
   (.is-stale) with a quiet .stale-note under it; the banner is the
   cross-cutting strip under the masthead when nothing is updating at all; the
   live pair dims exactly the scores the stopped fast lane owns. Amber (--note)
   throughout: staleness is the same species of news as a dispute — worth a
   glance, not an alarm. */
[data-fresh-for].is-stale{opacity:.6}
.stale-note{padding:8px 2px 0;color:var(--note)}
.stale-banner{border-bottom:1px solid color-mix(in oklab,var(--note) 40%,var(--line));background:var(--note-soft);color:var(--note);font-size:12.5px;text-align:center;padding:7px 20px}
[data-live-stale] .is-live [data-live-score],
[data-live-stale] .is-live [data-live-status]{opacity:.55}
.tick-stale{display:flex;align-items:center;padding:0 15px;font-family:var(--mono);font-size:11px;font-weight:600;color:var(--note);white-space:nowrap}

/* ---------- panels, notes, provenance ---------- */
.panel{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--raise);padding:calc(16px*var(--dens)) 18px}
.panel.flat{background:var(--surface)}
.prov{font-family:var(--mono);font-size:11px;color:var(--dim);letter-spacing:.01em}

/* The clock note that opens into the reader's clock choice (tz.js). The
   summary IS the old "all times London time" footnote; the panel drops
   below it the way the nav menu drops from the masthead. Until tz.js marks
   the details `ready` the summary is a plain note — no caret, no cursor —
   so a page without JavaScript never opens a select that does nothing.
   `right` hangs the panel from the note's right edge, for the league
   header's right column. */
.tzpick{position:relative;display:inline-block}
.tzpick summary{list-style:none}
.tzpick summary::-webkit-details-marker{display:none}
.tzpick:not(.ready) .tz-panel{display:none}
/* An armed note is a control, so no text selection; unarmed it is a plain
   footnote and stays selectable. */
.tzpick.ready summary{cursor:pointer;-webkit-user-select:none;user-select:none}
.tzpick.ready summary::after{content:"▾";margin-left:5px;font-size:9px;color:var(--dim)}
.tzpick.ready[open]>summary::after{content:"▴"}
.tzpick.ready summary:hover{color:var(--ink-2)}
.tz-panel{position:absolute;left:0;top:calc(100% + 6px);z-index:2;display:flex;flex-direction:column;gap:10px;padding:14px 16px;background:var(--bg);border:1px solid var(--line-2);border-radius:var(--r);box-shadow:var(--shadow);min-width:230px;text-align:left}
.tzpick.right .tz-panel{left:auto;right:0}
.tz-field{display:flex;flex-direction:column;gap:8px}
.tz-panel select{font:inherit;font-size:13px;padding:6px 8px;border:1px solid var(--line-2);border-radius:var(--r);background:var(--surface);color:var(--ink);max-width:250px}
.tz-check{display:flex;gap:8px;align-items:center;font-size:12.5px;color:var(--ink-2);cursor:pointer;white-space:nowrap}
/* The per-time zone mark ("BST", "EDT") tz.js hangs on each kickoff. */
.tzb{margin-left:6px;font-size:9.5px;font-weight:500;color:var(--dim);letter-spacing:.02em}
.kpi{display:flex;flex-direction:column;gap:3px}
.kpi b{font-family:var(--display);font-size:27px;font-weight:500;letter-spacing:-.02em;line-height:1}
.grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:14px}

/* ---------- doorways (home) ---------- */
.door{display:block;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--raise);padding:18px;color:var(--ink);box-shadow:var(--shadow)}
.door:hover{text-decoration:none;border-color:var(--accent);transform:translateY(-1px)}
.door{transition:transform .12s ease,border-color .12s ease}
.door h3{font-size:18px}
.door .top{display:flex;align-items:center;gap:9px;margin-bottom:12px}
.door .top .dot{width:9px;height:9px;border-radius:2px;background:var(--accent);flex:none}

/* ---------- tabs (secondary views on a league page) ---------- */
/* Links, not buttons: a tab is a page here, so it can be linked to, opened in
   a new tab and gone back out of. Same look, and the selector takes both so
   the specimen page in the design package still renders. */
.tabs{display:flex;gap:2px;border-bottom:1px solid var(--line);margin-bottom:0}
.tabs a,.tabs button{appearance:none;background:none;border:0;border-bottom:2px solid transparent;padding:9px 13px;font:inherit;font-size:13.5px;color:var(--dim);cursor:pointer;font-weight:500}
.tabs a:hover,.tabs button:hover{color:var(--ink);text-decoration:none}
.tabs a[aria-selected="true"],.tabs button[aria-selected="true"]{color:var(--ink);border-bottom-color:var(--accent)}

/* ---------- the player page's totals picker ---------- */
/* Radios and labels, no script: the checked input's sibling label reads as
   the chosen segment and its sibling pane shows. The inputs stay in the tab
   order (hidden by opacity, never display:none) so the arrow keys move the
   choice, and the focused one outlines its label. A panel with one scope
   draws no segments and its pane simply shows. */
.scopes{position:relative}
.scopes>input{position:absolute;opacity:0;width:0;height:0;margin:0}
.seg{display:flex;flex-wrap:wrap;gap:4px;margin-bottom:14px}
/* flex:1 so a wrapped row still fills the rail — four segments in 264px is
   two rows, and a lone "Club + country" on its own row reads as a tab. */
.seg label{flex:1 1 auto;text-align:center;font-size:11.5px;font-weight:600;padding:5px 8px;border-radius:var(--r);border:1px solid var(--line-2);color:var(--ink-2);cursor:pointer;white-space:nowrap;line-height:1.2}
.seg label:hover{color:var(--ink);border-color:var(--ink-2)}
.seg~.scope{display:none}
#scope-league:checked~.seg label[for=scope-league],#scope-club:checked~.seg label[for=scope-club],#scope-country:checked~.seg label[for=scope-country],#scope-both:checked~.seg label[for=scope-both]{background:var(--ink);border-color:var(--ink);color:var(--bg)}
#scope-league:focus-visible~.seg label[for=scope-league],#scope-club:focus-visible~.seg label[for=scope-club],#scope-country:focus-visible~.seg label[for=scope-country],#scope-both:focus-visible~.seg label[for=scope-both]{outline:2px solid var(--accent);outline-offset:2px}
#scope-league:checked~.scope[data-scope=league],#scope-club:checked~.scope[data-scope=club],#scope-country:checked~.scope[data-scope=country],#scope-both:checked~.scope[data-scope=both]{display:block}

/* ---------- match page ---------- */
.mhead{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:20px;padding:26px 0}
.mhead .side{display:flex;flex-direction:column;gap:8px;min-width:0}
.mhead .side.away{align-items:flex-end;text-align:right}
.mhead .side h2{font-size:24px}
.mhead .sc{font-family:var(--display);font-size:52px;font-weight:500;letter-spacing:-.03em;line-height:1;font-variant-numeric:tabular-nums;text-align:center;white-space:nowrap}
.mhead .sc .dec{display:block;font-family:var(--sans);font-size:11px;letter-spacing:.02em;text-transform:none;font-weight:500;color:var(--ink-2);margin-top:5px}
.mhead .sc small{display:block;font-family:var(--sans);font-size:11px;letter-spacing:.08em;text-transform:uppercase;font-weight:600;color:var(--dim);margin-top:9px}
.mhead.is-live .sc{color:var(--live)}.mhead.is-live .sc small{color:var(--live)}
/* The match's date, under the score in every state. After the is-live rule at
   equal specificity on purpose: the minute glows live-green, the date never
   does — it is not a number the match moves. */
.mhead .sc small.mdate{margin-top:5px;color:var(--dim);text-transform:none;letter-spacing:.02em;white-space:normal}
.aggregate-context{font-family:var(--sans);font-size:12.5px;font-weight:initial;line-height:normal;margin-top:9px;display:flex;flex-direction:column;align-items:center;gap:8px;white-space:normal}
.aggregate-chip{display:inline-flex;align-items:baseline;gap:8px;background:var(--accent-soft);border:1px solid var(--line);border-radius:2px;padding:4px 12px;color:var(--ink)}
.aggregate-chip>span{letter-spacing:normal}
.aggregate-chip .aggregate-label{font-size:10.5px}
.aggregate-chip .aggregate-score{font-family:var(--mono);font-size:14.5px;font-weight:600}
.aggregate-chip .aggregate-claim{font-family:var(--sans);font-size:12.5px;font-weight:600;color:var(--ink)}
/* A level aggregate is context, not a verdict: muted numbers, a dim "level",
   never a winner — the PENS chip below is the only element that names one. */
.aggregate-chip.is-level .aggregate-score{color:var(--ink-2)}
.aggregate-chip.is-level .aggregate-claim{color:var(--dim)}
/* The shootout verdict: same vocabulary as AGG, stronger — live green, the
   kicks home-first, the winner by name. letter-spacing/line-height reset here
   because the standalone chip sits directly inside .sc's display type. */
.pens-chip{display:inline-flex;align-items:baseline;gap:8px;background:var(--live-soft);border:1px solid color-mix(in oklab,var(--live) 32%,var(--line));border-radius:var(--r);padding:5px 13px 6px;color:var(--ink);white-space:nowrap;letter-spacing:normal;line-height:normal}
.pens-chip[hidden]{display:none}
.pens-chip .pens-label{color:var(--live)}
.pens-chip .pens-score{font-family:var(--mono);font-size:16px;font-weight:600;color:var(--live);font-variant-numeric:tabular-nums}
.pens-chip .pens-claim{font-family:var(--sans);font-size:14.5px;font-weight:600;color:var(--ink)}
.pens-chip .pens-claim:empty{display:none}
/* Standing alone under the score (no aggregate stack), the chip leaves the
   52px display type's line box — as a block-level flex its margin works and
   the strut cannot hold phantom space above it. :not([hidden]) so this
   display cannot resurrect a hidden chip. */
.mhead .sc>.pens-chip:not([hidden]){display:flex;width:fit-content;margin:9px auto 0}
/* The drawn ninety-minute score under a shootout verdict: muted, so the
   green chip is the strongest element in the centre column. */
.mhead .sc .drawn{color:var(--ink-2)}
/* Overruled: an authored ruling re-decided the tie, so the kicks stay on
   record but the chip stops being the verdict — the live green drains to
   the drawn family, and the claim is already empty (match_view.py owns both). */
.pens-chip.is-overruled{background:transparent;border-color:var(--line-2)}
.pens-chip.is-overruled .pens-label{color:var(--dim)}
.pens-chip.is-overruled .pens-score{color:var(--ink-2)}
/* The ruling banner: the PENS chip's geometry in the note amber — the
   semantic family for "a decision, read it", deliberately neither the live
   green (nothing is being won) nor an alarm (nothing is broken). */
.ruling-chip{display:inline-flex;align-items:baseline;gap:8px;background:var(--note-soft);border:1px solid color-mix(in oklab,var(--note) 45%,var(--line));border-radius:var(--r);padding:5px 13px 6px;color:var(--ink);letter-spacing:normal;line-height:normal}
.ruling-chip .ruling-label{color:var(--note)}
.ruling-chip .ruling-claim{font-family:var(--sans);font-size:14.5px;font-weight:600;color:var(--ink)}
.mhead .sc>.ruling-chip{display:flex;width:fit-content;margin:9px auto 0}
/* The editor's note is a person's words: the same amber holds its edge,
   telling it apart from the automated research box it can sit beside. */
.editors-note{border-left:3px solid color-mix(in oklab,var(--note) 60%,var(--line))}
.first-leg-link{display:block;font-family:var(--mono);font-size:11.5px;font-weight:400;letter-spacing:normal;color:var(--dim)}
/* The "Elsewhere in the matchday" rows: one link across time, names and
   score, so hovering rings the row (the record card's ring) rather than
   underlining all three. */
.sib{display:flex;gap:8px;align-items:baseline;font-size:13px;color:var(--ink);padding:3px 8px;margin:0 -8px;border-radius:var(--r)}
.sib:hover{text-decoration:none;box-shadow:inset 0 0 0 1px var(--accent)}
.mscorers{display:grid;grid-template-columns:1fr 1fr;gap:3px 26px;margin-top:-14px;padding-bottom:14px;font-size:12.5px;color:var(--dim)}
.mscorers .ms-side.away{text-align:right}
.tl{display:flex;flex-direction:column}
.tl-i{display:grid;grid-template-columns:50px 20px 1fr;gap:12px;align-items:start;padding:9px 0;border-top:1px solid var(--line)}
.tl-i:first-child{border-top:0}
.tl-min{font-family:var(--mono);font-size:12px;color:var(--dim);text-align:right;font-variant-numeric:tabular-nums;padding-top:1px}
.tl-ic{font-size:13px;text-align:center;color:var(--ink-2)}
.tl-b{min-width:0}
.tl-b .who{font-weight:600}
.tl-b .meta{font-size:12px;color:var(--dim)}
.tl-b .tl-said{font-size:11.5px;color:var(--ink-2);margin-top:2px}
.lu{display:grid;grid-template-columns:1fr 1fr;gap:18px 26px}
.lu-head{font-weight:600;padding-bottom:8px;display:flex;align-items:baseline;gap:8px}
.lu-form{font-family:var(--mono);font-size:11.5px;color:var(--dim);font-weight:400}
.lu-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:5px}
.lu-list li{display:flex;gap:8px;align-items:baseline;font-size:13px;min-width:0}
.lu-jr{min-width:20px;text-align:right;font-size:11.5px;color:var(--dim);flex:none}
.lu-nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lu-ps{font-size:11px;color:var(--dim);flex:none}
.lu-lbl{padding:12px 0 7px;color:var(--dim)}
.tl-i.struck .tl-b .who{text-decoration:line-through;color:var(--dim);font-weight:400}
.tl-i.unsettled{background:var(--note-soft)}
/* Event hierarchy (#583): the icon stops being the only differentiator.
   Goals step forward — a heavier, larger scorer name and a dark semibold
   minute, so the minute rail alone reads as a map of the score. Subs and
   yellows recede to secondary ink; the card glyphs wear a real card's
   vivid yellow and red (--card-yellow, --card-red — never the muted
   attention pair, never a competition's hue); a red card keeps the
   full-weight name because it changes the match. A disallowed goal is
   excluded from the step-up — struck already says it does not count —
   while an unsettled one keeps it, the amber row and its tag carrying
   the doubt. */
.tl-i.ev-goal:not(.struck) .who{font-weight:700;font-size:16px;letter-spacing:-.01em}
.tl-i.ev-goal:not(.struck) .tl-min{color:var(--ink);font-weight:600}
.tl-i.ev-sub .who,.tl-i.ev-yellow .who{font-weight:400;color:var(--ink-2)}
.tl-i.ev-sub .tl-ic{color:var(--dim)}
.tl-i.ev-yellow .tl-ic{color:var(--card-yellow)}
.tl-i.ev-red .tl-ic{color:var(--card-red)}
.tag{display:inline-block;font-size:10.5px;letter-spacing:.05em;text-transform:uppercase;font-weight:600;border-radius:99px;padding:1px 7px;border:1px solid var(--line-2);color:var(--dim)}
.tag.note{color:var(--note);border-color:color-mix(in oklab,var(--note) 45%,var(--line));background:var(--note-soft)}
.tag.live{color:var(--live);border-color:color-mix(in oklab,var(--live) 45%,var(--line));background:var(--live-soft)}
/* An outbound clip pill on a timeline row: the tag's shape, a link's manners. */
.tl-clip{display:inline-block;font-size:10px;border-radius:99px;padding:1px 8px;margin-left:7px;border:1px solid var(--line-2);color:var(--dim);text-decoration:none;line-height:1.6}
.tl-clip:hover{color:var(--note);border-color:color-mix(in oklab,var(--note) 45%,var(--line));background:var(--note-soft)}

/* ---------- the data report card (match/<id>/data/) ---------- */
/* One row per data type, one column per feed. Diagnostic furniture: compact,
   honest, no bars — the partial state borrows the dispute amber because a gap
   in a feed is the same species of news as a disagreement between two. */
.dr{border-collapse:collapse;width:100%;font-size:12.5px}
.dr th,.dr td{padding:5px 12px 5px 0;text-align:left;border-top:1px solid var(--line);vertical-align:top;white-space:nowrap}
.dr thead th{border-top:0;font-family:var(--sans);font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;font-weight:600;color:var(--dim)}
.dr tbody th{font-family:var(--sans);font-weight:600;color:var(--ink-2)}
.dr td{font-family:var(--mono);font-size:12px;color:var(--ink)}
.dr td.dr-absent{color:var(--dim)}
.dr td.dr-partial{color:var(--note)}

/* ---------- countdown (launch state) ---------- */
.cd{display:flex;gap:22px;align-items:baseline}
.cd div{display:flex;flex-direction:column}
.cd b{font-family:var(--display);font-size:38px;font-weight:500;line-height:1;font-variant-numeric:tabular-nums}
.cd span{font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--dim);font-weight:600;margin-top:5px}

/* ---------- footer ---------- */
.site-ft{border-top:1px solid var(--line);margin-top:56px;padding:26px 0 40px;font-size:12.5px;color:var(--dim)}
.site-ft .cols{display:flex;gap:38px;flex-wrap:wrap}
.site-ft a{color:var(--ink-2)}

/* ---------- the feedback crayon note ----------
   A hand-drawn red-crayon "Feedback!" annotation that rides the content
   column's lower-right while the reader scrolls (position:sticky, no script)
   and parks above the footer at page end, arrow landing on the footer's own
   Feedback link — which these rules lift to the footer's top-right corner
   and ring in the same crayon, so the annotation points at the real thing.
   Markup: _feedback_note.html, included once by base.html. Deleting the
   treatment = that include line, the template, and this block.
   The crayon red is a deliberate one-off outside the token palette (hue-25
   red belongs semantically to --down): scoped here, never reused. */
.fb-perch,.site-ft a[href="/feedback/"]{--fb-red:oklch(0.55 0.21 28)}
:root[data-theme="dark"] .fb-perch,:root[data-theme="dark"] .site-ft a[href="/feedback/"]{--fb-red:oklch(0.73 0.17 25)}
.fb-perch{position:sticky;bottom:12px;z-index:35;height:164px;width:100%;max-width:1180px;margin:0 auto -84px;pointer-events:none}
/* shown only while the reader's allowance lasts (the script in the note's
   own template); without it the footer's ringed link is the whole ask */
html:not([data-fb-note]) .fb-perch{display:none}
.fb-note{position:absolute;right:22px;bottom:0;display:block;pointer-events:none;transform:rotate(-5deg);transform-origin:70% 85%}
.fb-note:focus-visible{outline:2px solid var(--fb-red);outline-offset:4px;border-radius:6px}
.fb-note svg{display:block;width:200px;height:auto;overflow:visible}
/* only the ink takes clicks — the pads hug the word and the small line, so
   the box's empty corners never steal a tap from a fixture row beneath */
.fb-note use{fill:none;stroke-linecap:round;stroke-linejoin:round;pointer-events:auto}
.fb-pad{fill:none;pointer-events:fill}
/* three passes per stroke: a paper halo so the word stays legible over
   fixture rows, a wide soft pass (wax build-up), then the crayon line */
.fb-halo{stroke:var(--bg);stroke-width:8.5;opacity:.92}
.fb-soft{stroke:var(--fb-red);stroke-width:5.2;opacity:.30}
.fb-main{stroke:var(--fb-red);stroke-width:3.2;opacity:.95}
.fb-halo.fb-l2{stroke-width:6}
.fb-soft.fb-l2{stroke-width:3.6}
.fb-main.fb-l2{stroke-width:2.1}
.fb-note use.fb-copy{font-family:var(--mono);font-size:10.5px;font-weight:600;letter-spacing:.01em}
.fb-note use.fb-copy.fb-halo{fill:var(--bg);stroke:var(--bg);stroke-width:3.6;opacity:.92}
.fb-note use.fb-copy.fb-soft{fill:var(--fb-red);stroke:var(--fb-red);stroke-width:1.4;opacity:.3}
.fb-note use.fb-copy.fb-main{fill:var(--fb-red);stroke:var(--fb-red);stroke-width:.2;opacity:.95}
/* the parked arrow's true target: the footer's Feedback link, lifted to the
   footer's top-right and ringed in crayon. The ring is a mask coloured by
   --fb-red, so the dark theme needs no second drawing; the cols reserve the
   corner so no column ever slides under the ring. */
.site-ft .wrap{position:relative}
.site-ft .cols{padding-right:130px}
.site-ft a[href="/feedback/"]{position:absolute;top:4px;right:36px;padding:10px 16px 12px;font-family:var(--mono);font-size:11px;letter-spacing:.01em}
.site-ft a[href="/feedback/"]::after{content:"";position:absolute;inset:0;background:var(--fb-red);opacity:.9;
  --fb-ring:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 126 52'%3E%3Cfilter id='c' x='-40%25' y='-40%25' width='180%25' height='180%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.09' numOctaves='2' seed='7' result='w'/%3E%3CfeDisplacementMap in='SourceGraphic' in2='w' scale='3'/%3E%3C/filter%3E%3Cg filter='url(%23c)' fill='none' stroke='%23000' stroke-linecap='round'%3E%3Cpath d='M30 10C55 3 100 4 115 15C125 24 117 38 92 45C64 51 18 49 8 37C0 27 14 12 44 8C62 5 84 6 98 10' stroke-width='3.2'/%3E%3Cpath d='M34 13C58 7 98 8 111 18C119 26 111 37 89 43C63 48 22 46 12 35C6 27 20 15 46 11' stroke-width='1.9' opacity='.5'/%3E%3C/g%3E%3C/svg%3E");
  -webkit-mask:var(--fb-ring) center/100% 100% no-repeat;mask:var(--fb-ring) center/100% 100% no-repeat}
@media (max-width:700px){
  /* parked-only on a phone — relative is sticky minus the following-around,
     so the note never hovers over a fixture card's score column */
  .fb-perch{position:relative;height:104px}
  .fb-note{right:14px}
  .fb-note svg{width:124px}
  /* at this width the sub-caption's letterforms fall under 8px — ink that
     small reads as smudge, so the word and the arrow carry it alone */
  .fb-note .fb-l2{display:none}
  /* the pill keeps its corner: the columns must not slide under the ring */
  .site-ft .cols{padding-right:110px}
  .site-ft a[href="/feedback/"]{right:2px}
}

/* ---------- mobile: 360px is the design target ---------- */
@media (max-width:860px){
  /* minmax(0,1fr), not 1fr — plain 1fr floors at the column's min-content,
     letting one nowrap fixture row widen the whole collapsed page. */
  .grid-3,.grid-2,.kf-split,.lu{grid-template-columns:minmax(0,1fr)!important}
  .site-hd .row{height:52px;gap:12px}
  .fantasy{margin-left:-4px;font-size:14px;padding:5px 7px}
  .ticker{top:55px}
  .nav{gap:0}.nav a,.nav summary{padding:6px 8px;font-size:13px}
  .menu .caps{padding:2px 8px 5px}
  .acct{max-width:16ch;font-size:12px;padding:6px 8px}
  .fx-row{grid-template-columns:58px 1fr 48px 1fr;gap:8px;padding:9px 10px}
  .fx-go{display:none}
  .fx-when{font-size:11px}
  .club .nm{font-size:13.5px}
  .mhead{grid-template-columns:1fr auto 1fr;gap:10px;padding:18px 0}
  .mhead .side h2{font-size:16px}
  .mhead .sc{font-size:38px}
  /* The centre column's auto track floors at min-content, and a long club
     name in the verdict chip must not widen the whole page: on a phone the
     claim may wrap onto a second chip line, mid-name included. The ruling
     chip's verdict prose earns the same wrap. */
  .pens-chip,.ruling-chip{white-space:normal;flex-wrap:wrap;justify-content:center}
  .tbl{font-size:12.5px}
  .tbl td,.tbl th{padding:6px 4px}
  .hide-sm{display:none!important}
  .sm-only{display:block}
  /* The competition label under the kickoff time, on the mixed lists (hub,
     Football Today) — the one place a phone reader cannot otherwise tell a
     friendly from a qualifier. Wraps in the 58px column; the taller row is
     the price of keeping the fact. */
  .fx-comp{font-size:10px;line-height:1.3}
  /* Clip pills sit up to three in a run; a thumb needs ≥28px of target. */
  .tl-clip{font-size:12px;padding:4px 12px;margin-left:10px}
  .etoday .digest{display:none}
  /* The home page's live wall, capped: six cards and the door to the rest. */
  .live-cap .live-grid>a:nth-child(n+7){display:none}
  .live-cap .live-more{display:block}
}

/* Under ~800px the brand, the search box, the strip, a signed-in email and
   the theme switch stop fitting on one line — 800 rather than 640 because
   the longest competition names (Conference League) plus the box and an
   email at its .acct floor fill a 760px row — so the nav drops to its own
   line and wraps if it must — never scrolls: a scrolling nav would clip the
   menu panel, whose containing block is the row above it. */
@media (max-width:800px){
  .site-hd .row{flex-wrap:wrap;height:auto;padding-top:9px;padding-bottom:7px;gap:8px}
  .brand{margin-right:auto;font-size:16px}
  /* When the Fantasy link is shown it takes the brand's auto margin, so
     the pair stays together on the left instead of the link drifting to
     the account controls. */
  html[data-fantasy="in"] .brand{margin-right:0}
  .fantasy{margin-right:auto;font-size:13px;padding:5px 6px}
  /* Signed in, the account link is a round chip holding the reader's
     initial — the address it had room for here was "alex@b…", which told
     nobody anything — so the brand, the Fantasy link, the search box, the
     chip and the theme switch fit one line down to about 355px: every
     current iPhone at 100%. Signed out it still reads "Log in". */
  .acct{flex:none;min-width:0}
  html[data-auth="in"] .acct{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border-radius:50%;font-size:13px}
  html[data-auth="in"] .acct .who{display:none}
  html[data-auth="in"] .acct .me{display:inline}
  /* auto, not 0: on the rare row too narrow for all five (under ~355px —
     a first-generation SE, or a phone zoomed a step) the switch wraps to
     its own line and sits at the right edge of it, not the left. Guarded,
     because on a wider row the same auto margin would eat the slack that
     otherwise keeps the switch next to the chip. */
  @media (max-width:400px){.theme{margin-left:auto}}
  /* The box takes what the first line has left between the brand and the
     account controls, and the dropdown spans the row under the strip. */
  .search{position:static;flex:4 1 3ch;min-width:0;max-width:24ch}
  .search input,.search input:focus{width:100%}
  .search input::-webkit-search-cancel-button{display:none}
  .results{left:20px;right:20px;width:auto;min-width:0;max-width:none}
  .nav{order:3;width:100%;margin-left:0;gap:0;flex-wrap:wrap}
  .nav a,.nav details{flex:none}
  .nav a,.nav summary{padding:6px 7px}
  .menu .caps{padding:2px 7px 5px}
  /* The panel spans the content edge to edge; its columns wrap. Its height
     cap lives on the base rule: a landscape phone is wider than any width
     guard here, and the cap is about height. */
  .menu{left:20px;flex-wrap:wrap}
  /* Section headers wrap: heading + link on the first line — .wk must not
     shrink to one-word lines — and every .sec aside (date ranges, counts)
     drops to its own line under them. */
  .sec{flex-wrap:wrap}
  .sec .wk{white-space:nowrap;flex:none}
  .sec .aside{flex-basis:100%;order:6;margin-left:0}
  /* The masthead has no fixed height once it wraps, so nothing can be pinned
     under it. */
  .ticker{position:static}
}

/* Accent-forward furniture the base rules do not touch. The accent is the
   only thing a league may change, so putting it on the section marks is what
   makes the three competitions feel different from one another. */
.sec::after{content:"";flex:1;height:2px;background:var(--accent);order:-1;max-width:26px}
