:root {
  color-scheme: light dark;
  --bg: #0f1115;
  --surface: #171a21;
  --surface-2: #1e222b;
  --line: #2a2f3a;
  --text: #e6e9ef;
  --muted: #939cad;
  --accent: #4ade80;
  --home: #5b9cf8;
  --away: #f0883e;
  --draw: #6b7280;
  --radius: 10px;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}
/* The light palette, defined once and applied from two places: the system
   preference when the viewer has not chosen, and an explicit choice when they
   have. The :not() guard is what lets "dark" win on a machine set to light —
   without it the media query would keep overriding the button. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg: #f6f7f9; --surface: #fff; --surface-2: #f0f2f5; --line: #e2e5ea;
    --text: #16181d; --muted: #626b7b; --accent: #16a34a;
    --home: #2563eb; --away: #d97706;
  }
}
:root[data-theme="light"] {
  color-scheme: light;
  --bg: #f6f7f9; --surface: #fff; --surface-2: #f0f2f5; --line: #e2e5ea;
  --text: #16181d; --muted: #626b7b; --accent: #16a34a;
  --home: #2563eb; --away: #d97706;
}
:root[data-theme="dark"] { color-scheme: dark; }

* { box-sizing: border-box; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}
a { color: inherit; }

/* ---------- chrome ----------
   Two tiers. The sport is the outer choice and sits on top; the sections
   belong to the chosen sport and sit under it, indented into the same column
   the page content starts in, so the hierarchy reads without a label. */
.topbar {
  border-bottom: 1px solid var(--line);
  background: var(--surface); position: sticky; top: 0; z-index: 10;
}
.tier { display: flex; align-items: center; gap: 28px; padding: 0 24px; }
.tier-sports { padding-top: 10px; padding-bottom: 2px; }
.tier-sections {
  padding-bottom: 0; gap: 0;
  border-top: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
  margin-top: 8px;
}
/* Sora, self-hosted and cut down to the eleven characters the wordmark uses:
   b e t a i d r . c o m. The full latin subset Google serves is 25KB; this is
   2KB, and it is the whole reason the brand can have its own face at all —
   a webfont round-trip to fonts.gstatic.com in front of every page would be a
   poor trade on a box that is meant to run offline, and a hard failure on one
   that actually is. The variable weight axis survives the subset, so 400 and
   650 both come out of this one file.

   Sora is SIL Open Font License 1.1 (github.com/soratypeface/Sora). */
@font-face {
  font-family: "Sora Brand";
  src: url("fonts/sora-brand.woff2") format("woff2");
  font-weight: 400 800;
  font-style: normal;
  font-display: swap;
  /* Nothing outside the wordmark should ever reach for this face — it has no
     glyphs for anything else and would fall back mid-word. */
  unicode-range: U+002E, U+0061-0065, U+0069, U+006D-006F, U+0072, U+0074;
}

/* The lockup. The mark takes the accent, the word takes the text colour, and
   the domain trails behind it in muted — the same weight split the identity
   sheet used, so the header and the favicon read as one thing. */
.brand {
  display: inline-flex; align-items: center; gap: 9px;
  text-decoration: none; white-space: nowrap;
}
.brand-mark { color: var(--accent); flex: 0 0 auto; display: block; }
.brand-word {
  font-family: "Sora Brand", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  font-weight: 650; letter-spacing: -0.03em; font-size: 16.5px; color: var(--text);
}
.brand-dom { font-weight: 400; color: var(--muted); letter-spacing: -0.015em; }
.brand:hover .brand-mark { filter: brightness(1.12); }
@media (max-width: 560px) { .brand-dom { display: none; } }

/* Top tier: the sports. Pill-shaped and icon-led, so the current sport is
   obvious at a glance and never mistaken for the section row below it. */
.sportnav { display: flex; gap: 6px; flex-wrap: wrap; }
.sporttab {
  display: inline-flex; align-items: center; gap: 7px;
  text-decoration: none; color: var(--muted);
  font-size: 13px; font-weight: 500; line-height: 1;
  padding: 7px 13px; border-radius: 999px;
  border: 1px solid transparent; white-space: nowrap;
  transition: background .12s, color .12s, border-color .12s;
}
.sporttab .ico { font-size: 15px; filter: saturate(.9); }
.sporttab:hover { color: var(--text); background: color-mix(in srgb, var(--line) 45%, transparent); }
.sporttab.on {
  color: var(--text); font-weight: 600;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-color: color-mix(in srgb, var(--accent) 42%, transparent);
}
.sporttab.on .ico { filter: none; }

/* Second tier: the sections of the chosen sport. Underlined rather than
   pilled, so the two rows never look like alternatives to each other. */
.sectionnav { display: flex; gap: 22px; flex-wrap: wrap; }
.sectionnav a {
  text-decoration: none; color: var(--muted); font-size: 13.5px;
  padding: 10px 0; border-bottom: 2px solid transparent; white-space: nowrap;
}
.sectionnav a:hover { color: var(--text); }
.sectionnav a.on { color: var(--text); font-weight: 550; border-bottom-color: var(--accent); }

@media (max-width: 720px) {
  .tier { gap: 14px; padding: 0 14px; overflow-x: auto; scrollbar-width: none; }
  .tier::-webkit-scrollbar { display: none; }
  .tier-sports { flex-wrap: nowrap; }
  .sportnav, .sectionnav { flex-wrap: nowrap; }
  .sectionnav { gap: 16px; }
  .brand { display: none; }
}

main { max-width: 1040px; margin: 0 auto; padding: 28px 24px 60px; }
.page-head { margin-bottom: 22px; }
h1 { font-size: 26px; letter-spacing: -0.02em; margin: 0 0 4px; }
h2 { font-size: 17px; letter-spacing: -0.01em; margin: 32px 0 12px; }
.sub { color: var(--muted); margin: 0; font-size: 14px; }

footer {
  border-top: 1px solid var(--line); padding: 20px 24px; color: var(--muted);
  font-size: 12.5px; max-width: 1040px; margin: 0 auto;
}
footer a { color: var(--muted); }

/* ---------- cards & tables ---------- */
.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 18px; margin-bottom: 18px;
}
table { width: 100%; border-collapse: collapse; font-size: 14px; }
.scroll { overflow-x: auto; }
th, td { padding: 9px 10px; text-align: left; border-bottom: 1px solid var(--line); }
th {
  color: var(--muted); font-weight: 550; font-size: 12px;
  text-transform: uppercase; letter-spacing: 0.04em; white-space: nowrap;
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--surface-2); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; font-family: var(--mono); font-size: 13px; }
td a { text-decoration: none; }
td a:hover { text-decoration: underline; }
.empty { color: var(--muted); font-size: 14px; }

/* ---------- fixture / result rows ---------- */
.fixture { display: grid; grid-template-columns: 92px 1fr 190px; gap: 14px; align-items: center; }
.fixture .when { color: var(--muted); font-size: 12.5px; font-family: var(--mono); }
.teams { font-weight: 550; }
.score {
  font-family: var(--mono); font-weight: 650; background: var(--surface-2);
  padding: 2px 8px; border-radius: 5px; margin: 0 8px;
}
.xg-chip { color: var(--muted); font-size: 12.5px; font-family: var(--mono); }

.probbar {
  display: flex; height: 22px; border-radius: 5px; overflow: hidden;
  background: var(--surface-2); font-size: 11px; font-family: var(--mono);
}
.probbar span { display: flex; align-items: center; justify-content: center; color: #0b0d11; min-width: 0; }
.probbar i { font-style: normal; opacity: 0.9; }
.probbar .ph { background: var(--home); }
.probbar .pd { background: var(--draw); color: #fff; }
.probbar .pa { background: var(--away); }

/* ---------- match page ---------- */
.scoreline {
  display: grid; grid-template-columns: 1fr auto 1fr; gap: 18px; align-items: center;
  padding: 8px 0 18px;
}
.scoreline .side { font-size: 19px; font-weight: 600; }
.scoreline .side.away { text-align: right; }
.scoreline .big { font-family: var(--mono); font-size: 30px; font-weight: 700; letter-spacing: -0.02em; }
.scoreline .meta { color: var(--muted); font-size: 12.5px; text-align: center; }

.statgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
.stat { background: var(--surface-2); border-radius: 8px; padding: 12px 14px; }
.stat .k { color: var(--muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.04em; }
.stat .v { font-family: var(--mono); font-size: 21px; font-weight: 650; margin-top: 3px; }

svg.pitch { width: 100%; height: auto; display: block; }
svg.pitch .turf { fill: var(--surface-2); }
svg.pitch .lines rect, svg.pitch .lines line, svg.pitch .lines circle {
  fill: none; stroke: var(--line); stroke-width: 2;
}
svg.pitch .shot { fill-opacity: 0.14; stroke-width: 2; }
svg.pitch .shot.home { fill: var(--home); stroke: var(--home); }
svg.pitch .shot.away { fill: var(--away); stroke: var(--away); }
svg.pitch .shot.goal { fill-opacity: 0.85; }

svg.chart { width: 100%; height: auto; display: block; }
svg.chart .grid { stroke: var(--line); stroke-width: 1; }
svg.chart .grid.ft { stroke-dasharray: 3 4; }
svg.chart .axis { fill: var(--muted); font-size: 11px; font-family: var(--mono); }
svg.chart .line { fill: none; stroke-width: 2.5; }
svg.chart .line.home { stroke: var(--home); }
svg.chart .line.away { stroke: var(--away); }
svg.chart .goalmark.home { fill: var(--home); }
svg.chart .goalmark.away { fill: var(--away); }

.legend { display: flex; align-items: center; gap: 8px; color: var(--muted); font-size: 13px; margin-top: 10px; }
.legend .key { width: 11px; height: 11px; border-radius: 3px; display: inline-block; margin-left: 10px; }
.legend .key.home { background: var(--home); }
.legend .key.away { background: var(--away); }

.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 820px) {
  .two-col { grid-template-columns: 1fr; }
  .fixture { grid-template-columns: 1fr; gap: 8px; }
  main { padding: 20px 14px 44px; }
}

.badge {
  display: inline-block; font-size: 11px; padding: 2px 7px; border-radius: 999px;
  background: var(--surface-2); color: var(--muted); font-family: var(--mono);
}
.badge.hit { background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent); }
.notice {
  border-left: 3px solid var(--accent); background: var(--surface-2);
  padding: 12px 14px; border-radius: 0 8px 8px 0; font-size: 14px; margin-bottom: 18px;
}
.notice code { font-family: var(--mono); font-size: 12.5px; }

/* ---------- picks controls ---------- */
.controls { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 16px; }
.ctl .lbl {
  display: block; color: var(--muted); font-size: 11.5px; text-transform: uppercase;
  letter-spacing: 0.04em; margin-bottom: 6px;
}
/* Sizing only — colour, border, radius and focus now come from the field
   system at the foot of this file, so the radar cannot drift from the rest. */
.ctl input[type=number], .ctl select { width: 100%; }
.rangewrap { display: flex; align-items: center; gap: 10px; }
.rangewrap input[type=range] { flex: 1; accent-color: var(--accent); }
.rangewrap output { font-family: var(--mono); font-size: 13px; min-width: 38px; text-align: right; }

.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  padding: 7px 12px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--surface-2); font-size: 13px; user-select: none;
}
.chip.on { border-color: var(--accent); color: var(--text); }
.chip input { accent-color: var(--accent); margin: 0; }

button[type=submit] {
  padding: 9px 20px; border-radius: 999px; border: 0; cursor: pointer;
  background: var(--accent); color: #0b0d11; font: inherit; font-weight: 600;
  margin-left: 8px;
}
button[type=submit]:hover { filter: brightness(1.08); }

/* ---------- per-match market browser ---------- */
.markets details {
  border: 1px solid var(--line); border-radius: 8px; margin-bottom: 8px; background: var(--surface-2);
}
.markets summary {
  cursor: pointer; padding: 10px 14px; font-weight: 550; font-size: 14px;
  list-style: none; display: flex; align-items: center; gap: 8px;
}
.markets summary::-webkit-details-marker { display: none; }
.markets summary::before { content: "▸"; color: var(--muted); font-size: 11px; }
.markets details[open] summary::before { content: "▾"; }
.markets details[open] summary { border-bottom: 1px solid var(--line); }
.markets table { background: var(--surface); border-radius: 0 0 8px 8px; }
.markets td, .markets th { padding: 7px 14px; }

.badge.xg { background: color-mix(in srgb, var(--home) 22%, transparent); color: var(--home); }
.badge.plain { background: color-mix(in srgb, var(--away) 18%, transparent); color: var(--away); }

/* ---------- pick of the day ---------- */
.potd {
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--accent) 12%, var(--surface)) 0%, var(--surface) 62%);
  border-color: color-mix(in srgb, var(--accent) 34%, var(--line));
}
.potd-tag {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.09em;
  color: var(--accent); font-weight: 650; margin-bottom: 10px;
}
.potd-body { display: flex; justify-content: space-between; gap: 26px; flex-wrap: wrap; align-items: center; }
.potd-match { font-size: 21px; font-weight: 640; letter-spacing: -0.02em; }
.potd-match a { text-decoration: none; }
.potd-match span { color: var(--muted); font-weight: 400; margin: 0 5px; }
.potd-meta { color: var(--muted); font-size: 12.5px; margin-top: 3px; }
.potd-pick {
  margin-top: 12px; font-size: 17px; font-weight: 600;
  color: var(--accent); letter-spacing: -0.01em;
}
.potd-nums { display: flex; gap: 26px; flex-wrap: wrap; }
.potd-nums > div { display: flex; flex-direction: column; gap: 2px; }
.potd-nums .k { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; }
.potd-nums .v { font-family: var(--mono); font-size: 20px; font-weight: 650; }

.scroll-chips { max-height: 132px; overflow-y: auto; padding: 2px; }
.chip.small { padding: 5px 10px; font-size: 12px; }

/* ---------- crests and marks ---------- */
.crest {
  display: inline-block; vertical-align: middle; object-fit: contain;
  border-radius: 4px; flex: none;
}
.crest-fallback {
  display: inline-flex; align-items: center; justify-content: center;
  color: #fff; font-size: 9px; font-weight: 700; letter-spacing: 0.02em;
  border-radius: 5px; font-family: var(--mono);
}
.teamchip { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
.teamchip > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.compmark {
  display: inline-block; vertical-align: middle; object-fit: contain;
  border-radius: 3px; margin-right: 5px;
}
.vs { color: var(--muted); margin: 0 8px; font-weight: 400; }
.fixture-cell { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }

.sideteam { display: flex; align-items: center; gap: 12px; text-decoration: none; }
.scoreline .side.away .sideteam { flex-direction: row-reverse; }
.teamhead { display: flex; align-items: center; gap: 18px; }
.teamhead-name { font-size: 22px; font-weight: 650; letter-spacing: -0.02em; }
.teamhead-meta { color: var(--muted); font-size: 13px; margin-top: 2px; }

/* ================================================================
   Live streams: the day list, the watch page and the admin panel
   ================================================================ */

/* ---------- shared controls ---------- */
.btn {
  display: inline-flex; align-items: center; gap: 6px; justify-content: center;
  padding: 9px 18px; border-radius: 999px; border: 0; cursor: pointer;
  background: var(--accent); color: #0b0d11; font: inherit; font-weight: 600;
  font-size: 14px; text-decoration: none; margin: 0;
}
.btn:hover { filter: brightness(1.08); }
.btn.ghost {
  background: var(--surface-2); color: var(--text); border: 1px solid var(--line);
  font-weight: 500;
}
.btn.ghost:hover { border-color: var(--muted); filter: none; }
.btn.small { padding: 5px 12px; font-size: 12.5px; }
.btn.on { background: var(--accent); color: #0b0d11; }

/* Fields are defined once, at the foot of this file. Buttons and links keep
   the outline treatment: they have no border of their own to colour. */
.btn:focus-visible, a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
textarea { font-family: var(--mono); line-height: 1.5; resize: vertical; }

.count {
  font-family: var(--mono); font-size: 12px; color: var(--muted);
  background: var(--surface-2); border-radius: 999px; padding: 2px 8px; margin-left: 6px;
  vertical-align: middle;
}
.flash {
  border-radius: 8px; padding: 10px 14px; font-size: 14px; margin-bottom: 12px;
  background: color-mix(in srgb, var(--accent) 16%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
}
.flash.err {
  background: color-mix(in srgb, var(--away) 16%, var(--surface));
  border-color: color-mix(in srgb, var(--away) 45%, transparent);
}

/* ---------- date strip ---------- */
.datestrip {
  display: flex; gap: 6px; align-items: stretch; overflow-x: auto;
  padding-bottom: 4px; margin-bottom: 18px;
  scrollbar-width: none; -ms-overflow-style: none;
}
.datestrip::-webkit-scrollbar { display: none; height: 0; }
.datestrip a {
  flex: none; text-decoration: none; text-align: center; min-width: 74px;
  padding: 8px 12px; border-radius: 9px; border: 1px solid var(--line);
  background: var(--surface); color: var(--muted);
}
.datestrip a:hover { color: var(--text); border-color: var(--muted); }
.datestrip a.on { border-color: var(--accent); color: var(--text); background: var(--surface-2); }
.datestrip .dow { display: block; font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; }
.datestrip .dom { display: block; font-family: var(--mono); font-size: 14px; margin-top: 2px; }
.datestrip .jump { display: flex; align-items: center; margin-left: auto; }

/* ---------- the day's matches ---------- */
.comp-head { display: flex; align-items: center; gap: 8px; margin: 26px 0 10px; }
.card.list { padding: 4px 0; }
.live-row {
  display: grid; grid-template-columns: 66px 1fr 54px 1fr 116px;
  align-items: center; gap: 10px; padding: 11px 16px; text-decoration: none;
  border-bottom: 1px solid var(--line);
}
.card.list .live-row:last-child { border-bottom: 0; }
.live-row:hover { background: var(--surface-2); }
.live-row .when {
  font-family: var(--mono); font-size: 13px; color: var(--muted);
  display: flex; align-items: center; gap: 6px;
}
.live-row .dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent);
}
.live-row .side { display: flex; align-items: center; gap: 9px; min-width: 0; }
.live-row .side.home { justify-content: flex-end; text-align: right; }
.live-row .nm { font-weight: 550; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.live-row .vs { text-align: center; color: var(--muted); font-family: var(--mono); margin: 0; }
.live-row .chan { text-align: right; }
.badge.chan-badge {
  background: color-mix(in srgb, var(--accent) 20%, transparent); color: var(--accent);
}

/* ---------- watch page ---------- */
.watch-head {
  display: grid; grid-template-columns: 1fr 110px 1fr; align-items: center; gap: 12px;
}
.watch-head .wh-side { display: flex; align-items: center; gap: 12px; font-size: 17px; }
.watch-head .wh-side.away { justify-content: flex-end; text-align: right; }
.watch-head .wh-mid { text-align: center; }
.watch-head .big {
  display: block; font-family: var(--mono); font-size: 26px; font-weight: 700;
  letter-spacing: -0.02em;
}
.watch-head .meta { color: var(--muted); font-size: 12.5px; }

.player {
  position: relative; aspect-ratio: 16 / 9; width: 100%; background: #000;
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
  margin-bottom: 18px;
}
.player iframe, .player video { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.player-link {
  position: absolute; inset: 0; display: flex; flex-direction: column; gap: 14px;
  align-items: center; justify-content: center; text-align: center; padding: 24px;
  background: var(--surface); color: var(--text);
}
.player-link p { margin: 0; color: var(--muted); max-width: 40ch; }

.chanbar { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; }
.chanbar h2 { margin: 22px 0 4px; }
.chanbar .sub { margin-bottom: 10px; }

.channels {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 10px;
  max-height: 420px; overflow-y: auto; padding: 2px;
}
.chan-card {
  display: flex; align-items: flex-start; gap: 10px; text-decoration: none;
  padding: 11px 13px; border: 1px solid var(--line); border-radius: 9px;
  background: var(--surface);
}
.chan-card:hover { border-color: var(--muted); background: var(--surface-2); }
.chan-card.on { border-color: var(--accent); background: var(--surface-2); }
.chan-card .n {
  font-family: var(--mono); font-size: 12px; color: var(--muted);
  background: var(--surface-2); border-radius: 6px; padding: 2px 7px; flex: none;
}
.chan-card.on .n { color: var(--accent); }
.chan-card .body { min-width: 0; }
.chan-card .lbl {
  display: block; font-weight: 550; font-size: 14px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chan-card .tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; }
.chan-card .tag {
  font-size: 11px; color: var(--muted); background: var(--surface-2);
  border-radius: 999px; padding: 1px 7px; font-family: var(--mono);
}
.chan-card .tag.src { color: var(--home); }
.chan-card .tag.out { color: var(--away); }

/* ---------- admin ---------- */
.adminbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 12px 16px; }
.adminbar .spacer { flex: 1; }
.adminbar form { margin: 0; }
.daynav { display: flex; align-items: center; gap: 8px; }

.admin-list td { vertical-align: middle; }
.admin-list tr.off { opacity: 0.45; }
.quickadd { display: flex; gap: 6px; margin: 0; }
.quickadd input[type=url] { flex: 1; min-width: 120px; }
.quickadd button { margin: 0; }

.fixture-form {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 14px; align-items: end;
}
.fixture-form .ctl.wide { grid-column: 1 / -1; }
.fixture-form input { width: 100%; }
.fixture-form button { margin: 0; }


.chan-table .linkcell a {
  font-family: var(--mono); font-size: 12px; color: var(--muted);
  display: inline-block; max-width: 240px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom;
}

@media (max-width: 720px) {
  .live-row { grid-template-columns: 52px 1fr 40px 1fr; row-gap: 6px; }
  .live-row .chan { grid-column: 2 / -1; text-align: left; }
  .watch-head { grid-template-columns: 1fr 70px 1fr; }
  .watch-head .wh-side { font-size: 14px; gap: 8px; }
  .channels { max-height: none; }
}

/* ---------- inline tips ---------- */
.tip { margin: 0 0 16px; font-size: 13.5px; }
.tip summary {
  cursor: pointer; color: var(--accent); list-style: none;
  display: inline-flex; align-items: center; gap: 6px; font-weight: 550;
}
.tip summary::-webkit-details-marker { display: none; }
.tip summary::before {
  content: "?"; display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; border-radius: 50%; font-size: 10.5px; font-weight: 700;
  background: var(--accent); color: #0b0d11;
}
.tip[open] summary { margin-bottom: 8px; }
.tip > div {
  background: var(--surface-2); border-left: 3px solid var(--accent);
  padding: 12px 14px; border-radius: 0 8px 8px 0; color: var(--text);
}
.tip ul { margin: 6px 0 0; padding-left: 18px; }
.tip li { margin-bottom: 4px; }
.tip code { font-family: var(--mono); font-size: 12.5px; }
.teamsub { margin: 0 0 10px; font-size: 15px; display: flex; align-items: center; gap: 9px; }

/* ---------- guide ---------- */
.steps { margin: 0; padding-left: 20px; }
.steps li { margin-bottom: 12px; }
.minihead { font-size: 13px; text-transform: uppercase; letter-spacing: 0.05em;
            color: var(--muted); margin: 0 0 6px; }
.muted-p { margin: 0 0 16px; color: var(--text); font-size: 13.5px; line-height: 1.7; }
.limits { margin: 0; padding-left: 20px; }
.limits li { margin-bottom: 9px; }

/* ---------- results header and pager ---------- */
.resulthead {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin: 30px 0 12px;
}
.resulthead h2 { margin: 0; }
.muted-inline { color: var(--muted); font-weight: 400; font-size: 13.5px; margin-left: 8px; }
.pager { display: flex; align-items: center; gap: 10px; }
.pager-foot { justify-content: center; margin: 16px 0 4px; }
.pgbtn {
  display: inline-flex; align-items: center; padding: 7px 15px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface); color: var(--text);
  text-decoration: none; font-size: 13.5px; font-weight: 550; white-space: nowrap;
}
.pgbtn:hover { border-color: var(--accent); color: var(--accent); }
.pgbtn.off { opacity: 0.36; pointer-events: none; }
.pgnum { color: var(--muted); font-size: 13px; font-family: var(--mono); white-space: nowrap; }

/* ---------- chip group headers ---------- */
.lblrow {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 6px;
}
.lblrow .lbl { margin-bottom: 0; }
.lbl-hint { text-transform: none; letter-spacing: 0; }
.chipactions { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; }
.selcount { color: var(--muted); font-family: var(--mono); font-size: 11.5px; }
.linkbtn {
  background: none; border: 0; padding: 0; cursor: pointer; font: inherit;
  color: var(--accent); font-weight: 550;
}
.linkbtn:hover { text-decoration: underline; }
/* The league fold exists only on a phone; see mobile.css .leagues-ctl. */
.lg-toggle { display: none; }
.chipactions .sep { color: var(--line); }

/* ---------- best-prediction carousel ---------- */
.potd-wrap { margin-bottom: 22px; }
.potd-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 10px; flex-wrap: wrap;
}
.potd-head .potd-tag { margin-bottom: 0; }
/* Only rendered when the window had to be widened past two days — out of
   season the strongest selections are weeks away, and eight of them under a
   heading that implies tonight would be misleading. */
.potd-tag .potd-span {
  margin-left: 8px; font-weight: 500; color: var(--muted);
  font-family: var(--mono); font-size: 11.5px;
  border: 1px solid var(--line); border-radius: 999px; padding: 2px 8px;
}
.potd-nav { display: flex; align-items: center; gap: 9px; }
.carbtn { padding: 5px 13px; font-size: 14px; line-height: 1.2; }

.potd-track { position: relative; }
.potd-track .slide { display: none; margin-bottom: 0; }
.potd-track .slide.active { display: block; animation: potdIn 0.22s ease-out; }
@keyframes potdIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

.potd-versus {
  display: grid; grid-template-columns: 1fr auto 1fr; gap: 18px;
  align-items: end; padding-bottom: 16px; margin-bottom: 16px;
  border-bottom: 1px solid var(--line);
}
.vteam { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.vteam.away { align-items: flex-end; text-align: right; }
.vplayer {
  height: 104px; width: auto; max-width: 150px; object-fit: contain;
  object-position: bottom center; filter: drop-shadow(0 6px 14px rgba(0,0,0,0.34));
}
.vplayer.ghost {
  height: 104px; width: 76px; border-radius: 12px;
  background: linear-gradient(180deg, transparent, color-mix(in srgb, var(--muted) 16%, transparent));
}
.vmeta { display: flex; align-items: center; gap: 9px; min-width: 0; }
.vteam.away .vmeta { flex-direction: row-reverse; }
.vname { font-size: 16px; font-weight: 640; letter-spacing: -0.01em;
         white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vplayer-name { color: var(--muted); font-size: 12px; margin-top: 1px; }
.vplayer-name span {
  font-family: var(--mono); color: var(--accent); margin-left: 4px; font-size: 11px;
}
.vmid { text-align: center; padding-bottom: 4px; }
.vs-big { font-size: 15px; color: var(--muted); margin-bottom: 4px; }

.potd-dots { display: flex; justify-content: center; gap: 7px; margin-top: 12px; }
.potd-dots .dot {
  width: 7px; height: 7px; padding: 0; border-radius: 50%; border: 0; cursor: pointer;
  background: var(--line); transition: background 0.15s, width 0.15s;
}
.potd-dots .dot.on { background: var(--accent); width: 20px; border-radius: 4px; }

@media (max-width: 700px) {
  .potd-versus { grid-template-columns: 1fr auto 1fr; gap: 10px; }
  .vplayer, .vplayer.ghost { height: 72px; max-width: 96px; }
  .vname { font-size: 13.5px; }
}

/* ---------- calibration bars ---------- */
.calbar { position: relative; height: 10px; background: var(--surface-2); border-radius: 5px; }
.calbar-pred, .calbar-act { position: absolute; top: -1px; width: 3px; height: 12px; border-radius: 2px; }
.calbar-pred { background: var(--muted); }
.calbar-act { background: var(--accent); }

/* ---------- bet slip ---------- */
.slipcol { width: 30px; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.slipbox { accent-color: var(--accent); width: 15px; height: 15px; cursor: pointer; }
.slipbar {
  position: sticky; bottom: 14px; z-index: 20;
  display: flex; align-items: center; gap: 14px; justify-content: flex-end;
  background: var(--surface); border: 1px solid var(--accent);
  border-radius: 999px; padding: 9px 10px 9px 20px; margin: 16px 0 0;
  box-shadow: 0 8px 26px rgba(0,0,0,0.28); font-size: 13.5px;
}
.slipgo { background: var(--accent); color: #0b0d11; border-color: var(--accent); cursor: pointer; }
.slipgo:hover { filter: brightness(1.08); color: #0b0d11; }
.notice.warn { border-left-color: var(--away); }
.rowwarn { background: color-mix(in srgb, var(--away) 9%, transparent); }

.freshness { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.dot-status { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.dot-status.ok { background: var(--accent); }
.dot-status.warn { background: var(--away); }
.dot-status.bad { background: #e5484d; }
.vplayer.crestfill {
  display: flex; align-items: flex-end; justify-content: center;
  width: auto; min-width: 84px; background: none;
}
.vplayer.crestfill .crest { opacity: 0.92; }

/* ---------- channel detail on the day's list ---------- */
/* The chip row is a second grid line spanning the whole width, so adding it
   does not disturb the five columns above it. */
.live-row .chanlist {
  grid-column: 1 / -1;
  display: flex; flex-wrap: wrap; gap: 5px;
  margin: 2px 0 0 76px;
}
.live-row .chip-mini {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11.5px; color: var(--muted);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: 999px; padding: 2px 9px;
  max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.live-row .chip-mini i {
  font-style: normal; font-family: var(--mono); font-size: 10px;
  color: var(--home); opacity: 0.9;
}
.live-row .chip-mini.out { border-style: dashed; }
.live-row .chip-mini.out::after { content: "↗"; opacity: 0.7; }
.live-row .chip-mini.more { color: var(--text); background: transparent; border-style: dashed; }
.live-row:hover .chip-mini { border-color: var(--muted); }

@media (max-width: 720px) {
  .live-row .chanlist { margin-left: 0; }
  .live-row .chip-mini { max-width: 150px; }
}

/* ---------- "On now" block ---------- */
.comp-head.live-head { color: var(--accent); }
.comp-head.live-head .dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent);
  display: inline-block;
}
.card.list.live-block { border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
.live-row.is-live .when { color: var(--text); font-weight: 600; }
.card.list.done-block { opacity: 0.62; }
.card.list.done-block:hover { opacity: 1; }
.live-row .chip-mini.comp {
  color: var(--text); background: transparent;
  border-color: color-mix(in srgb, var(--home) 45%, transparent);
}

/* --- In-play, from live_state ---------------------------------------
   The clock replaces the kick-off time once the feed is running, so the
   column keeps its width and the row does not reflow when a match starts. */
.live-row .when .clock { color: var(--accent); font-weight: 650; }
.live-row .vs .score { color: var(--text); font-weight: 650; }

/* A goal just went in. Fades rather than blinks: the row is a link, and a
   pulsing target is hard to click. */
@keyframes score-flash {
  from { background: color-mix(in srgb, var(--accent) 22%, transparent); }
  to   { background: transparent; }
}
.live-row.just-changed { animation: score-flash 2.4s ease-out; }

/* Freshness line under the day's list: when the feed last confirmed anything.
   Present so a stopped daemon reads as a stopped daemon rather than as a
   quiet evening with no football on. */
.feed-note {
  display: flex; align-items: center; gap: 7px;
  font-size: 12px; color: var(--muted); font-family: var(--mono);
  padding: 10px 4px 0;
}
.feed-note.stale { color: var(--away); }

@media (prefers-reduced-motion: reduce) {
  .live-row.just-changed { animation: none; }
}

/* ================================================================
   Day-list row, restyled: a scoreline that lines up down the column
   ================================================================ */

/* Wider centre cell so a two-digit basketball score is not cramped, and the
   two flanks share the rest evenly. */
.live-row {
  grid-template-columns: 74px 1fr 96px 1fr 118px;
  gap: 12px;
  padding: 12px 16px;
}

/* Crests keep a fixed box whether the badge loaded or fell back to a
   monogram, so names begin at the same x on every row. */
.live-row .crest,
.live-row .crest-fallback {
  width: 28px; height: 28px; flex: none;
  border-radius: 6px;
}
.live-row .side { gap: 10px; }
.live-row .nm {
  font-weight: 550; font-size: 14.5px; line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}

/* The scoreline. Each number sits in its own fixed cell with tabular figures,
   which is what stops a column of scores drifting as 1 becomes 10. */
.live-row .vs { display: flex; align-items: center; justify-content: center; margin: 0; }
.live-row .score {
  display: inline-flex; align-items: baseline; justify-content: center; gap: 1px;
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 17px; font-weight: 700; letter-spacing: -0.02em;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: 8px; padding: 3px 8px; min-width: 74px;
}
.live-row .score i { font-style: normal; min-width: 1.15em; text-align: center; }
.live-row .score b { font-weight: 500; opacity: 0.4; padding: 0 1px; }
.live-row .score.on {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--accent) 12%, var(--surface-2));
}
.live-row .vs-t {
  color: var(--muted); font-size: 13px; font-family: var(--mono);
}

/* Kick-off time / running clock */
.live-row .when { justify-content: flex-start; font-size: 13px; }
.live-row .when .clock {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  color: var(--accent); font-weight: 650;
}

@media (max-width: 720px) {
  .live-row {
    grid-template-columns: 58px 1fr 78px 1fr;
    gap: 8px; padding: 11px 12px;
  }
  .live-row .crest, .live-row .crest-fallback { width: 24px; height: 24px; }
  .live-row .nm { font-size: 13px; }
  .live-row .score { font-size: 15px; min-width: 62px; padding: 2px 6px; }
  .live-row .chan { grid-column: 2 / -1; text-align: left; }
}

/* ---------- admin: live vs parked channels ----------
   Two lists, not one list with a checkbox column. "What is on air" and "what is
   available but parked" are different questions, and a single table makes you
   read every row to answer either. The parked list is collapsed by default
   because on a busy match it is the longer of the two and the less urgent. */
.chansec { margin: 0 0 18px; }
.chanhead {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin: 0 0 8px;
}
.chanhead h3 { margin: 0; font-size: 15px; }
.chanhead .spacer { flex: 1 1 auto; }
.chanhead .sub { margin: 0; font-size: 12.5px; color: var(--muted); }

.count.off {
  background: color-mix(in srgb, var(--muted) 18%, transparent);
  color: var(--muted);
}

details.chansec.parked > summary {
  display: flex; align-items: center; gap: 10px;
  cursor: pointer; list-style: none; user-select: none;
  padding: 9px 12px; border: 1px solid var(--line); border-radius: 8px;
  background: color-mix(in srgb, var(--line) 22%, transparent);
}
details.chansec.parked > summary::-webkit-details-marker { display: none; }
details.chansec.parked > summary:hover { background: color-mix(in srgb, var(--line) 38%, transparent); }
details.chansec.parked > summary .sub { font-size: 12.5px; color: var(--muted); }
details.chansec.parked > summary .caret { transition: transform .12s; color: var(--muted); }
details.chansec.parked[open] > summary .caret { transform: rotate(90deg); }
details.chansec.parked[open] > summary { border-radius: 8px 8px 0 0; }
details.chansec.parked .chanhead { padding: 10px 12px 0; }

/* The state control is the row's main verb, so it reads as a state you can
   press rather than a checkbox you have to interpret. */
.statebtn {
  display: inline-flex; align-items: center; gap: 6px;
  font: inherit; font-size: 12px; font-weight: 550; line-height: 1;
  padding: 6px 10px; border-radius: 999px; cursor: pointer;
  border: 1px solid transparent; white-space: nowrap;
}
.statebtn .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.statebtn.live {
  color: #15803d;
  background: color-mix(in srgb, #16a34a 15%, transparent);
  border-color: color-mix(in srgb, #16a34a 45%, transparent);
}
.statebtn.live .dot { box-shadow: 0 0 0 3px color-mix(in srgb, #16a34a 25%, transparent); }
.statebtn.parked { color: var(--muted); background: transparent; border-color: var(--line); }
.statebtn.live:hover { filter: brightness(.95); }
.statebtn.parked:hover { color: var(--text); border-color: var(--muted); }

/* A parked row is legible but visibly stood down. The generic .admin-list rule
   fades to 0.45, which is right for a row you are ignoring and too faint for a
   list you have deliberately opened in order to edit. */
.chan-table tr.off { opacity: .68; }
.chan-table tr.off:hover { opacity: 1; }

/* Day list: live and parked counts side by side. */
.chancount { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.chancount .n-live { color: #15803d; font-weight: 600; }
.chancount .n-off  { color: var(--muted); }
.chancount .none   { color: var(--muted); }

/* ---------- compact indicator: minute over score ---------- */
.live-row .when {
  display: flex; flex-direction: column; align-items: flex-start;
  justify-content: center; gap: 2px; line-height: 1.2;
}
.live-row .when-top { display: inline-flex; align-items: center; gap: 6px; }
.live-row .when-score {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 12px; font-weight: 700; color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-radius: 5px; padding: 0 5px; letter-spacing: -0.01em;
}

/* ---------- official channels on air ---------- */
.channels.onair { max-height: none; margin-bottom: 8px; }
.channels.onair .chan-card .n {
  color: var(--accent); font-size: 11px;
  background: color-mix(in srgb, var(--accent) 16%, transparent);
}

/* ---------- broadcaster pills ---------- */
.channels.onair { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 9px; }

.src-pill {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 12px 7px 7px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); text-decoration: none; color: var(--text);
  transition: border-color .15s, transform .15s, background .15s;
  min-width: 0;
}
.src-pill:hover { border-color: var(--accent); transform: translateY(-1px); }
.src-pill.live {
  background: linear-gradient(100deg,
    color-mix(in srgb, var(--accent) 10%, var(--surface)) 0%, var(--surface) 58%);
  border-color: color-mix(in srgb, var(--accent) 40%, var(--line));
}

/* badge, with a breathing ring while on air */
.src-badge-wrap { position: relative; flex: none; width: 36px; height: 36px; }
.src-badge {
  width: 36px; height: 36px; border-radius: 50%; object-fit: contain;
  background: var(--surface-2); display: block;
}
.src-badge.mono {
  display: flex; align-items: center; justify-content: center;
  font-family: var(--mono); font-size: 9px; font-weight: 700; letter-spacing: -.01em;
  color: #fff; background: hsl(var(--h) 42% 34%);
  box-shadow: inset 0 0 0 1px hsl(var(--h) 42% 46%);
}
.src-ring {
  position: absolute; inset: -4px; border-radius: 50%;
  border: 2px solid var(--accent); opacity: .75;
  animation: srcPulse 2s ease-out infinite;
}
@keyframes srcPulse {
  0%   { transform: scale(.92); opacity: .8; }
  70%  { transform: scale(1.12); opacity: 0; }
  100% { transform: scale(1.12); opacity: 0; }
}

.src-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.src-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
.src-name { font-weight: 640; font-size: 10.5px; letter-spacing: -.01em; }

.src-live {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 1px 7px 1px 6px; border-radius: 999px;
  background: var(--accent); color: #0b0d11;
  font-size: 9px; font-weight: 800; letter-spacing: .07em;
}
.src-live-dot {
  width: 5px; height: 5px; border-radius: 50%; background: #0b0d11;
  animation: srcBlink 1.4s ease-in-out infinite;
}
@keyframes srcBlink { 0%,100% { opacity: 1; } 50% { opacity: .25; } }

.src-tier {
  font-size: 9px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); border: 1px solid var(--line);
  padding: 1px 7px; border-radius: 999px;
}
.src-where {
  display: flex; align-items: center; gap: 6px;
  font-size: 9px; color: var(--muted);
}
.src-flag { border-radius: 2px; object-fit: cover; flex: none; }
.src-region { color: var(--muted); }
.src-where > span + span::before { content: "·"; margin-right: 6px; opacity: .6; }
.src-note {
  font-size: 9px; color: var(--muted); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.src-go { color: var(--muted); font-size: 9px; flex: none; }
.src-pill:hover .src-go { color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .src-ring, .src-live-dot { animation: none; }
}
@media (max-width: 560px) {
  .channels.onair { grid-template-columns: 1fr; }
  .src-note { white-space: normal; }
}

/* ---------- track record ---------- */
code.digest {
  font-family: var(--mono); font-size: 12px; color: var(--muted);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: 6px; padding: 2px 7px; word-break: break-all;
}
code.digest.full { display: block; font-size: 13px; color: var(--text); padding: 10px 12px; }

/* ---------- calibration ---------- */
.chart.calib { width: 100%; max-width: 620px; display: block; margin: 0 auto; }
.chart.calib .perfect {
  stroke: var(--muted); stroke-width: 1.5; stroke-dasharray: 5 5; opacity: 0.65;
}
.chart.calib .calibpt {
  fill: color-mix(in srgb, var(--home) 70%, transparent);
  stroke: var(--home); stroke-width: 1.5;
}
.chart.calib .line { fill: none; stroke: var(--home); stroke-width: 2; opacity: 0.55; }
.calbar {
  position: relative; height: 8px; background: var(--surface-2);
  border-radius: 4px; overflow: hidden;
}
.calbar .mid {
  position: absolute; left: 50%; top: 0; bottom: 0; width: 1px; background: var(--line);
}
.calbar .fill { position: absolute; top: 0; bottom: 0; background: var(--home); opacity: 0.75; }

/* A channel's territory, and the ones that cannot reach this viewer. */
.tag.geo {
  background: color-mix(in srgb, #0369a1 14%, transparent);
  color: #0369a1;
  border-color: color-mix(in srgb, #0369a1 35%, transparent);
}
.chan-card.off { opacity: .6; }
.chan-card.off:hover { opacity: 1; }
.chan-card.off .n { color: var(--muted); }
.ccpick { display: inline-flex; align-items: center; gap: 8px; margin-top: 8px; }
.ccpick label { font-size: 12.5px; color: var(--muted); }
.ccpick select { max-width: 240px; }

/* A model that has not earned trust says so, in its own colour. */
.notice.warn {
  border-left-color: var(--away);
  background: color-mix(in srgb, var(--away) 10%, var(--surface-2));
}
.geocell select { width: 100%; }
.geobar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin: 0 0 14px; font-size: 12.5px;
}
.geobar .sub { color: var(--muted); }
.geobar .ccpick { margin-top: 0; }

/* ---------- why the model says it ---------- */
.card.reasoning { display: flex; flex-direction: column; gap: 14px; }
.reasoning .reason {
  border-left: 3px solid var(--line); padding-left: 12px;
}
.reasoning .reason.home { border-left-color: var(--home); }
.reasoning .reason.away { border-left-color: var(--away); }
.reasoning .reason p { margin: 0 0 6px; font-size: 14.5px; line-height: 1.5; }
.reasoning .reason-nums {
  display: flex; flex-wrap: wrap; gap: 14px;
  color: var(--muted); font-size: 12.5px;
}
.reasoning .reason-nums b { font-family: var(--mono); color: var(--text); font-weight: 650; }

/* Which parts of the world this match actually reaches. */
.coverage {
  display: flex; flex-wrap: wrap; gap: 8px 18px; align-items: baseline;
  padding: 10px 14px; font-size: 13px; margin-bottom: 12px;
}
.coverage .cov-ok  { color: #15803d; }
.coverage .cov-gap { color: #c2410c; }
.coverage .sub { flex-basis: 100%; color: var(--muted); font-size: 12px; }

/* ---------- Live page filter bar ----------
   Two controls, stacked, above the day's list: how to order it, and which
   leagues to include. Reuses .chip so a league toggle here looks and behaves
   exactly like a market toggle on the radar. */
.livefilter {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 14px 16px; margin-bottom: 18px;
  display: flex; flex-direction: column; gap: 12px;
}
.lf-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.lf-row.leagues { align-items: flex-start; }
.lf-label {
  color: var(--muted); font-size: 11.5px; text-transform: uppercase;
  letter-spacing: 0.04em; min-width: 58px; padding-top: 7px;
}
.lf-row:not(.leagues) .lf-label { padding-top: 0; }
.lf-hint { color: var(--muted); font-size: 12.5px; }
.lf-chips { display: flex; flex-wrap: wrap; gap: 7px; flex: 1; min-width: 0; }
/* The match count rides inside the chip, so a league with one fixture is
   distinguishable from one with ten before you tick it. */
.lf-chips .chip i {
  font-style: normal; font-family: var(--mono); font-size: 10.5px;
  color: var(--muted); background: var(--bg);
  border-radius: 999px; padding: 1px 6px;
}
.lf-chips .chip.on i { color: var(--accent); }
.lf-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* Segmented control. The radios stay in the DOM for keyboard and no-JS use and
   are hidden visually rather than removed. */
.seg { display: inline-flex; border: 1px solid var(--line); border-radius: 999px; padding: 2px; }
.seg-opt {
  cursor: pointer; padding: 6px 14px; border-radius: 999px;
  font-size: 13px; color: var(--muted); user-select: none; white-space: nowrap;
}
.seg-opt input { position: absolute; opacity: 0; width: 0; height: 0; }
.seg-opt.on { background: var(--surface-2); color: var(--text); font-weight: 550; }
.seg-opt:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (max-width: 720px) {
  .lf-label { min-width: 0; width: 100%; padding-top: 0; }
  .seg { width: 100%; }
  .seg-opt { flex: 1; text-align: center; }
}

/* ---------- "watching from" country picker ---------- */
.ccpick { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ccpick label {
  color: var(--muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;
}
.ccpick-control {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 5px 12px 5px 10px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface-2); transition: border-color .15s;
}
.ccpick-control:focus-within { border-color: var(--accent); }
.ccflag {
  display: block; flex: none; border-radius: 3px;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--text) 14%, transparent);
  object-fit: cover;
}
.ccflag.globe {
  font-size: 15px; line-height: 1; box-shadow: none; width: 22px; text-align: center;
}
/* .ccpick-control select is defined with the other select rules below. */

/* Country label on a league heading, printed once per country so a nation with
   three divisions reads as one block and the A-Z ordering is visible rather
   than inferred from league names. */
.comp-head .comp-country {
  font-size: 11px; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 38%, transparent);
  border-radius: 999px; padding: 2px 9px; margin-right: 2px;
}
.comp-head .tier-tag {
  font-size: 10.5px; font-weight: 500; color: var(--muted);
  border: 1px solid var(--line); border-radius: 999px; padding: 1px 7px;
}
.cc-inline { display: inline-flex; align-items: center; gap: 6px; vertical-align: -2px; }

/* ---------- broadcaster filter strip ---------- */
#srcFilter .src-pill { cursor: pointer; font: inherit; text-align: left; position: relative; }
#srcFilter .src-pill[aria-pressed="true"] {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 45%, transparent);
}
#srcFilter .src-pill.all .src-badge.mono { font-size: 9px; }
.src-count {
  flex: none; margin-left: auto; min-width: 24px; text-align: center;
  font-family: var(--mono); font-size: 9px; font-weight: 650;
  color: var(--muted); background: var(--surface-2);
  border-radius: 999px; padding: 2px 8px;
}
#srcFilter .src-pill[aria-pressed="true"] .src-count { background: var(--accent); color: #0b0d11; }
.src-empty {
  margin: 12px 0 0; padding: 12px 14px; font-size: 13.5px;
  background: var(--surface-2); border-left: 3px solid var(--away);
  border-radius: 0 8px 8px 0; color: var(--text);
}
.src-empty a { color: var(--accent); }

/* ---------- filter strip: motion ----------
   Fast and understated. Every transition is under a quarter second, nothing
   bounces, and the whole layer is switched off for prefers-reduced-motion. */
#srcFilter { perspective: 800px; }
#srcFilter .src-pill {
  transition: border-color .18s ease, box-shadow .18s ease,
              transform .18s cubic-bezier(.2,.7,.3,1), background .18s ease;
  animation: chipIn .34s cubic-bezier(.2,.7,.3,1) both;
}
#srcFilter .src-pill:nth-child(1) { animation-delay: .00s; }
#srcFilter .src-pill:nth-child(2) { animation-delay: .04s; }
#srcFilter .src-pill:nth-child(3) { animation-delay: .08s; }
#srcFilter .src-pill:nth-child(4) { animation-delay: .12s; }
#srcFilter .src-pill:nth-child(5) { animation-delay: .16s; }
#srcFilter .src-pill:nth-child(n+6) { animation-delay: .2s; }
@keyframes chipIn {
  from { opacity: 0; transform: translateY(6px) scale(.985); }
  to   { opacity: 1; transform: none; }
}
#srcFilter .src-pill:hover { transform: translateY(-1px); }
#srcFilter .src-pill:active { transform: translateY(0) scale(.985); transition-duration: .07s; }
#srcFilter .src-pill[aria-pressed="true"] { transform: translateY(-1px); }

/* the selected chip lights up from its leading edge */
#srcFilter .src-pill::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: linear-gradient(100deg,
    color-mix(in srgb, var(--accent) 16%, transparent) 0%, transparent 55%);
  opacity: 0; transition: opacity .2s ease; pointer-events: none;
}
#srcFilter .src-pill[aria-pressed="true"]::after { opacity: 1; }

.src-count { transition: background .18s ease, color .18s ease, transform .18s ease; }
#srcFilter .src-pill[aria-pressed="true"] .src-count { transform: scale(1.06); }
.src-chip-region {
  font-size: 9px; color: var(--muted); white-space: nowrap;
  text-transform: uppercase; letter-spacing: .05em; margin-left: 2px;
}

/* rows leaving and entering the filtered list */
.live-row { transition: opacity .16s ease, transform .16s ease; will-change: opacity, transform; }
.live-row.filter-out { opacity: 0; transform: translateY(-4px) scale(.995); }
.live-row.filter-in { animation: rowIn .26s cubic-bezier(.2,.7,.3,1) both; }
@keyframes rowIn {
  from { opacity: 0; transform: translateY(7px); }
  to   { opacity: 1; transform: none; }
}
.src-empty { animation: rowIn .26s cubic-bezier(.2,.7,.3,1) both; }

@media (prefers-reduced-motion: reduce) {
  #srcFilter .src-pill, .live-row, .live-row.filter-in, .src-empty { animation: none; transition: none; }
  #srcFilter .src-pill:hover, #srcFilter .src-pill[aria-pressed="true"] { transform: none; }
}

/* ---------- horizontal carousel ----------
   A scroller with the bar hidden and arrows that only appear when there is
   somewhere to go. The edge fade is the honest signal: a hard cut at the
   viewport edge looks like the end of the list, a fade does not. */
.hs { position: relative; }
.hs-track {
  display: flex; gap: 10px; align-items: stretch;
  overflow-x: auto; overflow-y: hidden;
  scroll-behavior: smooth; scroll-snap-type: x proximity;
  padding: 2px 2px 6px;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;              /* Firefox */
  -ms-overflow-style: none;           /* old Edge */
}
.hs-track::-webkit-scrollbar { display: none; height: 0; }
.hs-track > * { flex: none; scroll-snap-align: start; }

/* fade whichever edge still has content behind it */
.hs[data-at="start"] .hs-track { mask-image: linear-gradient(90deg, #000 0, #000 86%, transparent 100%); }
.hs[data-at="mid"]   .hs-track { mask-image: linear-gradient(90deg, transparent 0, #000 5%, #000 95%, transparent 100%); }
.hs[data-at="end"]   .hs-track { mask-image: linear-gradient(90deg, transparent 0, #000 14%, #000 100%); }
.hs[data-at="none"]  .hs-track { mask-image: none; }

.hs-btn {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 34px; height: 34px; border-radius: 50%; z-index: 3;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); background: var(--surface); color: var(--text);
  cursor: pointer; font-size: 14px; line-height: 1; padding: 0;
  box-shadow: 0 3px 12px rgba(0,0,0,.22);
  opacity: 0; pointer-events: none;
  transition: opacity .18s ease, transform .18s cubic-bezier(.2,.7,.3,1), border-color .18s ease;
}
.hs-btn.prev { left: -6px; }
.hs-btn.next { right: -6px; }
.hs[data-at="start"] .hs-btn.next,
.hs[data-at="mid"]   .hs-btn.next,
.hs[data-at="mid"]   .hs-btn.prev,
.hs[data-at="end"]   .hs-btn.prev { opacity: 1; pointer-events: auto; }
.hs-btn:hover { border-color: var(--accent); color: var(--accent); transform: translateY(-50%) scale(1.08); }
.hs-btn:active { transform: translateY(-50%) scale(.94); transition-duration: .07s; }

/* the broadcaster strip becomes a single row rather than a wrapping grid */
.channels.onair.hs-track { display: flex; }
.channels.onair.hs-track .src-pill { min-width: 268px; }
.datestrip.hs-track { margin-bottom: 18px; padding-bottom: 6px; }

@media (prefers-reduced-motion: reduce) {
  .hs-track { scroll-behavior: auto; }
  .hs-btn { transition: none; }
  .hs-btn:hover, .hs-btn:active { transform: translateY(-50%); }
}

/* ---------- league heading ----------
   Two lines: the league, then the country under it in caps. That is the shape
   every scores site converged on, and for a good reason — a reader scanning
   fifty blocks looks for the country first and the division second, so putting
   them on one line makes both harder to find. */
.league-head {
  display: flex; align-items: center; gap: 11px;
  margin: 26px 0 10px; font-size: 15px; letter-spacing: -0.01em;
}
.league-head .lh-badge {
  display: flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; flex: 0 0 26px;
}
.league-head .lh-badge img { width: 100%; height: 100%; object-fit: contain; }
.league-head .lh-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.league-head .lh-name {
  font-weight: 650; line-height: 1.15; text-decoration: none; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
a.lh-name:hover { text-decoration: underline; }
.league-head .lh-country {
  font-size: 10.5px; font-weight: 600; letter-spacing: 0.07em;
  color: var(--muted); line-height: 1.15;
}
/* The count and the tier tag sit at the far end, away from the title, so a
   long league name never collides with them. */
.league-head .count, .league-head .tier-tag { margin-left: auto; }
.league-head .tier-tag + .count { margin-left: 8px; }

/* A league chip: flag, then the league. The country is the flag rather than a
   word, because spelled out across sixty chips the country is most of the row
   and the eye has to read it to reach the league. The name still carries it in
   the title and the flag's alt text. */
.lf-chips .chip .compmark { border-radius: 2px; flex: 0 0 auto; }
.lf-chips .chip-league { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lf-chips .chip { max-width: 260px; }

/* ---------- team search in the section row ---------- */
.teamsearch { display: flex; align-items: center; margin-left: auto; gap: 0; }
.teamsearch input[type=search] {
  border-radius: 999px 0 0 999px; border-right: 0; width: 150px;
  transition: width .16s ease, border-color 140ms ease, box-shadow 140ms ease;
}
.teamsearch input[type=search]:focus { outline: none; width: 210px; border-color: var(--accent); }
.teamsearch input::placeholder { color: var(--muted); }
.teamsearch button {
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-2); border: 1px solid var(--line); border-left: 0;
  color: var(--muted); cursor: pointer; padding: 6px 11px;
  border-radius: 0 999px 999px 0;
}
.teamsearch button:hover { color: var(--text); }
.teamsearch input:focus + button { border-color: var(--accent); color: var(--text); }
@media (max-width: 720px) {
  .teamsearch { margin-left: 10px; }
  .teamsearch input[type=search] { width: 110px; }
  .teamsearch input[type=search]:focus { width: 140px; }
}

/* ---------- search results ---------- */
.searchbig { display: flex; gap: 10px; padding: 14px 16px; }
.searchbig input[type=search] { flex: 1; }
.search-row {
  display: flex; align-items: center; gap: 12px; padding: 11px 16px;
  text-decoration: none; border-bottom: 1px solid var(--line);
}
.card.list .search-row:last-child { border-bottom: 0; }
.search-row:hover { background: var(--surface-2); }
.search-row .sr-name { font-weight: 550; }
.search-row .sr-meta { margin-left: auto; display: flex; align-items: center; gap: 9px; font-size: 12.5px; }
/* A hit from another sport says so, inline with the name. Search covers every
   sport, and a basketball "Arsenal" next to the football one needs telling
   apart before the click, not after. */
.sr-sport, .ts-sport {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; font-weight: 600; color: var(--muted);
  background: var(--surface-2); border: 1px solid var(--line); border-radius: 999px;
  padding: 1px 7px; white-space: nowrap; vertical-align: middle;
}
.search-row .sr-sport { margin-left: 8px; }
.ts-name .ts-sport { margin-left: 6px; }

/* ---------- team search modal ----------
   A <dialog>, so focus trapping, Escape and the top layer come from the
   platform. The motion is a transition rather than keyframes because the modal
   can be opened and dismissed faster than it animates, and a transition
   retargets from wherever it is while a keyframe restarts from the beginning. */
:root {
  --ease-out-strong: cubic-bezier(0.23, 1, 0.32, 1);
}
.ts-modal {
  width: min(560px, calc(100vw - 32px));
  max-height: min(70vh, 620px);
  padding: 0; border: 1px solid var(--line); border-radius: 14px;
  background: var(--surface); color: var(--text);
  box-shadow: 0 24px 60px -12px rgb(0 0 0 / 0.45);
  margin: 12vh auto auto;
  /* Never scale(0): nothing in the real world appears out of nothing. */
  opacity: 0; transform: scale(0.97) translateY(-6px);
  transition: opacity 200ms var(--ease-out-strong),
              transform 200ms var(--ease-out-strong),
              overlay 200ms allow-discrete, display 200ms allow-discrete;
}
.ts-modal.is-open { opacity: 1; transform: scale(1) translateY(0); }
.ts-modal::backdrop {
  background: rgb(0 0 0 / 0.45); backdrop-filter: blur(2px);
  opacity: 0; transition: opacity 200ms var(--ease-out-strong);
}
.ts-modal.is-open::backdrop { opacity: 1; }

.ts-head { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-bottom: 1px solid var(--line); }
.ts-input {
  flex: 1; background: transparent; border: 0; color: var(--text);
  font: inherit; font-size: 16px; padding: 4px 2px;
}
.ts-input:focus { outline: none; }
.ts-close { background: none; border: 0; color: var(--muted); font-size: 22px; cursor: pointer; line-height: 1; padding: 0 6px; }
.ts-close:hover { color: var(--text); }

.ts-results { overflow-y: auto; max-height: calc(70vh - 108px); padding: 6px; }
.ts-empty { color: var(--muted); font-size: 13.5px; padding: 18px 12px; margin: 0; text-align: center; }

.ts-row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 10px; border-radius: 9px; text-decoration: none; color: inherit;
  /* Entering rows: fresh nodes on every search, so a keyframe runs once and
     there is no interruption to retarget. --i staggers them. */
  animation: ts-row-in 190ms var(--ease-out-strong) backwards;
  animation-delay: calc(var(--i) * 35ms);
}
@keyframes ts-row-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
.ts-row:hover, .ts-row.on { background: var(--surface-2); }
.ts-row.on { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 45%, transparent); }
.ts-crest { width: 30px; height: 30px; object-fit: contain; flex: 0 0 30px; }
.ts-crest-fallback {
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; background: var(--surface-2); color: var(--muted);
  font-size: 10px; font-weight: 700; letter-spacing: 0.03em;
}
.ts-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ts-name { font-weight: 600; }
.ts-sub { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--muted); }
.ts-vs { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ts-when { font-family: var(--mono); }
.ts-live { display: inline-flex; align-items: center; gap: 5px; color: var(--accent); font-weight: 650; font-family: var(--mono); }
.ts-dot {
  width: 6px; height: 6px; border-radius: 50%; background: var(--accent);
  /* The only perpetual motion here, and it earns it: a pulse is what "this is
     happening now" looks like. Slow and low-contrast so it reads at the edge
     of vision without pulling the eye off the name. */
  animation: ts-pulse 2s ease-in-out infinite;
}
@keyframes ts-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.45; transform: scale(0.82); }
}
.ts-go { margin-left: auto; color: var(--muted); opacity: 0; transition: opacity 140ms ease; }
.ts-row:hover .ts-go, .ts-row.on .ts-go { opacity: 1; }

.ts-foot {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 9px 14px; border-top: 1px solid var(--line);
  font-size: 11.5px; color: var(--muted);
}
.ts-foot kbd {
  font-family: var(--mono); font-size: 10px; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: 4px; padding: 1px 5px; margin-right: 2px;
}
.ts-foot a { margin-left: auto; color: var(--accent); text-decoration: none; }
.ts-foot a:hover { text-decoration: underline; }

/* Fewer and gentler, not none: the fade still explains that something opened,
   the movement and the pulse go. */
@media (prefers-reduced-motion: reduce) {
  .ts-modal { transition: opacity 160ms ease; transform: none; }
  .ts-modal.is-open { transform: none; }
  .ts-row { animation: ts-fade-in 160ms ease backwards; animation-delay: 0s; }
  @keyframes ts-fade-in { from { opacity: 0; } to { opacity: 1; } }
  .ts-dot { animation: none; }
}

/* ---------- search result: fixtures and the Predict button ----------
   The stagger moved onto the wrapper when each result grew a fixture list —
   animating the row alone would have left its fixtures arriving flat. */
.ts-item {
  animation: ts-row-in 190ms var(--ease-out-strong) backwards;
  animation-delay: calc(var(--i) * 35ms);
  border-bottom: 1px solid var(--line);
  padding-bottom: 4px; margin-bottom: 4px;
}
.ts-results .ts-item:last-child { border-bottom: 0; margin-bottom: 0; }
.ts-item .ts-row { animation: none; }

.ts-fxlist { padding: 0 10px 6px 52px; display: flex; flex-direction: column; gap: 3px; }
.ts-fx { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ts-fx-main {
  display: flex; align-items: center; gap: 9px; flex: 1; min-width: 0;
  text-decoration: none; color: inherit; font-size: 12.5px;
  padding: 5px 8px; border-radius: 7px;
}
.ts-fx-main:hover { background: var(--surface-2); }
.ts-fx-teams { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ts-fx-teams b { font-family: var(--mono); color: var(--text); }
.ts-fx-comp { margin-left: auto; color: var(--muted); font-size: 11px; white-space: nowrap; }

.ts-predict {
  background: transparent; color: var(--accent); cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  border-radius: 999px; padding: 4px 11px; font: inherit; font-size: 11.5px; font-weight: 600;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}
.ts-predict:hover:not(:disabled) { background: color-mix(in srgb, var(--accent) 16%, transparent); }
.ts-predict.on { background: var(--accent); color: #0b0d11; border-color: var(--accent); }
.ts-predict:disabled { opacity: 0.6; cursor: default; }

/* The answer expands in place. It is a fresh node each time it is opened, so a
   keyframe runs once and there is nothing to interrupt. */
.ts-pred {
  flex: 1 0 100%; display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  padding: 6px 8px 2px; font-size: 11.5px;
  animation: ts-pred-in 180ms var(--ease-out-strong);
}
@keyframes ts-pred-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}
.ts-pred-wait { color: var(--muted); }
.ts-basis {
  color: var(--muted); font-family: var(--mono); font-size: 10.5px;
  text-transform: uppercase; letter-spacing: 0.05em; white-space: nowrap;
}
.ts-basis.live { color: var(--accent); display: inline-flex; align-items: center; gap: 5px; }
.ts-moved { color: var(--muted); font-family: var(--mono); font-size: 10.5px; }
.ts-bar {
  flex: 1; min-width: 160px; display: flex; height: 18px;
  border-radius: 5px; overflow: hidden; background: var(--surface-2);
  font-family: var(--mono); font-size: 10px;
}
.ts-bar i {
  display: flex; align-items: center; justify-content: center;
  font-style: normal; color: #0b0d11; min-width: 0; overflow: hidden;
  /* Widths change when a live match moves, so they transition rather than jump. */
  transition: width 260ms var(--ease-out-strong);
}
.ts-bar .h { background: var(--home); }
.ts-bar .d { background: var(--draw); color: #fff; }
.ts-bar .a { background: var(--away); }

@media (prefers-reduced-motion: reduce) {
  .ts-item { animation: ts-fade-in 160ms ease backwards; animation-delay: 0s; }
  .ts-pred { animation: ts-fade-in 160ms ease; }
}
@media (max-width: 720px) {
  .ts-fxlist { padding-left: 12px; }
  .ts-fx-comp { display: none; }
}

/* ---------- Predict page ---------- */
.pr-head { padding: 22px 18px; }
.pr-teams { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 18px; }
.pr-side { display: flex; align-items: center; gap: 14px; min-width: 0; }
.pr-side.away { flex-direction: row-reverse; text-align: right; }
.pr-name { font-size: 20px; font-weight: 650; letter-spacing: -0.01em; }
.pr-mid { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.pr-v { font-family: var(--mono); color: var(--muted); font-size: 18px; }
.pr-when { font-size: 12px; color: var(--muted); font-family: var(--mono); white-space: nowrap; }

/* The scan. One ring, five steps that light in turn, then the whole thing
   fades and the result rises into its place. It is a transition on the
   container so an early fetch does not snap it away mid-step. */
.pr-scan {
  display: flex; align-items: center; gap: 22px; padding: 26px 24px;
  transition: opacity 240ms var(--ease-out-strong), transform 240ms var(--ease-out-strong);
}
.pr-scan.is-done { opacity: 0; transform: translateY(-8px); }
.pr-scan-ring { position: relative; width: 54px; height: 54px; flex: 0 0 54px; }
.pr-scan-ring::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  border: 3px solid color-mix(in srgb, var(--accent) 22%, transparent);
}
.pr-scan-ring span {
  position: absolute; inset: 0; border-radius: 50%;
  border: 3px solid transparent; border-top-color: var(--accent);
  animation: pr-spin 900ms linear infinite;
}
@keyframes pr-spin { to { transform: rotate(360deg); } }
.pr-scan-title { font-size: 17px; font-weight: 650; margin-bottom: 8px; }
.pr-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.pr-steps li {
  font-size: 13px; color: var(--muted); padding-left: 20px; position: relative;
  transition: color 160ms ease;
}
.pr-steps li::before {
  content: ""; position: absolute; left: 0; top: 7px; width: 8px; height: 8px;
  border-radius: 50%; border: 1.5px solid var(--line); transition: background 160ms ease, border-color 160ms ease;
}
.pr-steps li.on { color: var(--text); }
.pr-steps li.on::before { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 35%, transparent); }
.pr-steps li.done::before { background: var(--accent); border-color: var(--accent); }

#pr-result { opacity: 0; transform: translateY(10px); transition: opacity 260ms var(--ease-out-strong), transform 260ms var(--ease-out-strong); }
#pr-result.is-in { opacity: 1; transform: none; }
.sub-inline { font-size: 12.5px; color: var(--muted); font-weight: 400; margin-left: 8px; }

.pr-nowcard { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding: 14px 16px; }
.ts-bar.big { height: 30px; font-size: 12px; min-width: 260px; }
.ts-bar.big i { padding: 0 6px; white-space: nowrap; }

/* Best outcomes: fresh nodes, so a keyframe stagger is right. */
.pr-best { padding: 4px 0; }
.pr-bestrow {
  display: grid; grid-template-columns: 34px 1fr auto 150px; align-items: center; gap: 14px;
  padding: 12px 18px; border-bottom: 1px solid var(--line);
  animation: ts-row-in 200ms var(--ease-out-strong) backwards;
  animation-delay: calc(var(--i) * 45ms);
}
.pr-best .pr-bestrow:last-child { border-bottom: 0; }
.pr-rank {
  width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: var(--surface-2); font-family: var(--mono); font-weight: 650; font-size: 13px;
}
.pr-bestrow:first-child .pr-rank { background: var(--accent); color: #0b0d11; }
.pr-besttext { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pr-bestlabel { font-weight: 600; }
.pr-bestfam { font-size: 12px; color: var(--muted); }
.pr-bestpct { font-family: var(--mono); font-size: 22px; font-weight: 700; letter-spacing: -0.02em; }

/* Strength: a bar and its number, coloured by grade. */
.pr-str { display: inline-flex; align-items: center; gap: 8px; min-width: 120px; }
.pr-str i { display: block; height: 6px; border-radius: 3px; background: var(--muted); flex: 0 0 auto; max-width: 80px; transition: width 300ms var(--ease-out-strong); }
.pr-str b { font-family: var(--mono); font-size: 12px; font-weight: 600; color: var(--muted); }
.pr-str.strong i { background: var(--accent); }
.pr-str.strong b { color: var(--accent); }
.pr-str.moderate i { background: var(--home); }
.pr-str.weak i { background: var(--line); }

.pr-factors { padding: 4px 0; }
.pr-factor {
  display: grid; grid-template-columns: 150px 1fr; gap: 4px 16px; padding: 11px 18px;
  border-bottom: 1px solid var(--line);
  animation: ts-row-in 200ms var(--ease-out-strong) backwards;
  animation-delay: calc(var(--i) * 35ms + 160ms);
}
.pr-factors .pr-factor:last-child { border-bottom: 0; }
.pr-fk { color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; padding-top: 2px; }
.pr-fv { font-weight: 550; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.pr-fn { grid-column: 2; font-size: 12.5px; color: var(--muted); }
.pr-lean { font-size: 11px; font-weight: 600; border-radius: 999px; padding: 2px 8px; }
.pr-lean.home { background: color-mix(in srgb, var(--home) 22%, transparent); color: var(--home); }
.pr-lean.away { background: color-mix(in srgb, var(--away) 22%, transparent); color: var(--away); }

.pr-filter { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; flex-wrap: wrap; }
.pr-filter input[type=search] { flex: 1; min-width: 220px; }
.pr-table tr.weak td { color: var(--muted); }
.pr-foot { font-size: 12.5px; color: var(--muted); margin-top: 14px; line-height: 1.6; }

@media (prefers-reduced-motion: reduce) {
  .pr-scan-ring span { animation: none; border-top-color: var(--accent); opacity: .7; }
  .pr-bestrow, .pr-factor { animation: ts-fade-in 160ms ease backwards; animation-delay: 0s; }
  .pr-scan, #pr-result { transition: opacity 160ms ease; transform: none; }
}
@media (max-width: 720px) {
  .pr-teams { grid-template-columns: 1fr; }
  .pr-side.away { flex-direction: row; text-align: left; }
  .pr-mid { flex-direction: row; justify-content: space-between; }
  .pr-bestrow { grid-template-columns: 28px 1fr auto; }
  .pr-bestrow .pr-str { grid-column: 2 / -1; }
  .pr-factor { grid-template-columns: 1fr; }
  .pr-fn { grid-column: 1; }
}

/* ---------- teams are links everywhere ----------
   A chip that links keeps the look of one that does not, so a row does not
   turn blue and underlined the moment every team in it becomes clickable.
   The affordance appears on hover instead. */
a.teamchip { color: inherit; text-decoration: none; border-radius: 6px; }
a.teamchip:hover > span { text-decoration: underline; }
@media (hover: hover) and (pointer: fine) {
  a.teamchip:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
}
/* The "v" that opens the match, now that the teams around it open their own
   pages. Small, but it has to read as a target rather than punctuation. */
.fixture-cell a.vs {
  color: var(--muted); text-decoration: none; padding: 0 5px; border-radius: 4px;
  font-family: var(--mono); font-size: 12px;
}
.fixture-cell a.vs:hover { color: var(--text); background: var(--surface-2); }

a.pr-side, a.wh-side { color: inherit; text-decoration: none; }
a.pr-side:hover .pr-name, a.wh-side:hover strong { text-decoration: underline; }

/* Live row as a div with links inside it. The inner anchors inherit the row's
   look so it reads exactly as it did when the whole row was one link; the
   team cells underline on hover to say they go somewhere different. */
.live-row { cursor: pointer; }
.live-row a.when, .live-row a.vs, .live-row a.side { color: inherit; text-decoration: none; }
.live-row a.side:hover .nm { text-decoration: underline; }
.live-row a.when:hover .clock, .live-row a.vs:hover .score { filter: brightness(1.15); }

/* Radar carousel: the team name opens the team page. */
a.vname { display: block; color: inherit; text-decoration: none; }
a.vname:hover { text-decoration: underline; }

/* ================================================================
   [hidden] must actually hide
   ================================================================
   The attribute's default rule is `[hidden] { display: none }` at UA
   specificity, so any class that sets a display type overrides it. The predict
   page hit this: .pr-scan is display:flex, so `scan.hidden = true` left the
   loader in the layout at opacity 0 — an empty card sitting between the badges
   and the result. Several other components here set display on a class too, so
   this is declared once, globally, rather than patched per component. */
[hidden] { display: none !important; }

/* ---------- page tutorial ----------
   Three states, and the transitions between them are the design: an orb in the
   corner, a summary card, and the full explanation in a dialog. The orb is the
   resting state — everything that dismisses the card returns here, so the
   tutorial is never something you have to fight off to read the page.

   Sits above the sticky topbar (z-index 10) but below the search dialog, which
   is in the top layer and outranks stacking contexts anyway. */
.tut {
  position: fixed; right: 18px; bottom: 18px; z-index: 40;
  display: flex; flex-direction: column; align-items: flex-end; gap: 10px;
  max-width: min(360px, calc(100vw - 36px));
}

/* The float. Deliberately small and quiet: it is on every page of the site, so
   anything louder becomes furniture people learn to ignore. It grows a label
   on hover, which is how a bare "?" earns the space it takes up. */
.tut-orb {
  display: inline-flex; align-items: center; gap: 0;
  padding: 0; width: 42px; height: 42px;
  border-radius: 999px; cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  background: var(--surface);
  color: var(--accent);
  box-shadow: 0 4px 16px rgb(0 0 0 / 0.28);
  justify-content: center; overflow: hidden; white-space: nowrap;
  transition: width .18s ease, gap .18s ease, padding .18s ease,
              background .12s ease, color .12s ease, box-shadow .18s ease;
}
.tut-orb:hover, .tut-orb:focus-visible {
  width: 168px; gap: 8px; padding: 0 15px;
  background: var(--accent); color: #0b0d11;
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.34);
}
.tut-orb-label {
  font-size: 13px; font-weight: 600; line-height: 1;
  max-width: 0; opacity: 0; transition: max-width .18s ease, opacity .14s ease;
}
.tut-orb:hover .tut-orb-label, .tut-orb:focus-visible .tut-orb-label {
  max-width: 130px; opacity: 1;
}
/* While the card is open the orb is the close control, so it stops advertising
   itself as a way in. */
.tut.is-open .tut-orb { background: var(--surface-2); color: var(--muted); }
.tut.is-open .tut-orb:hover { width: 42px; gap: 0; padding: 0; color: var(--text); }
.tut.is-open .tut-orb-label { max-width: 0; opacity: 0; }

/* The summary card. */
.tut-card {
  width: min(340px, calc(100vw - 36px));
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 15px 16px 14px;
  box-shadow: 0 12px 34px rgb(0 0 0 / 0.34);
  opacity: 0; transform: translateY(8px) scale(.98);
  transform-origin: bottom right;
  transition: opacity .18s ease, transform .18s ease;
}
.tut.is-open .tut-card { opacity: 1; transform: none; }

.tut-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.tut-kicker {
  margin: 0 0 2px; font-size: 10.5px; font-weight: 700; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--accent);
}
.tut-card-title { margin: 0; font-size: 15.5px; font-weight: 650; letter-spacing: -0.01em; }
.tut-x {
  background: none; border: 0; cursor: pointer; color: var(--muted);
  font-size: 20px; line-height: 1; padding: 0 2px; margin: -2px -2px 0 0;
}
.tut-x:hover { color: var(--text); }
.tut-summary { margin: 9px 0 13px; font-size: 13.5px; line-height: 1.6; color: var(--text); }
.tut-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.tut-btn {
  font: inherit; font-size: 12.5px; font-weight: 600; line-height: 1;
  padding: 8px 13px; border-radius: 8px; cursor: pointer; text-decoration: none;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--muted);
  transition: background .12s, color .12s, border-color .12s;
}
.tut-btn:hover { color: var(--text); border-color: color-mix(in srgb, var(--line) 160%, transparent); }
.tut-btn-main {
  background: var(--accent); color: #0b0d11; border-color: transparent;
  display: inline-flex; align-items: center;
}
.tut-btn-main:hover { color: #0b0d11; filter: brightness(1.06); }

/* The full explanation. */
.tut-modal {
  width: min(560px, calc(100vw - 32px)); max-height: min(76vh, 720px);
  padding: 0; border: 1px solid var(--line); border-radius: 14px;
  background: var(--surface); color: var(--text);
  box-shadow: 0 24px 60px rgb(0 0 0 / 0.46);
  opacity: 0; transform: translateY(10px) scale(.985);
  transition: opacity .16s ease, transform .16s ease;
  overflow: hidden;
}
.tut-modal.is-open { opacity: 1; transform: none; }
.tut-modal::backdrop {
  background: rgb(0 0 0 / 0.5); opacity: 0; transition: opacity .16s ease;
}
.tut-modal.is-open::backdrop { opacity: 1; }
.tut-modal-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 16px 18px 12px; border-bottom: 1px solid var(--line);
}
.tut-modal-head h2 { margin: 0; font-size: 18px; font-weight: 650; letter-spacing: -0.015em; }
.tut-modal-body {
  padding: 4px 18px 16px; overflow-y: auto;
  max-height: calc(min(76vh, 720px) - 122px);
  font-size: 13.5px; line-height: 1.68;
}
.tut-modal-body h4 {
  margin: 18px 0 6px; font-size: 11px; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--accent);
}
.tut-modal-body h4:first-child { margin-top: 14px; }
.tut-modal-body p { margin: 0 0 9px; }
.tut-modal-body strong { font-weight: 650; }
.tut-modal-body code { font-family: var(--mono); font-size: 12.5px; }
.tut-modal-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 18px; border-top: 1px solid var(--line); background: var(--surface-2);
}
.tut-modal-hint { font-size: 12px; color: var(--muted); }
.tut-modal-hint kbd {
  font-family: var(--mono); font-size: 11px; padding: 1px 5px;
  border: 1px solid var(--line); border-radius: 4px; background: var(--surface);
}

/* The switch in the header, pushed to the far right of the section row. */
.tut-toggle {
  margin-left: auto; display: inline-flex; align-items: center; gap: 6px;
  font: inherit; font-size: 12.5px; font-weight: 500; line-height: 1;
  padding: 6px 11px; margin-bottom: 8px; border-radius: 999px; cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--text); white-space: nowrap;
  transition: background .12s, color .12s, border-color .12s, opacity .12s;
}
.tut-toggle:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); }
/* Off reads as off: the accent drains out of it entirely. */
.tut-toggle.is-off {
  background: transparent; border-color: var(--line); color: var(--muted); opacity: .8;
}
.tut-toggle.is-off:hover { color: var(--text); opacity: 1; }

@media (max-width: 700px) {
  .tut { right: 12px; bottom: 12px; }
  .tut-toggle .tut-toggle-text { display: none; }
  .tut-toggle { padding: 6px 8px; }
}
@media (prefers-reduced-motion: reduce) {
  .tut-orb, .tut-orb-label, .tut-card, .tut-modal, .tut-modal::backdrop {
    transition-duration: 1ms;
  }
  .tut-card, .tut-modal { transform: none; }
  .tut-orb:hover, .tut-orb:focus-visible { width: 168px; }
}

/* ---------- header preferences cluster ----------
   Appearance, language and bookmaker: one group, 8px apart, all the same
   height. The height is a token because three different controls have to
   agree on it — a button, and two pills wrapped around a select — and they
   drift apart the moment each carries its own number. */
:root { --hdr-ctl-h: 28px; }
.tier-prefs {
  display: flex; align-items: center; gap: 8px;
  margin-left: auto; flex: none;
}
.tier-prefs > .themetoggle { margin-left: 0; }
.tier-prefs > .langpick { margin: 0; }
[dir="rtl"] .tier-prefs { margin-left: 0; margin-right: auto; }
/* Below the width where the sheet takes these over, the wrapper must stop
   being a box: an empty flex child still eats one of the tier's gaps, which
   would leave a 28px hole in the bar where the cluster used to be. */
@media (max-width: 720px) { .tier-prefs { display: contents; } }

/* ---------- theme toggle ---------- */
.themetoggle {
  margin-left: auto; flex: none;
  display: inline-flex; align-items: center; gap: 2px;
  height: var(--hdr-ctl-h); padding: 3px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface-2);
  cursor: pointer; color: var(--muted); font: inherit; line-height: 1;
  transition: border-color .18s ease, color .18s ease;
}
.themetoggle:hover { border-color: var(--accent); color: var(--text); }
/* Square rather than the old 26x22 pills, and touching rather than 6px apart:
   the three together are now about half the width they were. Three states are
   kept — a two-way switch cannot say "follow my machine", which is the default
   and the one most readers never need to change. */
.themetoggle .tt-opt {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 999px; font-size: 10.5px;
  transition: background .2s cubic-bezier(.2,.7,.3,1), color .2s ease, transform .2s ease;
}
/* the active mode is the one filled in; the other two are just affordances */
.themetoggle[data-mode="light"]  .tt-opt[data-v="light"],
.themetoggle[data-mode="dark"]   .tt-opt[data-v="dark"],
.themetoggle[data-mode="system"] .tt-opt[data-v="system"] {
  background: var(--accent); color: #0b0d11; transform: scale(1.04);
}
.themetoggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .themetoggle, .themetoggle .tt-opt { transition: none; }
  .themetoggle .tt-opt { transform: none !important; }
}
@media (max-width: 560px) {
  .themetoggle .tt-opt { width: 20px; height: 20px; font-size: 11px; }
}

/* On narrow screens the sport tier scrolls sideways. Left alone, the toggle
   would sit at the far end of that scroll and be unreachable without swiping
   the nav; pinning it keeps it on screen where it was put. */
@media (max-width: 860px) {
  .themetoggle {
    position: sticky; right: 0; z-index: 2;
    box-shadow: -10px 0 12px -6px var(--surface);
    background: var(--surface);
  }
}

/* ---------- admin: playlists ---------- */
.pl-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin: 0 0 12px; }
.pl-tile {
  display: flex; flex-direction: column; gap: 2px; padding: 12px 14px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  text-decoration: none; color: inherit;
}
.pl-tile b { font-family: var(--mono); font-size: 22px; font-weight: 700; letter-spacing: -0.02em; }
.pl-tile span { font-size: 12px; color: var(--muted); }
.pl-tile i { font-style: normal; font-size: 11px; color: var(--muted); font-family: var(--mono); margin-top: 4px; }
.pl-tile.on { border-color: var(--accent); }
.pl-tile.probe b { color: var(--accent); }
.pl-filter { display: flex; gap: 8px; align-items: center; margin-bottom: 10px; }
.pl-filter input[type=search] { flex: 1; }
.pl-logo { width: 28px; height: 28px; object-fit: contain; background: var(--surface-2); border-radius: 5px; }
.pl-class { font-size: 11px; font-weight: 600; border-radius: 999px; padding: 2px 8px; text-transform: uppercase; letter-spacing: 0.04em; }
.pl-class.fast     { background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent); }
.pl-class.restream { background: color-mix(in srgb, var(--away) 22%, transparent);   color: var(--away); }
.pl-class.geo      { background: var(--surface-2); color: var(--muted); }
.pl-class.unknown  { background: var(--surface-2); color: var(--muted); }
.pl-feats i {
  font-style: normal; font-family: var(--mono); font-size: 10.5px; color: var(--muted);
  background: var(--surface-2); border-radius: 4px; padding: 1px 6px; margin-right: 4px; white-space: nowrap;
}
.pl-feats i.warn { color: var(--away); }
.pl-table tr.cls-restream td { color: var(--muted); }
.admin-list tr.on td { background: color-mix(in srgb, var(--accent) 8%, transparent); }

/* ---------- admin: width and density ----------
   The public pages are capped at 1040px because that is a comfortable measure
   for reading. An admin table is not read that way — it is scanned across, and
   the cap was the only reason it needed a horizontal scrollbar: the day list
   alone declares 718px of fixed columns before the match name gets any space.
   Admin pages therefore get the window, and the tables get a tighter type
   scale, so everything fits without scrolling sideways. */
body[data-admin] main,
body[data-admin] footer { max-width: min(1560px, calc(100vw - 32px)); }

body[data-admin] table { font-size: 13px; }
body[data-admin] th,
body[data-admin] td { padding: 7px 8px; }
body[data-admin] th { font-size: 11.5px; }

/* Long club names wrap instead of widening the column past its share. */
body[data-admin] .admin-list td { overflow-wrap: anywhere; }
/* The paste field is the one genuinely elastic column: let it give up space
   rather than hold the table open. */
body[data-admin] .quickadd input[type=url] { min-width: 0; }
body[data-admin] .admin-list .btn.small { white-space: nowrap; }

/* The .scroll wrapper stays as the small-screen fallback — below this width no
   amount of shrinking makes six columns fit, and a scrollbar beats clipping. */
@media (min-width: 1100px) {
  body[data-admin] .card.scroll { overflow-x: visible; }
}

/* The paste cell holds three controls and is what actually sets the table's
   minimum width. Letting them wrap onto a second line lets the column give up
   space when the window is narrow, which is the difference between a table
   that compresses and one that pushes a scrollbar under the whole page. */
body[data-admin] .quickadd { flex-wrap: wrap; row-gap: 5px; }
body[data-admin] .quickadd input[type=url] { flex: 1 1 130px; min-width: 0; }
body[data-admin] .quickadd input[type=text] { flex: 0 1 96px; min-width: 0; width: 96px; }
body[data-admin] .admin-list input { font-size: 12.5px; padding: 5px 7px; }

/* ---------- admin: playlist header ----------
   These actions are slow — re-fetching walks a remote .m3u, "check more"
   probes channels over the network one at a time — and they were five small
   forms crammed into a table cell. Pulled out into a header for the open
   playlist: identity on the left, actions on the right, probe progress
   underneath. Motion here is doing a job rather than decorating one: a press
   has to say "received" immediately, because the reply can be seconds away and
   the honest failure otherwise is someone pressing Import three times. */
:root { --ease-out-soft: cubic-bezier(.22, .61, .36, 1); }

.pl-head {
  background: linear-gradient(180deg,
              color-mix(in srgb, var(--accent) 5%, var(--surface)), var(--surface));
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 16px 18px 14px; margin: 0 0 14px;
  animation: pl-head-in 340ms var(--ease-out-soft) both;
}
@keyframes pl-head-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}
.pl-head-top {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 18px; flex-wrap: wrap;
}
.pl-id { display: flex; align-items: flex-start; gap: 12px; min-width: 0; }
.pl-mark {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 10px; color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
}
.pl-title {
  margin: 1px 0 3px; font-size: 19px; font-weight: 650; letter-spacing: -0.02em;
  overflow-wrap: anywhere;
}
.pl-meta {
  margin: 0; font-size: 12.5px; color: var(--muted);
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
}
.pl-meta strong { color: var(--text); font-family: var(--mono); font-weight: 600; }
.pl-meta a { color: var(--muted); text-decoration: none; border-bottom: 1px solid var(--line); }
.pl-meta a:hover { color: var(--text); border-bottom-color: var(--muted); }

/* The toolbar. Grouped by consequence: the three things you do routinely, a
   separator, then the two that change or remove the playlist itself. */
.pl-toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.pl-act { margin: 0; }
.pl-tool-sep {
  width: 1px; align-self: stretch; min-height: 22px; background: var(--line); margin: 0 2px;
}
.btn.danger { color: var(--away); }
.btn.danger:hover {
  border-color: color-mix(in srgb, var(--away) 55%, transparent);
  background: color-mix(in srgb, var(--away) 12%, transparent);
}

/* Press feedback. The button lifts on hover, sinks on press, and on submit is
   replaced by its own working label with a spinner — disabled, so the form
   cannot be sent twice while the request is in flight. */
.btn {
  transition: transform 120ms var(--ease-out-soft), filter 120ms ease,
              background-color 140ms ease, border-color 140ms ease, opacity 140ms ease;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0) scale(.975); }
.btn[disabled] { cursor: progress; opacity: .85; transform: none; }
.btn.is-working { pointer-events: none; }
.btn.is-working::before {
  content: ""; width: 12px; height: 12px; flex: 0 0 auto;
  border-radius: 50%; border: 2px solid currentColor; border-top-color: transparent;
  animation: pl-spin 620ms linear infinite;
}
@keyframes pl-spin { to { transform: rotate(360deg); } }

/* Probe progress. */
.pl-progress { position: relative; margin-top: 16px; padding-top: 5px; }
.pl-progress::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 5px;
  border-radius: 999px; background: var(--surface-2);
}
.pl-progress-bar {
  position: absolute; top: 0; left: 0; height: 5px; border-radius: 999px; width: 0;
  background: linear-gradient(90deg,
              color-mix(in srgb, var(--accent) 65%, transparent), var(--accent));
  animation: pl-fill 780ms 120ms var(--ease-out-soft) forwards;
}
@keyframes pl-fill { to { width: var(--pct); } }
/* Below the track, not over it — the label was clipping the unfilled end. */
.pl-progress-label {
  display: block; text-align: right; margin-top: 7px;
  font-size: 11px; font-family: var(--mono); color: var(--muted);
}

/* Tiles: same data, given weight and a staggered entrance so the header reads
   as one object arriving rather than five. */
.pl-tile {
  transition: border-color 140ms ease, transform 140ms var(--ease-out-soft),
              background-color 140ms ease;
  animation: pl-tile-in 320ms var(--ease-out-soft) both;
  animation-delay: calc(60ms + var(--i, 0) * 45ms);
}
@keyframes pl-tile-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
.pl-tile:hover {
  transform: translateY(-2px); border-color: var(--muted);
  background: color-mix(in srgb, var(--line) 22%, var(--surface));
}
.pl-tile.on { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 7%, var(--surface)); }
.pl-tile.cls-fast b     { color: var(--accent); }
.pl-tile.cls-restream b { color: var(--away); }
.pl-tile b { transition: color 140ms ease; font-variant-numeric: tabular-nums; }

/* Auto on/off, as a state rather than the words yes/no. */
.pl-auto {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em;
}
.pl-auto i {
  width: 7px; height: 7px; border-radius: 50%; background: var(--muted); flex: 0 0 auto;
}
.pl-auto.on { color: var(--accent); }
.pl-auto.on i {
  background: var(--accent);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 60%, transparent);
  animation: pl-pulse 2.4s ease-out infinite;
}
@keyframes pl-pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent); }
  70%  { box-shadow: 0 0 0 6px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
.pl-open {
  font-size: 11.5px; font-weight: 600; color: var(--accent);
  text-transform: uppercase; letter-spacing: 0.05em;
}

/* The flash message is the reply to whichever action was pressed, so it is the
   one thing on the page that should announce itself. */
.flash { animation: pl-flash-in 300ms var(--ease-out-soft) both; }
@keyframes pl-flash-in {
  from { opacity: 0; transform: translateY(-6px) scale(.995); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .pl-head, .pl-tile, .flash { animation: none; }
  .pl-progress-bar { animation: none; width: var(--pct); }
  .pl-auto.on i { animation: none; }
  .btn:hover, .btn:active { transform: none; }
  .btn.is-working::before { animation-duration: 2s; }
}

/* ---------- admin: stream feeds ----------
   A list to pick from on the left, the open feed on the right. The previous
   page stacked a table, a preview and a form and made you scroll between what
   a feed is and what you can do to it; here identity and actions share a
   header (the same one the playlists page uses) and the settings sit under
   it, grouped by what they are for. */
.pv-layout {
  display: grid; grid-template-columns: 300px minmax(0, 1fr);
  gap: 18px; align-items: start;
}
@media (max-width: 960px) { .pv-layout { grid-template-columns: 1fr; } }

.pv-side-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 0 0 10px; }
.pv-side-head h2 { margin: 0; }
.pv-list { display: flex; flex-direction: column; gap: 6px; }
.pv-feed {
  display: flex; gap: 10px; align-items: flex-start; padding: 11px 12px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  text-decoration: none; color: inherit;
  transition: border-color 140ms ease, transform 140ms var(--ease-out-soft),
              background-color 140ms ease, opacity 140ms ease;
  animation: pl-tile-in 320ms var(--ease-out-soft) both;
  animation-delay: calc(40ms + var(--i, 0) * 35ms);
}
.pv-feed:hover { transform: translateY(-1px); border-color: var(--muted); }
.pv-feed.on { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 7%, var(--surface)); }
/* A paused feed is legible but visibly stood down. */
.pv-feed.off { opacity: .6; }
.pv-feed.off:hover, .pv-feed.off.on { opacity: 1; }
.pv-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); margin-top: 6px; flex: 0 0 auto; }
.pv-feed:not(.off) .pv-dot { background: var(--accent); }
.pv-feed-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pv-feed-body b { font-weight: 600; overflow-wrap: anywhere; }
.pv-host {
  font-family: var(--mono); font-size: 11.5px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pv-feed-meta { font-size: 12px; color: var(--muted); }
.pv-none {
  margin: 0; padding: 16px 12px; text-align: center; font-size: 13.5px; color: var(--muted);
  border: 1px dashed var(--line); border-radius: var(--radius);
}
.pv-help { margin-top: 14px; font-size: 12.5px; color: var(--muted); line-height: 1.5; }
.pv-help summary { cursor: pointer; color: var(--text); font-weight: 550; }
.pv-help p { margin: 8px 0 0; }
.pv-help code { font-family: var(--mono); font-size: 11.5px; }

.pv-head { margin-bottom: 14px; }
.pv-lastrun { margin-top: 4px; }
.pv-result { font-family: var(--mono); color: var(--text); }

.pv-card-title { margin: 0 0 4px; font-size: 15px; }
.pv-lead { margin-bottom: 4px; }
.pv-hint { margin: 6px 0 0; font-size: 12px; color: var(--muted); line-height: 1.45; }
.pv-hint code { font-family: var(--mono); font-size: 11.5px; }

.pv-form form { margin-top: 10px; }
.pv-section { padding: 16px 0; border-top: 1px solid var(--line); }
.pv-section:first-of-type { border-top: 0; padding-top: 6px; }
.pv-section h3 {
  margin: 0 0 10px; font-size: 11.5px; font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em;
}
.pv-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px 14px; align-items: end;
}
.pv-grid .ctl.wide { grid-column: 1 / -1; }
.pv-grid input, .pv-grid textarea, .pv-map-grid input { width: 100%; }
.pv-grid textarea, .pv-map-grid input, #base_url { font-family: var(--mono); font-size: 12.5px; }

.pv-map summary { list-style: none; cursor: pointer; display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.pv-map summary::-webkit-details-marker { display: none; }
.pv-map summary h3 { margin: 0; display: inline-flex; align-items: center; gap: 6px; }
.pv-map summary h3::before {
  content: ""; width: 0; height: 0; border: 4px solid transparent; border-left-color: currentColor;
  transition: transform 140ms var(--ease-out-soft);
}
.pv-map[open] summary h3::before { transform: rotate(90deg); }
.pv-map summary .pv-hint { margin: 0; }
.pv-map-group { margin-top: 14px; }
.pv-map-group h4 { margin: 0 0 8px; font-size: 12.5px; font-weight: 600; }
.pv-map-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 10px 12px; }

.pv-form-foot { display: flex; align-items: center; gap: 10px; padding-top: 16px; border-top: 1px solid var(--line); }
.pv-form-foot .btn { margin: 0; }

.pv-preview { margin-bottom: 14px; }
.pv-keys { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 12px; }
.pv-keys code {
  font-family: var(--mono); font-size: 11.5px; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: 6px; padding: 2px 7px;
}

@media (prefers-reduced-motion: reduce) {
  .pv-feed { animation: none; }
  .pv-feed:hover { transform: none; }
}

/* ---------- predict: the full-history analysis ----------
   What the price was built on, shown before the price. The basis badge is
   the honest part: a fit on the competition's own record and a prior with
   nothing behind it must not look the same. */
.pr-scan-status { margin: 10px 0 0; font-size: 12.5px; color: var(--muted); line-height: 1.45; }
.pr-scan.is-failed .pr-scan-ring span { animation: none; border-top-color: var(--away); }
.pr-scan.is-failed .pr-scan-title { color: var(--away); }

.pr-analysis { padding: 16px 18px; }
.pr-an-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.pr-an-basis {
  font-size: 11.5px; font-weight: 650; text-transform: uppercase; letter-spacing: .05em;
  border-radius: 999px; padding: 3px 10px;
  background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--accent);
}
.pr-an-basis.neighbourhood { background: color-mix(in srgb, var(--home) 18%, transparent); color: var(--home); }
.pr-an-basis.partial, .pr-an-basis.prior { background: color-mix(in srgb, var(--away) 18%, transparent); color: var(--away); }
.pr-an-meta { font-size: 12.5px; color: var(--muted); }
.pr-an-meta a { color: var(--muted); text-decoration: underline; }
.pr-an-meta a:hover { color: var(--text); }
.pr-an-notes { margin: 0 0 14px; padding-left: 18px; font-size: 13.5px; line-height: 1.5; }
.pr-an-notes li + li { margin-top: 3px; }
.pr-an-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px 24px; }
@media (max-width: 720px) { .pr-an-grid { grid-template-columns: 1fr; } }
.pr-an-form { display: flex; flex-direction: column; gap: 4px; padding: 8px 0; border-top: 1px solid var(--line); }
.pr-an-form:first-child { border-top: 0; padding-top: 0; }
.pr-an-form b { font-weight: 600; }
.pr-an-letters { display: flex; gap: 3px; }
.pr-an-letters i {
  font-style: normal; font-family: var(--mono); font-size: 11px; font-weight: 700;
  width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 5px; background: var(--surface-2); color: var(--muted);
}
.pr-an-letters i.W { background: color-mix(in srgb, var(--accent) 24%, transparent); color: var(--accent); }
.pr-an-letters i.L { background: color-mix(in srgb, var(--away) 22%, transparent); color: var(--away); }
.pr-an-formnum { font-size: 12px; color: var(--muted); }
.pr-an-rec {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 8px;
  font-size: 13.5px;
}
.pr-an-rec b { font-weight: 650; }
.pr-an-rec span { color: var(--muted); font-size: 12.5px; }
.pr-an-rec .pr-an-n { flex-basis: 100%; }
.pr-an-meetings { list-style: none; margin: 0; padding: 0; font-size: 12.5px; }
.pr-an-meetings li {
  display: grid; grid-template-columns: 82px 1fr auto; gap: 10px; align-items: baseline;
  padding: 4px 0; border-top: 1px solid var(--line);
}
.pr-an-meetings li:first-child { border-top: 0; }
.pr-an-date, .pr-an-comp { font-family: var(--mono); font-size: 11px; color: var(--muted); white-space: nowrap; }
.pr-an-meetings b { font-family: var(--mono); font-weight: 650; }

/* A selection from a league whose own walk-forward says the model is not worth
   acting on. Deliberately not alarming — these are ranked for completeness and
   only appear when the reader has asked to see them — but never silent. */
.badge.untrusted {
  background: color-mix(in srgb, var(--away) 16%, transparent);
  color: var(--away);
  border: 1px solid color-mix(in srgb, var(--away) 34%, transparent);
  cursor: help;
}

/* --------------------------------------------------------------------------
   Ticket check — reading somebody else's slip.

   Motion here is doing three jobs and no others: confirming a bookmaker was
   chosen, showing that a real request is in flight rather than a frozen tab,
   and letting a slip arrive row by row instead of appearing as a wall. The
   numbers themselves never move — odds and probabilities are what the reader
   is deciding on, and a figure still animating is a figure you cannot read.
   -------------------------------------------------------------------------- */

:root {
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
}

/* ---- step 1: choosing a bookmaker ---------------------------------------- */

.ticket-form fieldset { border: 0; margin: 0 0 1.1rem; padding: 0; }
.ticket-form legend {
  display: flex; align-items: center; gap: .5rem;
  font-size: .82rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); margin-bottom: .55rem; padding: 0;
}
.step-no {
  display: inline-grid; place-items: center; width: 1.35rem; height: 1.35rem;
  border-radius: 50%; background: var(--surface-2); color: var(--text);
  font-size: .74rem; font-weight: 700;
}

.book-grid {
  display: grid; gap: .55rem;
  grid-template-columns: repeat(auto-fill, minmax(7.4rem, 1fr));
}
.book-tile {
  position: relative; display: flex; flex-direction: column;
  align-items: center; gap: .45rem; padding: .8rem .5rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface-2); cursor: pointer;
  transition: transform 160ms var(--ease-out), border-color 160ms var(--ease-out),
              box-shadow 160ms var(--ease-out), opacity 160ms var(--ease-out);
}
.book-tile input { position: absolute; opacity: 0; pointer-events: none; }
.book-mark {
  width: 34px; height: 34px; border-radius: 8px; object-fit: contain;
  background: #fff; padding: 2px;
}
.book-initial {
  display: grid; place-items: center; font-weight: 700;
  background: var(--surface); color: var(--muted);
}
.book-name { font-size: .82rem; font-weight: 600; text-align: center; }

@media (hover: hover) and (pointer: fine) {
  .book-tile:hover { transform: translateY(-2px); border-color: var(--muted); }
}
.book-tile:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The chosen one lifts; the rest step back rather than disappear, so the row
   still reads as a set you can change your mind within. */
.book-tile.on {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 6px 18px -10px var(--accent);
  transform: translateY(-2px);
}
.ticket-form.has-book .book-tile:not(.on) { opacity: .55; }
@media (hover: hover) and (pointer: fine) {
  .ticket-form.has-book .book-tile:not(.on):hover { opacity: 1; }
}

.book-tick {
  position: absolute; top: .4rem; right: .4rem; width: 15px; height: 15px;
  color: var(--accent); opacity: 0; transform: scale(.6);
  transition: opacity 160ms var(--ease-out), transform 160ms var(--ease-out);
}
.book-tile.on .book-tick { opacity: 1; transform: scale(1); }

/* ---- the country strip: "top books where you are" ------------------------ */
/* Sits above the book grid. Left says which country we ordered for; right lets
   the viewer correct it. The books that operate there are ordered first and
   carry a small "Here" / "Top here" tag, so the lead is visible, not implied. */
.cc-strip {
  display: block; margin: 0 0 1rem;
  padding: .95rem 1.05rem 1.05rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface-2);
}
.cc-lead { margin: .45rem 0 0; font-size: .84rem; line-height: 1.5; color: var(--muted); max-width: 62ch; }
.cc-lead strong { color: var(--text); font-weight: 700; }
.cc-strip .ccpick { margin: 0; }

.cc-badge {
  position: absolute; top: .4rem; left: .4rem;
  font-size: .62rem; font-weight: 700; letter-spacing: .02em; text-transform: uppercase;
  padding: .1rem .34rem; border-radius: 999px;
  color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 34%, transparent);
}
/* A local book carries a faint accent edge even before it is chosen, so the
   country's own books read as a group at a glance. */
.book-tile.cc-local { border-color: color-mix(in srgb, var(--accent) 30%, var(--line)); }

/* ---- the ranked "top books in your country" panel ------------------------
   Six rows that all said "Coming soon / Notify me" read as one repeated thing
   rather than six books, because the only part that differed — the name — was
   the least emphasised. So the name now leads, what we can do with it is a
   line under it in plain words, and the action sits at the end of the row on
   its own. The books we can act on carry an accent edge and a tick; the rest
   step back rather than shouting the same two words in unison. */

.cc-head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: .6rem 1rem;
}
.cc-title { display: flex; align-items: center; gap: .55rem; min-width: 0; }
.cc-title h2 { margin: 0; font-size: 1.02rem; letter-spacing: -0.01em; }
.cc-title .compmark, .cc-title img {
  width: 24px; height: 18px; border-radius: 3px; object-fit: cover; flex: none; margin: 0;
}
.cc-count { margin: .5rem 0 0; font-size: .78rem; color: var(--muted); }
.cc-count b { color: var(--accent); font-family: var(--mono); }
/* The invitation under the count: how a "build list" book becomes exact is
   evidence visitors already hold, so it is asked for where the gap is shown. */
.cc-teach { margin: .35rem 0 0; font-size: .78rem; line-height: 1.5; color: var(--muted); max-width: 62ch; }
.cc-teach a { color: var(--accent); font-weight: 600; }

/* "Prediction generated …" on the match hero: a dated claim reads as a record. */
.pred-stamp { display: inline-block; margin-left: .6rem; font-size: .74rem; color: var(--muted); font-variant-numeric: tabular-nums; }

/* Breadcrumbs: where the page sits. One line, quiet, before the content. */
.crumbs { margin: .2rem 0 .7rem; font-size: .8rem; color: var(--muted); }
.crumbs ol { list-style: none; display: flex; flex-wrap: wrap; gap: .25rem .4rem; margin: 0; padding: 0; }
.crumbs li + li::before { content: "›"; margin-right: .4rem; color: var(--line); }
.crumbs a { color: var(--muted); text-decoration: none; }
.crumbs a:hover { color: var(--accent); text-decoration: underline; }
.crumbs [aria-current="page"] { color: var(--text); font-weight: 600; }

/* Long-form pages (about, methodology, terms…): a reading measure and rhythm. */
.prose { max-width: 72ch; line-height: 1.6; }
.prose h2 { margin: 1.4rem 0 .5rem; font-size: 1.12rem; }
.prose h2:first-child { margin-top: .2rem; }
.prose p, .prose ul { margin: 0 0 .85rem; }
.prose ul { padding-left: 1.2rem; }
.prose li { margin: .3rem 0; }
.prose code { font-family: var(--mono); font-size: .9em; background: var(--surface-2); padding: .05em .35em; border-radius: 4px; }

.cc-books {
  list-style: none; margin: .8rem 0 0; padding: 0;
  display: grid; gap: .5rem; grid-template-columns: repeat(auto-fill, minmax(16.5rem, 1fr));
}
.cc-book {
  position: relative; display: flex; align-items: center; gap: .6rem;
  padding: .6rem .7rem; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface);
  transition: border-color 140ms var(--ease-out), transform 140ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .cc-book:hover { transform: translateY(-1px); border-color: var(--muted); }
}
/* The rank is a reference, not a headline: small, tabular, out of the way. */
.cc-rank {
  position: absolute; top: .3rem; right: .45rem;
  font-size: .62rem; font-weight: 700; color: var(--muted); opacity: .5;
  font-variant-numeric: tabular-nums;
}
.cc-book .book-mark, .cc-mono {
  width: 34px; height: 34px; border-radius: 9px; flex: 0 0 auto;
}
/* A monogram takes its hue from the name, so an unwired list still reads as a
   set of distinct books rather than a column of grey squares. */
.cc-mono {
  display: grid; place-items: center;
  font-size: .74rem; font-weight: 800; letter-spacing: .02em;
  color: hsl(var(--h) 62% 72%);
  background: hsl(var(--h) 42% 22%);
  border: 1px solid hsl(var(--h) 45% 34%);
}
:root[data-theme="light"] .cc-mono { color: hsl(var(--h) 55% 32%); background: hsl(var(--h) 70% 93%); border-color: hsl(var(--h) 50% 82%); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .cc-mono { color: hsl(var(--h) 55% 32%); background: hsl(var(--h) 70% 93%); border-color: hsl(var(--h) 50% 82%); }
}

.cc-body { display: flex; flex-direction: column; min-width: 0; gap: 1px; flex: 1 1 auto; }
.cc-name {
  font-size: .9rem; font-weight: 650; letter-spacing: -0.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cc-state {
  font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .045em;
  color: var(--muted);
}
.cc-detail {
  font-size: .72rem; color: var(--muted); opacity: .82; line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Three states, three weights. */
.cc-book.is-convert { border-color: color-mix(in srgb, var(--accent) 38%, var(--line)); }
.cc-book.is-convert .cc-state { color: var(--accent); }
.cc-book.is-build .cc-state { color: var(--home); }
.cc-book.is-soon { background: color-mix(in srgb, var(--line) 16%, var(--surface)); }
.cc-book.is-soon .cc-name { color: var(--muted); font-weight: 600; }

.cc-ok {
  flex: 0 0 auto; display: grid; place-items: center; width: 22px; height: 22px;
  border-radius: 50%; font-size: .72rem; font-weight: 800;
  color: var(--muted); background: var(--surface-2);
}
.cc-book.is-convert .cc-ok {
  color: var(--accent); background: color-mix(in srgb, var(--accent) 16%, transparent);
}

.cc-notify {
  flex: 0 0 auto; font-size: .68rem; font-weight: 700; white-space: nowrap;
  padding: .3rem .6rem; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); color: var(--muted); cursor: pointer;
  transition: background 140ms var(--ease-out), color 140ms var(--ease-out),
              border-color 140ms var(--ease-out);
}
@media (hover: hover) {
  .cc-notify:hover { background: var(--accent); color: #0b0d11; border-color: var(--accent); }
}
.cc-notify:disabled {
  cursor: default; border-style: dashed; opacity: .8;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent); border-color: color-mix(in srgb, var(--accent) 34%, transparent);
}

@media (prefers-reduced-motion: reduce) { .cc-book:hover { transform: none; } }

/* ---- step 2: the code ---------------------------------------------------- */

.code-row { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.code-book {
  display: grid; place-items: center; width: 2.3rem; height: 2.3rem;
  border-radius: 8px; background: var(--surface-2);
}
.code-book img { width: 26px; height: 26px; border-radius: 6px; object-fit: contain; }
.code-book.pop img { animation: book-pop 220ms var(--ease-out) both; }
@keyframes book-pop {
  from { opacity: 0; transform: scale(.86); }
  to   { opacity: 1; transform: scale(1); }
}

.ticket-form input#code {
  flex: 1 1 12rem; padding: .6rem .75rem;
  font-family: var(--mono); font-size: 1rem;
  text-transform: uppercase; letter-spacing: .12em;
  border-radius: 8px; border: 1px solid var(--line);
  background: var(--surface-2); color: var(--text);
  transition: border-color 160ms var(--ease-out), box-shadow 160ms var(--ease-out);
}
.ticket-form input#code:focus {
  outline: 0; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
.check-btn { position: relative; min-width: 7rem; }
.check-btn.busy .cb-label { opacity: .45; }
.check-btn.busy::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent;
  animation: cb-spin 620ms linear infinite;
}
@keyframes cb-spin { to { transform: rotate(360deg); } }

/* ---- the pipeline, while we wait ---------------------------------------- */

.ticket-working { margin: 1rem 0; }
.pipe { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.pipe li {
  display: flex; align-items: center; gap: .6rem;
  color: var(--muted); font-size: .9rem;
  opacity: .45;
  transition: opacity 200ms var(--ease-out), color 200ms var(--ease-out);
}
.pipe li.active, .pipe li.done { opacity: 1; color: var(--text); }
.pip {
  width: 9px; height: 9px; border-radius: 50%; flex: none;
  background: var(--line);
  transition: background-color 200ms var(--ease-out), transform 200ms var(--ease-out);
}
.pipe li.active .pip { background: var(--accent); animation: pip-beat 900ms ease-in-out infinite; }
.pipe li.done .pip { background: var(--accent); transform: scale(.75); animation: none; }
@keyframes pip-beat {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.55); }
}

/* ---- results ------------------------------------------------------------- */

.reveal { animation: tk-reveal 220ms var(--ease-out) both; animation-delay: var(--d, 0ms); }
@keyframes tk-reveal {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

.slip-summary { display: flex; flex-wrap: wrap; gap: 1.6rem; }
.tally { display: flex; flex-wrap: wrap; gap: .4rem; margin: .8rem 0; }

.ticket-legs td { vertical-align: top; }
.ticket-legs .meta { display: block; font-size: .78rem; color: var(--muted); }
.ticket-legs tr.unresolved { opacity: .62; }
.ticket-legs .suggestion {
  margin-top: .35rem; padding-left: .6rem;
  border-left: 2px solid var(--accent); font-size: .85rem;
}

/* Fewer and gentler, not none: the reveal keeps its fade so a slip still reads
   as arriving, and loses the movement and the stagger. */
@media (prefers-reduced-motion: reduce) {
  .book-tile, .book-tick, .code-book img, .pip { transition-duration: 1ms; animation: none; }
  .book-tile.on, .book-tile:hover { transform: none; }
  .reveal { animation: tk-fade 160ms ease both; animation-delay: 0ms; }
  @keyframes tk-fade { from { opacity: 0; } to { opacity: 1; } }
  .check-btn.busy::after { animation-duration: 1.6s; }
}

/* ---- the slip, leg by leg -------------------------------------------------
   Cards rather than a table: a leg is five different kinds of fact (who is
   playing, what they backed, what we make of it, the verdict, the better
   option) and a row of seven cells made them all look like the same kind. The
   motion is a reveal and then a fill — bars grow to their value once, on
   arrival — and nothing keeps moving after that. */

.tk-head {
  display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 2fr) auto;
  gap: 1.2rem 1.6rem; align-items: center; padding: 1.1rem 1.3rem;
}
@media (max-width: 860px) { .tk-head { grid-template-columns: 1fr; } }
.tk-head-id { display: flex; align-items: center; gap: .8rem; min-width: 0; }
.tk-head .book-mark { width: 40px; height: 40px; }
.tk-code { font-family: var(--mono); font-size: 1.35rem; font-weight: 700; letter-spacing: .12em; }
.tk-head-meta { font-size: .8rem; color: var(--muted); margin-top: .15rem; }

.tk-k { display: block; font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.tk-sub { font-size: .78rem; color: var(--muted); }

.tk-compare { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: .9rem; align-items: center; }
.tk-odds { text-align: center; }
.tk-odds b { display: block; font-family: var(--mono); font-size: 1.5rem; font-weight: 700; letter-spacing: -.02em; line-height: 1.15; }
.tk-gapwrap { text-align: center; }
.tk-gap {
  position: relative; height: 8px; border-radius: 999px; margin: .35rem 0 .45rem;
  background: linear-gradient(90deg, color-mix(in srgb, var(--away) 55%, transparent),
              var(--surface-2) 50%, color-mix(in srgb, var(--accent) 55%, transparent));
}
.tk-gap::before { content: ""; position: absolute; left: 50%; top: -4px; width: 2px; height: 16px; background: var(--line); }
.tk-gap i {
  position: absolute; top: 50%; left: 0; width: 16px; height: 16px; border-radius: 50%;
  background: var(--text); border: 3px solid var(--surface); box-shadow: 0 1px 4px rgb(0 0 0 / .35);
  transform: translate(-50%, -50%);
  animation: tk-slide 700ms 200ms var(--ease-out) both;
}
.tk-gap.good i { background: var(--accent); }
.tk-gap.bad i { background: var(--away); }
.tk-gap.none i { opacity: .4; }
@keyframes tk-slide { from { left: 50%; } to { left: var(--x); } }
.tk-gap-label { font-size: .78rem; color: var(--muted); }

.tk-ring { position: relative; width: 84px; height: 84px; justify-self: end; }
.tk-ring svg { transform: rotate(-90deg); }
.tk-ring-track { fill: none; stroke: var(--surface-2); stroke-width: 6; }
.tk-ring-fill {
  fill: none; stroke: var(--accent); stroke-width: 6; stroke-linecap: round;
  stroke-dasharray: var(--c); stroke-dashoffset: var(--c);
  animation: tk-ring 900ms 250ms var(--ease-out) forwards;
}
@keyframes tk-ring { to { stroke-dashoffset: var(--off); } }
.tk-ring-text { position: absolute; inset: 0; display: grid; place-content: center; text-align: center; }
.tk-ring-text b { font-family: var(--mono); font-size: .95rem; font-weight: 700; line-height: 1; }
.tk-ring-text span { font-size: .58rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin-top: .2rem; }

.tk-note {
  display: flex; gap: .8rem; align-items: flex-start; padding: .8rem 1rem; margin: .8rem 0;
  border: 1px solid var(--line); border-left: 3px solid var(--home); border-radius: var(--radius);
  background: var(--surface); font-size: .88rem; line-height: 1.5;
}
.tk-note-ico {
  flex: none; width: 20px; height: 20px; border-radius: 50%; display: grid; place-items: center;
  background: color-mix(in srgb, var(--home) 22%, transparent); color: var(--home);
  font-weight: 700; font-size: .72rem; font-family: var(--mono); margin-top: .1rem;
}

.tk-tally { display: flex; flex-wrap: wrap; gap: .45rem; align-items: center; margin: .9rem 0; }
.tk-tally-key { flex-basis: 100%; font-size: .74rem; color: var(--muted); margin-top: .2rem; line-height: 1.7; }
.tk-chip {
  display: inline-flex; align-items: center; gap: .35rem;
  font-size: .78rem; font-weight: 600; border-radius: 999px; padding: .28rem .7rem;
  background: var(--surface-2); color: var(--muted); border: 1px solid transparent;
}
.tk-chip.small { font-size: .68rem; padding: .1rem .5rem; }
.tk-chip b { font-family: var(--mono); }
.tk-chip.agree { background: color-mix(in srgb, var(--accent) 20%, transparent); color: var(--accent); }
.tk-chip.thin  { background: color-mix(in srgb, var(--home) 20%, transparent); color: var(--home); }
.tk-chip.swap  { background: color-mix(in srgb, #f59e0b 22%, transparent); color: #d97706; }
:root[data-theme="dark"] .tk-chip.swap { color: #fbbf24; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .tk-chip.swap { color: #fbbf24; } }
.tk-chip.drop  { background: color-mix(in srgb, var(--away) 22%, transparent); color: var(--away); }
.tk-chip.none  { background: var(--surface-2); color: var(--muted); border-color: var(--line); }

.tk-legs { display: flex; flex-direction: column; gap: .6rem; }
.tk-leg {
  position: relative; display: grid;
  grid-template-columns: 2rem minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1.3fr) minmax(0, 1.5fr);
  gap: .6rem 1rem; align-items: start; padding: .9rem 1rem .9rem .8rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  border-left-width: 4px;
  transition: border-color 160ms var(--ease-out), transform 160ms var(--ease-out), box-shadow 160ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .tk-leg:hover { transform: translateY(-1px); box-shadow: 0 10px 24px -18px rgb(0 0 0 / .6); }
}
/* The verdict tints the whole card, not only its edge: green where we agree,
   orange where we would not back it as priced, red where we disagree. */
.tk-leg.agree { border-left-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--surface)); }
.tk-leg.thin  { border-left-color: #f59e0b; background: color-mix(in srgb, #f59e0b 9%, var(--surface)); }
.tk-leg.swap  { border-left-color: #f59e0b; background: color-mix(in srgb, #f59e0b 11%, var(--surface)); }
.tk-leg.drop  { border-left-color: var(--away); background: color-mix(in srgb, var(--away) 10%, var(--surface)); }
.tk-leg.none, .tk-leg.unresolved { border-left-color: var(--line); }
.tk-leg.thin .tk-leg-no { background: color-mix(in srgb, #f59e0b 22%, transparent); color: #d97706; }
.tk-leg.unresolved .tk-fixture, .tk-leg.unresolved .tk-pick { opacity: .7; }
@media (max-width: 960px) {
  .tk-leg { grid-template-columns: 2rem minmax(0, 1fr) minmax(0, 1fr); }
  .tk-leg .tk-fixture { grid-column: 2 / -1; }
  .tk-leg .tk-verdict { grid-column: 2 / -1; }
}
@media (max-width: 600px) {
  .tk-leg { grid-template-columns: 2rem minmax(0, 1fr); }
  .tk-leg > * { grid-column: 2 / -1; }
  .tk-leg .tk-leg-no { grid-column: 1; grid-row: 2 / span 4; }
}
/* The picker and the "we fixed this" banner span the card at every width. */
.tk-leg > .tk-fixpanel, .tk-leg > .tk-fixedtag { grid-column: 1 / -1 !important; }
.tk-leg-no {
  width: 1.7rem; height: 1.7rem; border-radius: 50%; display: grid; place-items: center;
  font-family: var(--mono); font-size: .78rem; font-weight: 700;
  background: var(--surface-2); color: var(--muted);
}
.tk-leg.agree .tk-leg-no { background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent); }
.tk-leg.swap  .tk-leg-no { background: color-mix(in srgb, #f59e0b 22%, transparent); color: #d97706; }
.tk-leg.drop  .tk-leg-no { background: color-mix(in srgb, var(--away) 22%, transparent); color: var(--away); }
.tk-leg.thin  .tk-leg-no { background: color-mix(in srgb, var(--home) 22%, transparent); color: var(--home); }

.tk-teams { display: flex; align-items: center; gap: .55rem; text-decoration: none; color: inherit; flex-wrap: wrap; }
.tk-teams:hover .tk-team span { text-decoration: underline; }
.tk-team { display: inline-flex; align-items: center; gap: .45rem; font-weight: 600; min-width: 0; }
.tk-team .crest { flex: none; border-radius: 6px; }
.tk-v { font-family: var(--mono); font-size: .75rem; color: var(--muted); }
.tk-fx-meta { font-size: .76rem; color: var(--muted); margin-top: .3rem; }
.tk-pill {
  display: inline-block; font-size: .64rem; text-transform: uppercase; letter-spacing: .05em;
  padding: .05rem .4rem; border-radius: 4px; background: var(--surface-2); color: var(--muted);
}
.tk-pill.swap  { background: color-mix(in srgb, #f59e0b 22%, transparent); color: #d97706; }
.tk-pill.agree { background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent); }

.tk-pick b { display: block; font-size: .95rem; margin-top: .15rem; }
.tk-pick .tk-sub { display: block; }
.tk-oddspill {
  display: inline-block; margin-top: .35rem; font-family: var(--mono); font-size: .8rem; font-weight: 700;
  padding: .12rem .5rem; border-radius: 6px; background: var(--surface-2);
}

.tk-bar { position: relative; height: 10px; border-radius: 999px; background: var(--surface-2); margin: .35rem 0 .4rem; overflow: visible; }
.tk-bar-ours {
  position: absolute; inset: 0 auto 0 0; width: 0; border-radius: 999px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--accent) 70%, transparent), var(--accent));
  animation: tk-grow 700ms var(--ease-out) forwards; animation-delay: calc(var(--d, 0ms) + 120ms);
}
@keyframes tk-grow { to { width: var(--w); } }
.tk-bar-implied {
  position: absolute; top: -4px; bottom: -4px; left: var(--x); width: 2px; background: var(--text);
  opacity: .8; transform: translateX(-50%);
}
.tk-bar-implied::after {
  content: "price"; position: absolute; top: -13px; left: 50%; transform: translateX(-50%);
  font-size: .56rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted);
}
.tk-nums { display: flex; flex-wrap: wrap; gap: .2rem .7rem; font-size: .76rem; color: var(--muted); align-items: baseline; }
.tk-nums > b { font-family: var(--mono); font-size: 1.05rem; color: var(--text); }
.tk-nums span b { font-family: var(--mono); color: var(--text); }
.tk-unres { font-size: .82rem; color: var(--muted); margin-top: .25rem; line-height: 1.45; }
.tk-unres a { display: block; margin-top: .2rem; color: var(--accent); text-decoration: none; }

.tk-str { display: inline-flex; align-items: center; gap: .4rem; margin-top: .4rem; font-size: .72rem; color: var(--muted); }
.tk-str i { position: relative; width: 64px; height: 5px; border-radius: 999px; background: var(--surface-2); overflow: hidden; }
.tk-str i::after {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 0; border-radius: 999px; background: var(--muted);
  animation: tk-grow 600ms 250ms var(--ease-out) forwards; --w: inherit;
}
.tk-str.strong i::after { background: var(--accent); }
.tk-str.moderate i::after { background: var(--home); }
.tk-str b { font-family: var(--mono); color: var(--text); }
.tk-str-word { text-transform: uppercase; letter-spacing: .05em; font-size: .62rem; }

.tk-verdict p { margin: .35rem 0 0; font-size: .82rem; line-height: 1.45; }
.tk-alt {
  margin-top: .55rem; padding: .55rem .7rem; border-radius: 8px;
  background: color-mix(in srgb, #f59e0b 9%, var(--surface)); border: 1px solid color-mix(in srgb, #f59e0b 35%, transparent);
  animation: tk-reveal 260ms var(--ease-out) both; animation-delay: calc(var(--d, 0ms) + 200ms);
}
.tk-leg.agree .tk-alt { background: color-mix(in srgb, var(--accent) 8%, var(--surface)); border-color: color-mix(in srgb, var(--accent) 30%, transparent); }
.tk-alt-k { display: block; font-size: .64rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.tk-alt > b { display: block; font-size: .9rem; margin: .1rem 0; }
.tk-alt-nums { font-size: .76rem; color: var(--muted); }
.tk-alt-nums b { font-family: var(--mono); color: var(--text); }

.tk-ours-slip { margin-top: 1.2rem; }
.tk-ours-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.tk-ours-head h2 { margin: 0 0 .25rem; }
.tk-ours-head .tk-sub { display: block; max-width: 60ch; }
.tk-ours-list { list-style: none; margin: .9rem 0 .4rem; padding: 0; counter-reset: ours; }
.tk-ours-list li {
  display: grid; grid-template-columns: 1.4rem minmax(0, 1.4fr) minmax(0, 1.6fr) auto auto;
  gap: .4rem .9rem; align-items: center; padding: .55rem 0; border-top: 1px solid var(--line);
  animation: tk-reveal 240ms var(--ease-out) both; animation-delay: var(--d, 0ms);
}
.tk-ours-list li::before {
  counter-increment: ours; content: counter(ours);
  font-family: var(--mono); font-size: .74rem; color: var(--muted);
}
.tk-ours-list li:first-child { border-top: 0; }
@media (max-width: 700px) { .tk-ours-list li { grid-template-columns: 1.4rem minmax(0, 1fr); } .tk-ours-list li > * { grid-column: 2; } .tk-ours-list li::before { grid-row: 1 / span 4; } }
.tk-ours-fx { font-weight: 600; }
.tk-ours-num { font-family: var(--mono); font-size: .8rem; color: var(--muted); white-space: nowrap; }
.tk-left { margin: .2rem 0 .6rem; }
.tk-ours-total { display: flex; flex-wrap: wrap; gap: 1.6rem; padding: .8rem 0; border-top: 1px solid var(--line); margin-top: .4rem; }
.tk-ours-total b { display: block; font-family: var(--mono); font-size: 1.25rem; font-weight: 700; }
.tk-slip-text {
  width: 100%; margin-top: .6rem; font-family: var(--mono); font-size: .74rem; line-height: 1.5;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: 8px; color: var(--muted);
  padding: .5rem .6rem; resize: vertical; min-height: 6rem;
}
.tk-copy.done { background: var(--accent); color: #0b0d11; }

/* ---- fixing a leg, fixing the ticket, the pool ticket -------------------- */

.tk-fixall { margin-left: auto; display: inline-flex; align-items: center; gap: .4rem; }
.tk-poolhint { margin: .5rem 0 0; }
.tk-poolhint a { color: var(--accent); }
.mono { font-family: var(--mono); }

/* The fix buttons sit at the right edge of their row, and shine: a light
   sweeps across them and a soft glow breathes underneath, so the one thing
   on the card that changes something reads as the one thing that does. Once
   applied the same button turns into the undo, in green, and stays put. */
.tk-fixwrap { margin-top: .55rem; display: flex; flex-direction: column; align-items: flex-end; }
/* A fixed leg: the card goes green and a banner across its top says so. */
.tk-leg.fixed, .tk-leg.fixed.thin, .tk-leg.fixed.swap, .tk-leg.fixed.drop, .tk-leg.fixed.none {
  border-left-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 13%, var(--surface));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 40%, transparent);
}
.tk-fixedtag {
  display: flex; align-items: center; gap: .6rem; margin: -.3rem 0 .2rem;
  padding: .5rem .8rem; border-radius: 9px; font-size: .86rem;
  background: linear-gradient(90deg, color-mix(in srgb, var(--accent) 30%, var(--surface)), color-mix(in srgb, var(--accent) 12%, var(--surface)));
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  animation: tk-reveal 220ms var(--ease-out) both;
}
.tk-fixed-ico {
  flex: none; width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center;
  background: var(--accent); color: #0b0d11; font-weight: 800; font-size: .8rem;
}
.tk-fixed-text .tk-sub { display: block; margin-top: .1rem; }
.tk-fix { display: inline-flex; align-items: center; gap: .35rem; }
.tk-shine {
  position: relative; overflow: hidden; isolation: isolate;
  background: linear-gradient(135deg, #f59e0b, #fbbf24 45%, #f59e0b);
  color: #1a1305; border: 0;
  box-shadow: 0 0 0 0 color-mix(in srgb, #f59e0b 45%, transparent), 0 6px 18px -8px rgb(245 158 11 / .7);
  animation: tk-breathe 2.6s ease-in-out infinite;
}
.tk-shine::after {
  content: ""; position: absolute; top: -40%; bottom: -40%; left: -60%; width: 45%;
  background: linear-gradient(105deg, transparent 0%, rgb(255 255 255 / .55) 50%, transparent 100%);
  transform: skewX(-20deg);
  animation: tk-sweep 2.8s ease-in-out infinite;
}
@keyframes tk-sweep { 0%, 55% { left: -60%; } 100% { left: 130%; } }
@keyframes tk-breathe {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, #f59e0b 45%, transparent), 0 6px 18px -8px rgb(245 158 11 / .7); }
  50%      { box-shadow: 0 0 0 5px color-mix(in srgb, #f59e0b 0%, transparent), 0 8px 22px -6px rgb(245 158 11 / .95); }
}
.tk-shine:hover { filter: brightness(1.06); }
.tk-shine[aria-expanded="true"] { animation: none; box-shadow: 0 0 0 2px color-mix(in srgb, #f59e0b 55%, transparent); }
.tk-shine.is-fixed {
  background: linear-gradient(135deg, var(--accent), color-mix(in srgb, var(--accent) 70%, #fff) 50%, var(--accent));
  color: #0b0d11; animation: tk-breathe-green 2.6s ease-in-out infinite;
}
@keyframes tk-breathe-green {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 45%, transparent), 0 6px 18px -8px color-mix(in srgb, var(--accent) 70%, transparent); }
  50%      { box-shadow: 0 0 0 5px transparent, 0 8px 22px -6px color-mix(in srgb, var(--accent) 95%, transparent); }
}
/* The card lights up once when a fix lands on it. */
.tk-leg.glow { animation: tk-card-glow 1.4s var(--ease-out) both; }
@keyframes tk-card-glow {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 70%, transparent); }
  100% { box-shadow: 0 0 0 12px transparent, inset 0 0 0 1px color-mix(in srgb, var(--accent) 40%, transparent); }
}
.tk-fixpanel {
  margin-top: .5rem; padding: .5rem; border-radius: 10px;
  background: var(--surface-2); border: 1px solid var(--line);
  animation: tk-reveal 220ms var(--ease-out) both;
}
.tk-opt-head { font-size: .78rem; font-weight: 600; padding: .2rem .4rem .45rem; }
.tk-opt {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto; gap: .6rem; align-items: center;
  padding: .45rem .5rem; border-radius: 8px; cursor: pointer; border: 1px solid transparent;
  transition: background-color 140ms ease, border-color 140ms ease;
}
.tk-opt:hover { background: var(--surface); }
.tk-opt.on { background: var(--surface); border-color: var(--accent); }
.tk-opt input { accent-color: var(--accent); margin: 0; }
.tk-opt-text b { display: block; font-size: .86rem; }
.tk-opt-num { font-size: .76rem; color: var(--muted); white-space: nowrap; }
.tk-opt-num b { font-family: var(--mono); color: var(--text); font-size: .9rem; }
.tk-opt .tk-str { margin-top: 0; }
.tk-opt.leave { border-top: 1px dashed var(--line); border-radius: 0 0 8px 8px; margin-top: .2rem; }
.tk-opt-text { min-width: 0; }
.tk-opt-text b { overflow-wrap: break-word; }
/* The likeliest of the five to land, and the one applied on its own. Marked
   with a tint and a star rather than by position alone: the list is ordered,
   but nothing about a list says which row was acted on. */
.tk-opt.best { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.tk-opt.best:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.tk-star { color: var(--accent); font-size: .82em; vertical-align: .06em; }
.tk-best-word { color: var(--accent); font-weight: 700; }

/* The picker spans the card wherever it sits in the DOM. As a direct child of
   .tk-leg the grid-column rule does the work; the width fallback keeps it
   honest if a re-rendered fragment ever nests it again. */
.tk-fixpanel { grid-column: 1 / -1; width: 100%; box-sizing: border-box; }

/* Each option is one row: radio, name, our number, strength. The last two are
   given a floor so they cannot be crushed into the name and overlap it — which
   is what happened when the whole picker was folded into a fifth of the card. */
.tk-opt { grid-template-columns: auto minmax(9rem, 1fr) minmax(7.5rem, auto) minmax(4.5rem, auto); }
.tk-opt-text .tk-sub { display: block; line-height: 1.35; }
.tk-opt-num { text-align: right; }
@media (max-width: 720px) {
  /* Narrow: the numbers drop under the name rather than fight it for width. */
  .tk-opt { grid-template-columns: auto minmax(0, 1fr) auto; row-gap: .25rem; }
  .tk-opt-num { grid-column: 2 / -1; text-align: left; }
  .tk-opt .tk-str { grid-column: 2 / -1; }
}

.tk-fixbar {
  position: sticky; bottom: .8rem; z-index: 5;
  display: flex; align-items: center; gap: .7rem; flex-wrap: wrap;
  margin: .8rem 0; padding: .7rem 1rem; border-radius: var(--radius);
  background: var(--surface); border: 1px solid var(--accent);
  box-shadow: 0 14px 40px -18px rgb(0 0 0 / .6);
  animation: tk-reveal 240ms var(--ease-out) both; font-size: .86rem;
}
.tk-fixbar .spacer { flex: 1; }

/* The fix, while it runs: the page dims and one box holds the stages. */
.tk-fixing {
  position: fixed; inset: 0; z-index: 40; display: grid; place-items: center;
  background: color-mix(in srgb, var(--bg) 78%, transparent); backdrop-filter: blur(3px);
  animation: tk-fade 200ms ease both;
}
.tk-fixing-box {
  width: min(440px, calc(100vw - 32px)); padding: 1.4rem 1.5rem 1.2rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: 16px;
  box-shadow: 0 30px 80px -20px rgb(0 0 0 / .6);
  animation: tk-pop 320ms var(--ease-out) both;
}
@keyframes tk-pop { from { opacity: 0; transform: translateY(10px) scale(.97); } to { opacity: 1; transform: none; } }
.tk-fixing-ring { position: relative; width: 56px; height: 56px; margin: 0 auto .8rem; }
.tk-fixing-ring::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  border: 4px solid var(--surface-2);
}
.tk-fixing-ring span {
  position: absolute; inset: 0; border-radius: 50%;
  border: 4px solid transparent; border-top-color: var(--accent); border-right-color: var(--accent);
  animation: cb-spin 900ms linear infinite;
}
.tk-fixing-title { text-align: center; font-weight: 650; font-size: 1.05rem; margin-bottom: .9rem; }
.tk-fixing .pipe li { font-size: .86rem; }
body.tk-fixing-open { overflow: hidden; }

/* The pool ticket. */
.tk-pool { margin-top: 1.2rem; border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
.tk-pool.fresh { box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 18%, transparent); animation: tk-glow 1.6s ease-out both; }
@keyframes tk-glow { from { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent); } to { box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 18%, transparent); } }
.tk-pool-head { display: flex; align-items: center; gap: 1.4rem; flex-wrap: wrap; }
.tk-pool-code b {
  display: block; font-family: var(--mono); font-size: 2rem; font-weight: 800; letter-spacing: .16em;
  color: var(--accent); line-height: 1.1; margin: .1rem 0;
}
.tk-pool-code .tk-sub { display: block; }
.tk-pool-nums { display: flex; gap: 1.4rem; margin-left: auto; }
.tk-pool-nums b { display: block; font-family: var(--mono); font-size: 1.3rem; font-weight: 700; }
.tk-pool-actions { display: flex; gap: .5rem; }
.tk-pool-legs { list-style: none; margin: 1rem 0 .4rem; padding: 0; }
.tk-pool-legs li {
  display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1.3fr) auto auto;
  gap: .4rem 1rem; align-items: center; padding: .55rem 0; border-top: 1px solid var(--line);
  animation: tk-reveal 240ms var(--ease-out) both; animation-delay: var(--d, 0ms);
}
.tk-pool-legs li:first-child { border-top: 0; }
.tk-pool-legs li.fixed { background: linear-gradient(90deg, color-mix(in srgb, #f59e0b 8%, transparent), transparent 40%); }
@media (max-width: 760px) { .tk-pool-legs li { grid-template-columns: 1fr; } }
.tk-pool-fx { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.tk-pool-fx .tk-sub { flex-basis: 100%; }
.tk-pool-pick b { display: inline; }
.tk-pool-pick .tk-sub { display: block; }
.tk-pool-num { font-size: .8rem; color: var(--muted); white-space: nowrap; }
.tk-pool-num b { font-family: var(--mono); color: var(--text); }
.tk-pool-note { margin-top: .8rem; line-height: 1.5; }

/* Disclaimer, responsible gambling, terms. */
.tk-terms { margin: 2rem 0 .5rem; padding: 1.2rem 0 0; border-top: 1px solid var(--line); }
.tk-terms-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1.2rem 1.8rem; }
.tk-terms h3 { margin: 0 0 .35rem; font-size: .78rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.tk-terms p { margin: 0; font-size: .8rem; line-height: 1.55; color: var(--muted); }
.tk-terms a { color: var(--text); }

@media (prefers-reduced-motion: reduce) {
  .tk-gap i, .tk-ring-fill, .tk-bar-ours, .tk-str i::after { animation-duration: 1ms; animation-delay: 0ms; }
  .tk-alt, .tk-ours-list li, .tk-pool-legs li, .tk-fixpanel, .tk-fixbar, .tk-fixedtag { animation: tk-fade 160ms ease both; animation-delay: 0ms; }
  .tk-fixing, .tk-fixing-box { animation: none; }
  .tk-fixing-ring span { animation-duration: 2s; }
  .tk-shine, .tk-shine.is-fixed { animation: none; }
  .tk-shine::after { animation: none; display: none; }
  .tk-leg.glow { animation: none; }
  .tk-pool.fresh { animation: none; }
  .tk-leg:hover { transform: none; }
}

/* ---- admin: the bookmaker registry -------------------------------------- */

form.stack { display: flex; flex-direction: column; gap: .7rem; }
form.stack .fieldrow { display: flex; flex-wrap: wrap; gap: .7rem; }
form.stack .fieldrow label {
  display: flex; flex-direction: column; gap: .2rem;
  font-size: .82rem; color: var(--muted); flex: 1 1 12rem;
}
form.stack > label {
  display: flex; flex-direction: column; gap: .2rem;
  font-size: .82rem; color: var(--muted);
}
form.stack label.check { flex-direction: row; align-items: center; gap: .45rem; }
form.stack textarea { font-family: var(--mono); font-size: .82rem; }
form.inline { display: inline; }
td.wrap { max-width: 26rem; overflow-wrap: anywhere; }
.btn.on { background: var(--accent); color: #0b0d10; }

/* Receipts: the headline stat gets a colour only when it has earned one. */
.stat.good .v { color: #15803d; }
.stat.bad  .v { color: #c2410c; }
.pager { display: flex; align-items: center; gap: 14px; margin: 12px 0 20px; }
.pager .sub { color: var(--muted); font-size: 13px; }

/* The "wrong bookmaker" offer. Deliberately not styled as a warning: nothing
   has gone wrong, we simply know something the visitor does not yet. */
.notice.detected { border-left: 3px solid var(--accent); }
.notice.detected .btn { margin-left: .4rem; vertical-align: baseline; }

/* --------------------------------------------------------------------------
   Build a safer slip.

   The bar is buttons, not a slider: the bands are named things with meanings,
   and a slider would suggest 87% and 88% differ in some real way when the
   boundary is a convention we chose.
   -------------------------------------------------------------------------- */

.safer-slip .ss-head h2 { margin: 0 0 .3rem; font-size: 1.05rem; }

.safety-bar {
  display: flex; flex-wrap: wrap; gap: .4rem; margin: .9rem 0;
}
.sb-opt {
  flex: 1 1 7rem; display: flex; flex-direction: column; gap: .1rem;
  padding: .55rem .7rem; cursor: pointer; text-align: left;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface-2); color: var(--text);
  transition: border-color 160ms var(--ease-out), background-color 160ms var(--ease-out),
              transform 160ms var(--ease-out);
}
.sb-opt .sb-name { font-weight: 650; font-size: .9rem; }
.sb-opt .sb-range { font-family: var(--mono); font-size: .74rem; color: var(--muted); }
@media (hover: hover) and (pointer: fine) {
  .sb-opt:hover { border-color: var(--muted); }
}
.sb-opt:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sb-opt.on {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--surface-2));
  box-shadow: inset 0 0 0 1px var(--accent);
}
.sb-opt.on .sb-range { color: var(--text); }

.ss-blurb { color: var(--muted); font-size: .86rem; margin: 0 0 .7rem; }
.ss-table td { vertical-align: top; }
.ss-table .meta { display: block; font-size: .78rem; color: var(--muted); }
.ss-table tr.unresolved { opacity: .6; }
.ss-total { display: flex; flex-wrap: wrap; gap: 1.4rem; margin-top: .9rem; }
/* The compounding warning is the most useful sentence in the section, so it is
   set apart rather than left to look like a footnote. */
.ss-warn {
  margin-top: .9rem; padding: .6rem .8rem;
  border-left: 3px solid var(--away); border-radius: 0 6px 6px 0;
  background: color-mix(in srgb, var(--away) 8%, transparent);
}

@media (prefers-reduced-motion: reduce) {
  .sb-opt { transition-duration: 1ms; }
}

/* ---------- pool ticket: play it on a bookmaker ---------- */
.tk-play { margin-top: 14px; }
.tk-play-head { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 12px; }
.tk-play-head b { display: block; font-size: 17px; letter-spacing: -0.01em; margin: 2px 0 4px; }
.tk-play-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; }
.tk-play-tab {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  background: var(--surface-2); border: 1px solid var(--line); color: var(--muted);
  border-radius: 999px; padding: 6px 12px 6px 8px; font: inherit; font-size: 13px;
  transition: border-color 140ms ease, color 140ms ease;
}
.tk-play-tab img, .tk-play-logo { width: 22px; height: 22px; object-fit: contain; border-radius: 5px; background: #fff; }
.tk-play-tab.on { border-color: var(--accent); color: var(--text); font-weight: 600; }
.tk-play-table { font-size: 13.5px; }
.tk-play-table td .tk-sub { display: block; font-size: 11.5px; }
.tk-play-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 12px 0 8px; }
.tk-play-form { margin-top: 14px; padding-top: 12px; border-top: 1px dashed var(--line); }
.tk-play-formrow { display: flex; gap: 8px; margin-top: 6px; flex-wrap: wrap; }
.tk-play-formrow input {
  flex: 1; min-width: 180px; background: var(--surface-2); border: 1px solid var(--line);
  color: var(--text); font: inherit; padding: 8px 12px; border-radius: 8px;
  text-transform: uppercase; letter-spacing: 0.06em;
}
.tk-play-formrow input:focus { outline: none; border-color: var(--accent); }

.tk-play-verify { border-width: 2px; }
.tk-play-verify.ok { border-color: color-mix(in srgb, var(--accent) 60%, var(--line)); }
.tk-play-verify.partial { border-color: color-mix(in srgb, var(--away) 60%, var(--line)); }
.tk-play-verify-head { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.tk-play-verify-head b { display: block; font-size: 16px; margin-top: 2px; }
.tk-play-cmp { list-style: none; margin: 0; padding: 0; }
.tk-play-cmp li { display: grid; grid-template-columns: 22px 1fr auto; gap: 10px; align-items: center;
                  padding: 8px 4px; border-bottom: 1px solid var(--line); }
.tk-play-cmp li:last-child { border-bottom: 0; }
.tk-play-cmp li .tk-sub { display: block; }
.tk-play-cmp-ico { font-weight: 700; text-align: center; }
.tk-play-cmp li.matched .tk-play-cmp-ico { color: var(--accent); }
.tk-play-cmp li.missing .tk-play-cmp-ico { color: var(--away); }
.tk-play-cmp li.missing b { color: var(--muted); }

/* SportyBet-ready selections inside the play panel. */
.tk-sb { margin-top: 16px; padding-top: 14px; border-top: 1px dashed var(--line); }
.tk-sb-head b { display: block; font-size: 15px; margin: 2px 0 4px; }
.tk-sb-table { font-size: 12.5px; margin-top: 10px; }
.tk-sb-table .mono { font-family: var(--mono); font-size: 11.5px; }
.tk-sb-table tr.gap td { color: var(--away); }
.tk-sb-table tr.ok td.mono { color: var(--accent); }
.tk-sb-note { margin-top: 12px; line-height: 1.55; }
.tk-slip-text.mono { font-family: var(--mono); font-size: 11.5px; min-height: 120px; }

/* The booked code, front and centre once it exists; the button until then. */
.tk-sb-code b#tk-sb-code-text {
  display: block; font-family: var(--mono); font-size: 34px; font-weight: 700;
  letter-spacing: 0.12em; color: var(--accent); margin: 4px 0 2px;
}
.tk-sb-book b { display: block; font-size: 15px; margin: 2px 0 6px; }
/* The code SportyBet has just issued, at the top of the verify view. */
.tk-code-issued { padding-bottom: 14px; margin-bottom: 14px; border-bottom: 1px solid var(--line); }
.tk-code-big {
  display: block; font-family: var(--mono); font-size: 38px; font-weight: 700;
  letter-spacing: 0.14em; color: var(--accent); margin: 4px 0 4px;
}
.tk-sb-wait { display: block; margin-top: 8px; }
.tk-sb-form { margin-top: 10px; }
.tk-sb-reply { margin-top: 8px; padding: 8px 10px; border-left: 3px solid var(--away); background: var(--surface-2); border-radius: 6px; }
.tk-sb-details { margin-top: 14px; }
.tk-sb-details summary { cursor: pointer; }

/* ---------- pool ticket editor ---------- */
.tk-edit { margin-top: 14px; }
.tk-edit-legs { list-style: none; margin: 0; padding: 0; }
.tk-edit-legs li {
  display: grid; grid-template-columns: 1.1fr 1.4fr auto auto; gap: 12px; align-items: center;
  padding: 10px 4px; border-bottom: 1px solid var(--line);
}
.tk-edit-legs li:last-child { border-bottom: 0; }
.tk-edit-legs li.blocks { background: color-mix(in srgb, var(--away) 7%, transparent); border-radius: 8px; }
.tk-edit-fx .tk-sub { display: block; font-size: 11.5px; }
.tk-edit-pick { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.tk-edit-pick select { width: 100%; }
.tk-edit-drop { white-space: nowrap; }
@media (max-width: 720px) {
  .tk-edit-legs li { grid-template-columns: 1fr; }
}

/* ---------- footer ----------
   Four columns that answer the four things a reader arrives at the bottom of a
   page wanting: what this is, where else to go in this sport, what other sports
   exist, and — the one the old footer got wrong — where these particular
   numbers came from. */
footer .foot-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1.6fr;
  gap: 28px 32px;
  max-width: 1040px;
  margin: 0 auto;
  padding: 30px 24px 22px;
  text-align: left;
}
footer .foot-col h3 {
  margin: 0 0 10px;
  font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--muted);
}
footer .foot-col ul { list-style: none; margin: 0; padding: 0; }
footer .foot-col li { margin: 0 0 6px; }
footer .foot-col a { text-decoration: none; font-size: 13.5px; }
footer .foot-col a:hover { color: var(--text); text-decoration: underline; }
footer .foot-col a.on { color: var(--text); font-weight: 550; }
footer .foot-about .brand { font-size: 15px; display: inline-block; margin-bottom: 8px; }
footer .foot-about p,
footer .foot-source p { margin: 0 0 8px; font-size: 13px; line-height: 1.55; }
footer .foot-source strong { color: var(--text); }
/* The limits are the part most likely to be skimmed past, so they are set apart
   rather than run into the sources above them. */
footer .foot-warn {
  margin-top: 10px; padding-top: 10px;
  border-top: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
  font-size: 12.5px;
}
footer .foot-count {
  color: var(--muted); font-size: 11.5px; margin-left: 4px;
  font-variant-numeric: tabular-nums;
}
footer .foot-bar {
  border-top: 1px solid var(--line);
  max-width: 1040px; margin: 0 auto; padding: 14px 24px 26px;
  text-align: left;
}
footer .foot-legal { margin: 6px 0 0; font-size: 12.5px; color: var(--muted); }

@media (max-width: 900px) {
  footer .foot-grid { grid-template-columns: 1fr 1fr; gap: 22px; }
}
@media (max-width: 560px) {
  footer .foot-grid { grid-template-columns: 1fr; }
}

/* The explanation under a ledger entry that no longer verifies. Deliberately
   plain and attached to the row it explains — a break that comes with a reason
   is still a break, and dressing it up as a notice would read like an excuse. */
.ledger-note td {
  background: color-mix(in srgb, var(--away) 7%, transparent);
  border-top: 0; font-size: 12.5px; line-height: 1.55; color: var(--muted);
  padding-top: 8px; padding-bottom: 10px;
}
.ledger-note strong { color: var(--text); font-weight: 600; margin-right: 4px; }
.ledger-note .when { color: var(--away); white-space: nowrap; }

/* ---------- radar: market families, grouped ----------
   Seventy-one chips in one flat run is not a filter, it is a wall. The same
   chips under seven headings, in the words a bookmaker uses, is one. The group
   heading is a label, not a control: ticking is still per family, so a URL
   built before this change still selects exactly what it did. */
.mkt-groups { display: flex; flex-direction: column; gap: 10px; }
.mkt-group { display: flex; flex-direction: column; gap: 6px; }
.mkt-group-head {
  display: flex; align-items: baseline; gap: 8px; margin: 0;
  font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--accent);
}
.mkt-group-head .n {
  font-family: var(--mono); font-weight: 500; letter-spacing: 0; text-transform: none;
  color: var(--muted); font-size: 11px;
}
.mkt-group-head .why {
  font-weight: 400; letter-spacing: 0; text-transform: none;
  color: var(--muted); font-size: 12px;
}
.mkt-group .chips { gap: 6px; }
/* Taller than the old single scroller: there are headings in here now, and a
   scroller that shows one and a half groups makes people think that is all. */
.scroll-chips.mkt-groups { max-height: 300px; padding-right: 6px; }

/* ---- converting a slip to another bookmaker ------------------------------
   Two states and they must not look alike. An exact conversion carries the
   target's own ids and can come back as its code; a named one is the slip in
   that book's language for a person to rebuild. Green for the first, amber
   for the second, and the per-leg pill says which every leg is. */

.tk-conv { margin-top: 1.2rem; }
.tk-conv-head h2 { margin: 0 0 .25rem; }
.tk-conv-head .tk-sub { display: block; max-width: 70ch; }
.tk-conv-pick {
  display: grid; grid-template-columns: auto auto minmax(0, 1fr) auto;
  gap: 1rem; align-items: center; margin-top: 1rem;
}
@media (max-width: 900px) {
  .tk-conv-pick { grid-template-columns: 1fr; }
  .tk-conv-arrow { display: none; }
}
.tk-conv-from, .tk-conv-to { min-width: 0; }
.tk-conv-book { display: inline-flex; align-items: center; gap: .45rem; }
.tk-conv-book .book-mark { width: 28px; height: 28px; }
.tk-conv-book span { display: flex; flex-direction: column; line-height: 1.25; }
.tk-conv-book .mono { font-size: .8rem; color: var(--muted); }
.tk-conv-arrow { font-size: 1.4rem; color: var(--muted); }
.tk-conv-grid { display: grid; gap: .5rem; grid-template-columns: repeat(auto-fill, minmax(8.2rem, 1fr)); }
.tk-conv-tile {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: .3rem;
  padding: .7rem .5rem; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface-2); cursor: pointer; text-align: center;
  animation: tk-reveal 240ms var(--ease-out) both; animation-delay: var(--d, 0ms);
  transition: border-color 150ms var(--ease-out), transform 150ms var(--ease-out);
}
.tk-conv-tile input { position: absolute; opacity: 0; pointer-events: none; }
@media (hover: hover) and (pointer: fine) { .tk-conv-tile:hover { transform: translateY(-2px); border-color: var(--muted); } }
.tk-conv-tile:has(input:checked) { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--surface)); }
.tk-conv-tile:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
.tk-conv-name { font-size: .84rem; font-weight: 600; }
.tk-conv-can {
  font-size: .62rem; text-transform: uppercase; letter-spacing: .05em; font-weight: 700;
  border-radius: 999px; padding: .08rem .45rem;
}
.tk-conv-can.mint { background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent); }
.tk-conv-can.build { background: color-mix(in srgb, #f59e0b 22%, transparent); color: #d97706; }
.tk-conv-tile .tk-sub { font-size: .68rem; }
.tk-conv-go { white-space: nowrap; }

.tk-conv-out { margin-top: 1.2rem; border-left: 4px solid var(--line); }
.tk-conv-out.exact { border-left-color: var(--accent); }
.tk-conv-out.named { border-left-color: #f59e0b; }
.tk-conv-out-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.tk-conv-route { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.tk-conv-actions { display: flex; gap: .5rem; }
.tk-conv-legs { list-style: none; margin: 1rem 0 .5rem; padding: 0; counter-reset: cv; }
.tk-conv-legs li {
  display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1.3fr) minmax(0, .9fr) auto;
  gap: .4rem 1rem; align-items: center; padding: .6rem 0 .6rem .6rem;
  border-top: 1px solid var(--line); border-left: 3px solid transparent;
  animation: tk-reveal 240ms var(--ease-out) both; animation-delay: var(--d, 0ms);
}
.tk-conv-legs li:first-child { border-top: 0; }
.tk-conv-legs li.exact { border-left-color: color-mix(in srgb, var(--accent) 60%, transparent); }
.tk-conv-legs li.named { border-left-color: color-mix(in srgb, #f59e0b 60%, transparent); }
.tk-conv-legs li.unread { border-left-color: color-mix(in srgb, var(--away) 60%, transparent); opacity: .75; }
@media (max-width: 820px) { .tk-conv-legs li { grid-template-columns: 1fr; } }
.tk-conv-fx { display: flex; align-items: center; gap: .35rem; flex-wrap: wrap; }
.tk-conv-fx .tk-sub { flex-basis: 100%; }
.tk-conv-pick b { display: block; font-size: .95rem; }
.tk-conv-pick .tk-sub { display: block; }
.tk-conv-was { font-size: .8rem; color: var(--muted); }
.tk-conv-was .tk-sub { display: block; }
.tk-pill.warn { background: color-mix(in srgb, var(--away) 22%, transparent); color: var(--away); }
.tk-conv-verdict { margin: 1rem 0; padding: .8rem 1rem; border-radius: var(--radius); border: 1px solid var(--line); }
.tk-conv-verdict.ok { border-color: color-mix(in srgb, var(--accent) 50%, transparent); background: color-mix(in srgb, var(--accent) 8%, var(--surface)); }
.tk-conv-verdict.partial { border-color: color-mix(in srgb, #f59e0b 50%, transparent); background: color-mix(in srgb, #f59e0b 8%, var(--surface)); }
.tk-conv-verify { margin-top: 1rem; padding-top: .9rem; border-top: 1px solid var(--line); }
.tk-conv-verify label { display: block; margin-bottom: .45rem; font-size: .88rem; }
.tk-conv-verify input {
  flex: 1 1 12rem; padding: .55rem .7rem; font-family: var(--mono); text-transform: uppercase;
  letter-spacing: .08em; border-radius: 8px; border: 1px solid var(--line);
  background: var(--surface-2); color: var(--text);
}

@media (prefers-reduced-motion: reduce) {
  .tk-conv-tile, .tk-conv-legs li { animation: tk-fade 160ms ease both; animation-delay: 0ms; }
  .tk-conv-tile:hover { transform: none; }
}

/* ---------- convert page ----------
   The same tile grid the Fix page uses, twice on one form: where the code came
   from, and where it should go. The target tiles say two more things a person
   choosing needs — whether that book can hand back a code at all, and how many
   of our fixtures it has taught us its ids for, which is what decides whether
   the conversion comes out exact. */
.cv-form .book-pick + .code-enter { margin-top: 6px; }
.cv-target .cv-can {
  display: inline-block; margin-top: 4px; font-size: 10.5px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; padding: 2px 7px; border-radius: 999px;
}
.cv-target .cv-can.mint  { background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--accent); }
.cv-target .cv-can.build { background: var(--surface-2); color: var(--muted); }
.cv-target .cv-known { display: block; font-size: 11px; color: var(--muted); font-family: var(--mono); margin-top: 3px; }
.cv-go { display: flex; justify-content: flex-end; padding: 14px 16px 4px; }
.cv-go .btn { padding: 11px 22px; font-size: 15px; }

/* ---------- fix picker & conversion: motion ----------
   Both flows make someone wait on a network call they cannot see — the picker
   fetches our top selections, the converter asks a bookmaker for a code. Motion
   here is doing a job: it says the press registered, and it walks the eye down
   the rows that just appeared rather than dropping a block of text in at once. */

/* Options arrive in sequence, not all at once. The delay is set per row from
   its index so the stagger survives any number of them. */
.tk-fixpanel .tk-opt {
  animation: tk-opt-in 260ms var(--ease-out, cubic-bezier(.22,.61,.36,1)) both;
  animation-delay: calc(var(--i, 0) * 45ms);
}
@keyframes tk-opt-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
/* The chosen row lifts out of the list for a moment, so "this is the one that
   was applied" is visible without reading. */
.tk-fixpanel .tk-opt.on { animation: tk-opt-pick 420ms var(--ease-out, cubic-bezier(.22,.61,.36,1)) both; }
@keyframes tk-opt-pick {
  0%   { transform: none; box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent); }
  45%  { transform: translateY(-2px); box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent) 12%, transparent); }
  100% { transform: none; box-shadow: 0 0 0 0 transparent; }
}

/* The conversion result: header first, then the legs walking down. */
.tk-conv-out { animation: tk-conv-in 320ms var(--ease-out, cubic-bezier(.22,.61,.36,1)) both; }
@keyframes tk-conv-in {
  from { opacity: 0; transform: translateY(10px) scale(.995); }
  to   { opacity: 1; transform: none; }
}
/* The legs already carry a --d delay from the renderer; reuse it rather than
   inventing a second stagger convention that would drift from it. */
.tk-conv-legs > * {
  animation: tk-opt-in 300ms var(--ease-out, cubic-bezier(.22,.61,.36,1)) both;
  animation-delay: calc(120ms + var(--d, 0ms));
}
/* A minted code is the payoff of the whole page; it gets one pulse on arrival. */
.tk-conv-out.exact .tk-conv-id { animation: tk-code-pop 620ms var(--ease-out, cubic-bezier(.22,.61,.36,1)) both; animation-delay: 220ms; }
@keyframes tk-code-pop {
  0%   { opacity: 0; transform: scale(.94); }
  55%  { opacity: 1; transform: scale(1.03); }
  100% { opacity: 1; transform: none; }
}

/* Convert-page tiles: the same staggered entrance the book tiles use, and a
   clear lift for the one chosen on each side. */
.cv-target { transition: transform 140ms var(--ease-out, cubic-bezier(.22,.61,.36,1)), border-color 140ms ease, background-color 140ms ease; }
@media (hover: hover) and (pointer: fine) {
  .cv-target:hover { transform: translateY(-2px); }
}
.book-tile.on { animation: tk-tile-on 300ms var(--ease-out, cubic-bezier(.22,.61,.36,1)); }
@keyframes tk-tile-on {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 50%, transparent); }
  100% { box-shadow: 0 0 0 8px transparent; }
}

/* Convert, while it runs. The bookmaker can take seconds to answer and the
   button is the only thing that can say so. */
.cv-go .btn.is-working { pointer-events: none; opacity: .9; }
.cv-go .btn.is-working::before {
  content: ""; width: 13px; height: 13px; flex: 0 0 auto; margin-right: 2px;
  border-radius: 50%; border: 2px solid currentColor; border-top-color: transparent;
  animation: cv-spin 620ms linear infinite;
}
@keyframes cv-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .tk-fixpanel .tk-opt, .tk-fixpanel .tk-opt.on, .tk-conv-out, .tk-conv-legs > *,
  .tk-conv-out.exact .tk-conv-id, .book-tile.on { animation: none; }
  .cv-target:hover { transform: none; }
  .cv-go .btn.is-working::before { animation-duration: 2s; }
}

/* The reverse-direction hint under a build-list conversion: reading a code
   FROM a bookmaker always works, so a build list is never a dead end. */
.tk-note-aside {
  display: block; margin-top: .5rem; font-size: .82rem; color: var(--muted);
  padding-top: .5rem; border-top: 1px solid var(--line);
}
.tk-note-aside em { color: var(--text); font-style: normal; font-weight: 600; }

/* ---------- convert: target availability ----------
   Once a source is chosen, the target grid shows what each choice means.
   A book that hands back a code stays solid; one that gives only a build list
   fades but stays selectable — the build list is a real conversion. The source
   book itself cannot be a target and is stood down entirely. */
.cv-target.is-buildlist {
  opacity: .5;
  transition: opacity 200ms var(--ease-out, cubic-bezier(.22,.61,.36,1));
}
.cv-target.is-buildlist:hover,
.cv-target.is-buildlist.on { opacity: .82; }
.cv-target.is-self {
  opacity: .28; pointer-events: none; filter: grayscale(1);
  transition: opacity 200ms ease, filter 200ms ease;
}
.cv-target.is-self .book-tick { display: none; }
.cv-hint {
  margin: .5rem 0 0; font-size: .8rem; color: var(--muted);
}
.cv-hint b { color: var(--text); font-weight: 600; }
@media (prefers-reduced-motion: reduce) {
  .cv-target.is-buildlist, .cv-target.is-self { transition: none; }
}

/* ---- the admin shell -----------------------------------------------------
   One menu, on every page, with the current section marked. It is laid out
   as a column beside the content rather than inside it, so no admin page had
   to be restructured: layout_head() already opens <main>, the nav is placed
   into the first grid column spanning every row, and everything the page
   prints afterwards falls into the second column in its original order. */

body[data-admin] main {
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  gap: 0 26px;
  align-items: start;
}
body[data-admin] main > * { grid-column: 2; min-width: 0; }
body[data-admin] main > .admin-nav { grid-column: 1; grid-row: 1 / 500; }
/* The heading belongs over the content, not over the menu. */
body[data-admin] main > .page-head { padding-left: 0; }

.admin-nav {
  position: sticky; top: 84px; align-self: start;
  display: flex; flex-direction: column; gap: 2px;
  padding: 10px; margin-bottom: 18px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
}
.admin-nav-head {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 8px 10px; margin-bottom: 4px;
  border-bottom: 1px solid var(--line);
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
  color: var(--muted);
}
.admin-nav-mark {
  display: inline-grid; place-items: center; width: 24px; height: 24px; border-radius: 7px;
  background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--accent);
}
.admin-nav-group { display: flex; flex-direction: column; gap: 1px; padding: 6px 0 2px; }
.admin-nav-label {
  padding: 2px 8px 4px; font-size: 10.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .08em; color: var(--muted); opacity: .75;
}
.admin-nav-item {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 9px; border-radius: 8px;
  color: var(--text); text-decoration: none; font-size: 13.5px;
  border: 1px solid transparent;
  transition: background-color 140ms ease, border-color 140ms ease, color 140ms ease;
}
.admin-nav-item:hover { background: var(--surface-2); }
.admin-nav-item.on {
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
  border-color: color-mix(in srgb, var(--accent) 42%, transparent);
  font-weight: 600;
}
.admin-nav-item.on .admin-nav-ico { color: var(--accent); }
.admin-nav-ico { flex: none; color: var(--muted); }
.admin-nav-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.admin-nav-badge {
  flex: none; font-family: var(--mono); font-size: 10.5px; font-weight: 700;
  padding: 1px 6px; border-radius: 999px; background: var(--surface-2); color: var(--muted);
}
.admin-nav-badge.warn { background: color-mix(in srgb, #f59e0b 24%, transparent); color: #d97706; }
.admin-nav-foot { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line); }
.admin-nav-item.danger:hover { color: var(--away); background: color-mix(in srgb, var(--away) 10%, transparent); }

/* The page's own controls. Not navigation, so it never shares a row with it. */
.admin-actions {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 14px; margin-bottom: 16px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
}
.admin-actions-lead { font-size: 13px; color: var(--muted); }
.admin-actions-set { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-left: auto; }
.admin-actions form { margin: 0; }

/* Narrow: the menu becomes a scrolling row above the content. A dropdown
   would hide the section you are not on, which is the fault being fixed. */
@media (max-width: 900px) {
  body[data-admin] main { grid-template-columns: minmax(0, 1fr); }
  body[data-admin] main > *, body[data-admin] main > .admin-nav { grid-column: 1; }
  body[data-admin] main > .admin-nav { grid-row: auto; order: -1; }
  .admin-nav {
    position: static; flex-direction: row; align-items: center; gap: 6px;
    overflow-x: auto; padding: 8px 10px; -webkit-overflow-scrolling: touch;
  }
  .admin-nav-head, .admin-nav-label { display: none; }
  .admin-nav-group { flex-direction: row; gap: 6px; padding: 0; }
  .admin-nav-foot { display: flex; gap: 6px; margin: 0 0 0 auto; padding: 0 0 0 8px; border-top: 0; border-left: 1px solid var(--line); }
  .admin-nav-item { white-space: nowrap; }
}

/* ---------- live prediction picture ----------
   Under the live probability bar: the state the re-price is built from — cards
   per side, who is chasing, and whether legs are going. Only rendered live. */
.pr-nowcard.live-on { border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
.pr-livepic {
  display: flex; flex-wrap: wrap; align-items: center; gap: .45rem;
  margin-top: .6rem; font-size: .78rem;
}
.pr-live-cards { display: inline-flex; align-items: center; gap: .35rem; }
.pr-cards { display: inline-flex; gap: .2rem; }
.pr-cards b {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.1rem; height: 1.3rem; border-radius: 3px; font-size: .72rem;
  font-weight: 700; font-family: var(--mono); color: #fff; font-style: normal;
}
.pr-cards .pr-rc { background: var(--away); }         /* red card */
.pr-cards .pr-yc { background: #eab308; color: #3a2e05; }  /* yellow card */
.pr-cards-lbl { font-size: .68rem; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
.pr-press {
  display: inline-flex; align-items: center; gap: .3rem; padding: .12rem .5rem;
  border-radius: 999px; font-weight: 600; font-size: .74rem;
  background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--accent);
}
.pr-press::before { content: "↑"; font-weight: 800; }
.pr-note {
  padding: .12rem .5rem; border-radius: 999px; color: var(--muted);
  background: var(--surface-2); border: 1px solid var(--line); font-size: .72rem;
}

/* ---- admin forms ---------------------------------------------------------
   A settings form, not a stack of bare labels. Each section states what it is
   for on the left and holds its fields on the right, so a long configuration
   form reads as a few decisions rather than twenty boxes. Every field owns its
   label, its hint and its own space; the hint sits under the input it explains
   rather than floating between two of them, which is where a paragraph after a
   label ends up belonging to whichever field the reader guesses. */

.fx { padding: 0; overflow: hidden; }
.fx-section {
  display: grid; grid-template-columns: 210px minmax(0, 1fr);
  gap: 8px 32px; padding: 20px 22px; border-bottom: 1px solid var(--line);
}
.fx-section:last-of-type { border-bottom: 0; }
@media (max-width: 820px) { .fx-section { grid-template-columns: minmax(0, 1fr); gap: 12px; } }
.fx-legend h3 {
  margin: 0 0 4px; font-size: 13.5px; font-weight: 650; letter-spacing: -0.01em;
}
.fx-legend p { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--muted); }
.fx-legend code { font-family: var(--mono); font-size: 11.5px; }

.fx-fields { display: grid; grid-template-columns: repeat(12, 1fr); gap: 14px 16px; align-items: start; }
.fx-field { grid-column: span 12; display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.fx-field.s3 { grid-column: span 3; }
.fx-field.s4 { grid-column: span 4; }
.fx-field.s6 { grid-column: span 6; }
@media (max-width: 620px) { .fx-field.s3, .fx-field.s4, .fx-field.s6 { grid-column: span 12; } }

.fx-field > label {
  display: flex; align-items: baseline; gap: 6px;
  font-size: 11.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted);
}
.fx-req { font-size: 9.5px; letter-spacing: .04em; color: var(--accent); font-weight: 700; }

/* Width only. Everything else is the field system; .fx sets --fld-fs there. */
.fx input[type=text], .fx input[type=url], .fx input[type=number],
.fx input:not([type]), .fx select, .fx textarea { width: 100%; }
/* Anything that is code is shown as code — an endpoint, a JSON body, a dot
   path, a regular expression. Proportional type hides the difference between
   a dot and a comma in exactly the fields where it decides whether the
   bookmaker can be read at all. */
.fx .mono, .fx textarea { font-family: var(--mono); font-size: 12.5px; letter-spacing: -0.01em; }
.fx textarea { line-height: 1.55; resize: vertical; min-height: 4.6em; }
/* The chevron here used to be two gradients faking a triangle. It now comes
   from the field system at the foot of this file, along with every other
   select on the site. */
.fx-hint { font-size: 11.5px; line-height: 1.45; color: var(--muted); }
.fx-hint code { font-family: var(--mono); font-size: 11px;
  background: var(--surface-2); border-radius: 4px; padding: 0 4px; }

/* A switch, because "show this publicly" is a state rather than a checkbox in
   a list of them. */
.fx-switch { display: flex; align-items: flex-start; gap: 11px; cursor: pointer; }
.fx-switch input { position: absolute; opacity: 0; pointer-events: none; }
.fx-switch .fx-track {
  flex: none; width: 40px; height: 23px; border-radius: 999px; margin-top: 1px;
  background: var(--surface-2); border: 1px solid var(--line); position: relative;
  transition: background-color 160ms ease, border-color 160ms ease;
}
.fx-switch .fx-track::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 17px; height: 17px;
  border-radius: 50%; background: var(--muted);
  transition: transform 160ms var(--ease-out), background-color 160ms ease;
}
.fx-switch input:checked + .fx-track { background: color-mix(in srgb, var(--accent) 30%, transparent); border-color: var(--accent); }
.fx-switch input:checked + .fx-track::after { transform: translateX(17px); background: var(--accent); }
.fx-switch input:focus-visible + .fx-track { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent); }
.fx-switch-text b { display: block; font-size: 13.5px; font-weight: 600; }
.fx-switch-text span { font-size: 11.5px; color: var(--muted); line-height: 1.45; }

.fx-foot {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 16px 22px; background: var(--surface-2); border-top: 1px solid var(--line);
}
.fx-foot .fx-foot-note { margin-left: auto; font-size: 11.5px; color: var(--muted); }

@media (prefers-reduced-motion: reduce) {
  .fx-switch .fx-track::after { transition: none; }
}

/* A destructive button has to look destructive and stay readable. `.btn` sets
   a solid accent background, so `.danger` had been red text on green — the one
   combination that says "primary action" and "danger" at the same time and
   reads as neither. It is now a quiet red until you are over it, and solid red
   when you are. */
.btn.danger, .btn.ghost.danger {
  background: color-mix(in srgb, var(--away) 14%, transparent);
  color: var(--away);
  border: 1px solid color-mix(in srgb, var(--away) 34%, transparent);
}
.btn.danger:hover, .btn.ghost.danger:hover {
  background: var(--away); color: #fff; border-color: var(--away); filter: none;
}

/* The switch is a <label> too, so the uppercase field-label rule caught its
   text and shouted a whole paragraph. It carries its own type. */
.fx-field > label.fx-switch {
  text-transform: none; letter-spacing: normal;
  font-size: 13.5px; font-weight: 400; color: var(--text);
}
.fx-switch .fx-switch-text span { text-transform: none; letter-spacing: normal; }

/* ---- every remaining admin field ----------------------------------------
   The forms above were rebuilt section by section. These are the ones that
   are a single box and a button — a bulk paste, a search, a badge URL in a
   table row — and rewriting their markup would buy nothing. What they needed
   was the same input: the same height, the same radius, the same focus ring.
   A panel where half the fields answer to one style and half to another looks
   unfinished however good either half is. */
/* The admin's field sizing is two lines in the field system (--fld-py and
   --fld-fs on body[data-admin], one size down again inside a table). What was
   here was a second copy of the chrome, and it had already drifted from the
   copy in .fx above it. */
body[data-admin] textarea { font-family: var(--mono); font-size: 12.5px; line-height: 1.55; resize: vertical; }
/* The section headings a page prints between its forms. */
body[data-admin] main h2 { margin: 30px 0 12px; }
body[data-admin] main h2:first-of-type { margin-top: 6px; }

/* The old grid form, kept for the pages still using it (the stream-feed
   editor) so it sits at the same rhythm as the rebuilt ones. */
body[data-admin] .pv-grid .ctl .lbl, body[data-admin] .ctl .lbl {
  font-size: 11.5px; font-weight: 600; letter-spacing: .05em; margin-bottom: 5px;
}

/* ---------- admin: the on-air console ----------
   A row per match, with everything needed to put it on air on the same line.
   Matches with no channel are marked, because they are the only reason to be
   on this page. */
.onair-list { padding: 0; }
.onair-row {
  display: grid;
  grid-template-columns: 92px minmax(190px, 1.4fr) auto minmax(330px, 1.6fr) auto;
  gap: 14px; align-items: center;
  padding: 10px 14px;
  border-bottom: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
}
.onair-row:last-child { border-bottom: 0; }
.onair-row.needs { background: color-mix(in srgb, #c2410c 6%, transparent); }
.oa-when { display: flex; flex-direction: column; gap: 2px; font-size: 12.5px; }
.oa-when .clock { font-weight: 650; font-variant-numeric: tabular-nums; }
.oa-when .dot {
  width: 7px; height: 7px; border-radius: 50%; background: #16a34a;
  display: inline-block; margin-left: 5px;
  box-shadow: 0 0 0 3px color-mix(in srgb, #16a34a 25%, transparent);
}
.oa-score { color: var(--muted); font-variant-numeric: tabular-nums; }
.oa-match a { text-decoration: none; font-size: 13.5px; display: block; }
.oa-match a:hover { text-decoration: underline; }
.oa-match .sub { font-size: 11.5px; color: var(--muted); }
.oa-state { display: flex; gap: 5px; flex-wrap: wrap; }
.badge.warnbadge {
  background: color-mix(in srgb, #c2410c 15%, transparent);
  color: #c2410c; border-color: color-mix(in srgb, #c2410c 40%, transparent);
}
.oa-add { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }

.oa-add input[type=url] { flex: 1 1 150px; min-width: 130px; }
.oa-go { display: flex; gap: 6px; align-items: center; white-space: nowrap; }

@media (max-width: 1100px) {
  .onair-row { grid-template-columns: 80px 1fr; grid-auto-flow: row; }
  .oa-state, .oa-add, .oa-go { grid-column: 1 / -1; }
}

/* ---- the radar table -----------------------------------------------------
   Fixed layout, because the fault being fixed is columns fighting over width.
   Fourteen auto-width columns meant the browser gave a fixture two club names'
   worth of space and a percentage the same, so the fixture wrapped into three
   lines and the numbers wrapped into two, and the rows read as a collision.
   Here every column is told what it gets, and the facts that qualify a number
   sit under it as a second line instead of claiming a column of their own. */

.rd-wrap { padding: 0; }
.rd { width: 100%; table-layout: fixed; border-collapse: collapse; }
.rd th, .rd td {
  padding: 11px 12px; vertical-align: top; border-bottom: 1px solid var(--line);
}
.rd thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--surface); border-bottom: 1px solid var(--line);
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); white-space: nowrap; vertical-align: middle;
}
.rd tbody tr:last-child td { border-bottom: 0; }
.rd tbody tr:hover { background: color-mix(in srgb, var(--line) 26%, transparent); }

.rd-add  { width: 36px; text-align: center; vertical-align: middle; }
.rd-n    { width: 34px; text-align: right; color: var(--muted);
           font-family: var(--mono); font-size: 12px; }
/* The fixture gets the largest share because it carries the most words: two
   club names, a kick-off and a league. Starving it was what produced
   "Borussia Dor…" beside a column of whitespace. */
.rd-fx   { width: 34%; }
.rd-pick { width: 21%; }
.rd-ch   { width: 116px; }
.rd-edge { width: 118px; }
.rd-odds { width: 78px; }
.rd-ev   { width: 104px; }

/* The second line under any value: what qualifies it, never a separate column. */
.rd-meta {
  display: block; margin-top: 3px;
  font-size: 11.5px; line-height: 1.4; color: var(--muted);
}
/* In a numeric column the qualifier is one item per line, deliberately. Left
   to wrap it broke "usually 20.8%" across three lines in some rows and one in
   others, and a column that changes height per row reads as broken. */
.rd .num .rd-meta { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; }
.rd .num .rd-meta > span { white-space: nowrap; }
.rd .num { text-align: right; }
.rd .num b {
  display: block; font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 15px; font-weight: 700; letter-spacing: -0.02em; line-height: 1.2;
}
.rd-none { color: var(--muted); }

/* The fixture. One line for the two clubs, one for when and where — and the
   club names truncate rather than wrapping the row open. */
.rd-teams { display: flex; align-items: center; gap: 6px; min-width: 0; }
/* Both clubs share the space evenly rather than shrinking from their natural
   width, so a long name and a short one do not leave one truncated beside the
   other's slack. */
.rd-teams .teamchip { min-width: 0; flex: 1 1 0; }
.rd-teams .teamchip > span {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13.5px;
}
.rd-teams a.vs {
  flex: none; color: var(--muted); text-decoration: none;
  font-family: var(--mono); font-size: 11.5px; padding: 0 2px;
}
.rd-teams a.vs:hover { color: var(--text); }
.rd-fx .rd-meta { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; }
.rd-fx .rd-meta .compmark { margin: 0; }
.rd-dot { opacity: .55; }

.rd-pick b { font-size: 13.5px; font-weight: 600; line-height: 1.35; display: block; }

/* Edge: the number, a bar to scan the column by, and the lift it implies. */
.rd-edge b.up { color: var(--accent); }
.rd-edge b.down { color: var(--muted); }
.rd-bar {
  display: block; height: 3px; border-radius: 999px; margin: 5px 0 0 auto; width: 100%;
  background: var(--surface-2); overflow: hidden;
}
.rd-bar i { display: block; height: 100%; border-radius: 999px; background: var(--accent); }
.rd-edge b.down + .rd-bar i { background: var(--muted); }

.rd-2nd {
  display: inline-block; margin-top: 4px; padding: 1px 6px; border-radius: 999px;
  font-family: var(--mono); font-size: 10.5px; font-weight: 700; white-space: nowrap;
}
.rd-2nd.agree { background: color-mix(in srgb, var(--accent) 20%, transparent); color: var(--accent); }
.rd-2nd.differ { background: var(--surface-2); color: var(--muted); }

.rd .slipbox { margin: 2px 0 0; }

/* Narrow screens drop the columns that qualify rather than decide. The card
   still scrolls, but it no longer has to scroll to reach the pick itself. */
@media (max-width: 1100px) {
  .rd-edge, .rd th.rd-edge { display: none; }
  .rd-pick { width: 30%; }
}
@media (max-width: 860px) {
  .rd-ev, .rd th.rd-ev, .rd-n, .rd th.rd-n { display: none; }
  .rd th, .rd td { padding: 10px 9px; }
  .rd-fx { min-width: 200px; }
}

/* ---------- search: leagues & areas ----------
   A league is a place, not a fixture, so it gets a single compact row above the
   club cards, under a small group label. The flag stands in for a crest. */
.ts-group {
  font-size: .68rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted); padding: .5rem .2rem .3rem; margin-top: .2rem;
}
.ts-group:first-child { margin-top: 0; }
.ts-arearow .ts-area-flag { object-fit: cover; border-radius: 3px; }
.ts-arearow { border-radius: 8px; }
.ts-arearow:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }

/* ---------- competition colours on the match page ----------
   A tint, not a repaint. The band behind the scoreline takes the competition's
   own colour; everything below it keeps the site's contrast, because a match
   page still has to be read. --comp-dark/-light/-accent are set inline by
   competition_theme_style(), so a new competition needs no CSS. */
.matchhero { position: relative; overflow: hidden; }
.matchhero.has-comp-theme {
  border-color: color-mix(in srgb, var(--comp-accent) 34%, var(--line));
  background:
    radial-gradient(120% 140% at 50% -30%,
      color-mix(in srgb, var(--comp-light) 92%, transparent) 0%,
      color-mix(in srgb, var(--comp-dark) 70%, transparent) 45%,
      transparent 78%),
    var(--surface);
}
/* On a light theme the same colours would swamp the text, so they are held
   back. Two rules rather than one: an @media cannot sit inside a selector
   list, and written that way the whole block is discarded. */
:root[data-theme="light"] .matchhero.has-comp-theme {
  background:
    radial-gradient(120% 140% at 50% -30%,
      color-mix(in srgb, var(--comp-accent) 16%, transparent) 0%,
      color-mix(in srgb, var(--comp-accent) 7%, transparent) 45%,
      transparent 78%),
    var(--surface);
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .matchhero.has-comp-theme {
    background:
      radial-gradient(120% 140% at 50% -30%,
        color-mix(in srgb, var(--comp-accent) 16%, transparent) 0%,
        color-mix(in srgb, var(--comp-accent) 7%, transparent) 45%,
        transparent 78%),
      var(--surface);
  }
}
/* a hairline of the competition's colour along the top */
.matchhero.has-comp-theme::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: linear-gradient(90deg, transparent,
              var(--comp-accent) 22%, var(--comp-accent) 78%, transparent);
}
.matchhero-comp { margin: 0 0 6px; text-align: center; }
.matchhero.has-comp-theme .comp-name {
  background: color-mix(in srgb, var(--comp-accent) 20%, transparent);
  color: var(--comp-accent);
  border: 1px solid color-mix(in srgb, var(--comp-accent) 40%, transparent);
  font-weight: 600; letter-spacing: .02em;
}
.matchhero.has-comp-theme .scoreline .big { color: var(--text); }
.matchhero.has-comp-theme .sideteam span { font-weight: 650; }

@media (prefers-reduced-motion: no-preference) {
  .matchhero.has-comp-theme { animation: heroIn .3s cubic-bezier(.2,.7,.3,1) both; }
  @keyframes heroIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
}

/* ---------- radar: Get picks, and the picks dialog ---------- */
.getpicks { position: relative; display: inline-flex; align-items: center; gap: 8px; }
.getpicks .gp-spin {
  width: 0; height: 13px; border-radius: 50%; opacity: 0;
  border: 2px solid color-mix(in srgb, currentColor 30%, transparent);
  border-top-color: currentColor;
  transition: width .15s ease, opacity .15s ease;
}
/* The label slides rather than being replaced, so the button keeps its width
   and the row below it does not jump while a request is in flight. */
.getpicks.is-busy .gp-spin { width: 13px; opacity: 1; animation: gp-spin .6s linear infinite; }
.getpicks.is-busy { cursor: progress; }
.getpicks:not(:disabled):active { transform: translateY(1px); }
.getpicks:not(:disabled):hover { filter: brightness(1.06); }
@keyframes gp-spin { to { transform: rotate(360deg); } }
/* A press should be felt even when the answer is instant. */
@media (prefers-reduced-motion: reduce) {
  .getpicks .gp-spin { animation: none; }
  .getpicks:not(:disabled):active { transform: none; }
}

.pk-modal {
  width: min(1040px, calc(100vw - 32px));
  max-height: min(86vh, 900px);
  padding: 0; border: 1px solid var(--line); border-radius: 12px;
  background: var(--surface); color: var(--text);
  box-shadow: 0 24px 64px rgba(0, 0, 0, .38);
  opacity: 0; transform: translateY(14px) scale(.985);
  /* A slight overshoot on the way in, none on the way out: the dialog lands
     rather than fades, and closing gets out of the way without ceremony. */
  transition: opacity .22s ease, transform .34s cubic-bezier(.2, .9, .3, 1.12);
}
.pk-modal.is-open { opacity: 1; transform: none; }
.pk-modal:not(.is-open) { transition-duration: .16s, .16s; transition-timing-function: ease, ease; }
.pk-modal::backdrop {
  background: rgba(6, 8, 12, .55);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  opacity: 0; transition: opacity .25s ease;
}
.pk-modal.is-open::backdrop { opacity: 1; }

/* The rows arrive one after another, top first, each a short rise into
   place — fast enough that twenty picks are all still within half a second,
   so nothing is waited for. --i is set by the script that injects them. */
.pk-body tr { animation: rd-in .38s cubic-bezier(.2, .8, .2, 1) both; animation-delay: calc(min(var(--i, 0), 14) * 36ms); }
@keyframes rd-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
/* A ticked pick reads as ticked from across the room: the row takes an
   accent tint and a left rule, the box itself pops. A clashing row keeps its
   own warning colour — that is the more important thing to see. */
.rd tbody tr { transition: background-color .2s ease, box-shadow .2s ease; }
.rd tbody tr:not(.conflict):has(.slipbox:checked) {
  background: color-mix(in srgb, var(--accent) 9%, transparent);
  box-shadow: inset 3px 0 0 var(--accent);
}
.slipbox { accent-color: var(--accent); transition: transform .18s cubic-bezier(.3, 1.5, .5, 1); }
.slipbox:checked { transform: scale(1.12); }
/* The selected count nudges up as it changes, so the eye is told where the
   tick went without looking for it. */
#pkCount, #slipCount { display: inline-block; }
#pkCount.pop, #slipCount.pop { animation: count-pop .32s ease; }
@keyframes count-pop {
  40% { transform: scale(1.4); color: var(--accent); }
  100% { transform: none; }
}
/* A slow sheen across an enabled Build slip button — the one thing on the
   sheet that goes somewhere. Never on a disabled one. */
.pk-tools .slipgo, .slipbar .slipgo { position: relative; overflow: hidden; }
.pk-tools .slipgo:not(:disabled)::after, .slipbar .slipgo:not(:disabled)::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(110deg, transparent 35%, rgba(255, 255, 255, .32) 50%, transparent 65%);
  transform: translateX(-130%); animation: slip-sheen 3.2s ease-in-out infinite;
}
@keyframes slip-sheen { 0%, 55% { transform: translateX(-130%); } 100% { transform: translateX(130%); } }
.pk-tools .slipgo:not(:disabled):active { transform: scale(.98); }
.pk-head, .pk-tools {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 16px; border-bottom: 1px solid var(--line);
}
.pk-head .sub { display: block; font-size: 12.5px; color: var(--muted); }
/* The close control belongs in the top-right corner, where every dialog on
   every platform puts it and where a hand reaches without looking. The header
   is a flex row and nothing pushed its last child to the edge, so the ✕ sat
   mid-header right after the title on both the Picks and Bet slip modals — an
   easy miss on a phone. margin-left:auto sends the last child (the ✕, or the
   group holding "Open full page" + ✕) to the far right; align-self keeps it
   pinned to the top when the title wraps to two lines. */
.pk-head > :last-child { margin-left: auto; align-self: flex-start; display: inline-flex; align-items: center; gap: 8px; }
.pk-modal .ts-close {
  width: 34px; height: 34px; padding: 0; border-radius: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 24px; margin: -4px -6px 0 0;
}
.pk-modal .ts-close:hover { background: var(--surface-2); color: var(--text); }
.pk-tools .spacer { flex: 1 1 auto; }
.pk-body { max-height: calc(86vh - 132px); overflow: auto; }
.pk-body table { width: 100%; }
@media (prefers-reduced-motion: reduce) {
  .pk-modal, .pk-modal::backdrop, .rd tbody tr, .slipbox { transition: none; }
  .pk-body tr, #pkCount.pop, #slipCount.pop { animation: none; }
  .pk-tools .slipgo::after, .slipbar .slipgo::after { display: none; }
}

/* ================= Aider =================================================
   The assistant in the top-right corner.

   The whole visual argument here is "this thing is awake". A static input in a
   header is furniture and reads as one more filter; the sweep around the
   border and the pulse on the avatar are what say there is something on the
   other end of it. That is also why the effect is restrained: a betting site
   with a glowing box has to look competent, not like a slot machine, so the
   glow is one accent-coloured arc travelling a hairline border rather than a
   coloured halo.

   Every animation in this block is switched off under prefers-reduced-motion
   at the bottom of the section. The typewriter especially — it is exactly the
   effect that makes some readers ill, and the answer must still be readable. */

/* Animating a conic gradient needs the angle to be a real typed custom
   property; without @property it is a string and jumps rather than sweeps.
   Where the browser has no support the arc simply sits still, which is a
   perfectly good border. */
@property --aider-ang { syntax: '<angle>'; initial-value: 0deg; inherits: false; }
@keyframes aider-sweep { to { --aider-ang: 360deg; } }
@keyframes aider-ping  { 0% { transform: scale(1); opacity: .85; }
                        70% { transform: scale(2.6); opacity: 0; }
                       100% { transform: scale(2.6); opacity: 0; } }
@keyframes aider-breathe { 0%, 100% { transform: translateY(0); }
                            50%      { transform: translateY(-1px); } }
@keyframes aider-shine { 0%   { transform: translateX(-120%); }
                         60%  { transform: translateX(220%); }
                         100% { transform: translateX(220%); } }
@keyframes aider-twinkle { 0%, 100% { opacity: .55; transform: scale(.9) rotate(0deg); }
                           50%      { opacity: 1;   transform: scale(1.1) rotate(12deg); } }
@keyframes aider-rise { from { opacity: 0; transform: translateY(8px); }
                          to { opacity: 1; transform: none; } }
@keyframes aider-dot  { 0%, 80%, 100% { transform: translateY(0); opacity: .35; }
                        40%           { transform: translateY(-4px); opacity: 1; } }
@keyframes aider-caret { 0%, 45% { opacity: 1; } 55%, 100% { opacity: 0; } }

/* ---- the launcher ---- */
.aider-launch {
  position: relative; isolation: isolate; flex: none;
  display: inline-flex; align-items: center; gap: 7px;
  margin-left: 10px; padding: 4px 6px 4px 5px;
  border-radius: 999px; background: var(--surface-2);
  border: 1px solid var(--line);
  transition: border-color .18s ease, background .18s ease;
  /* Keep the shine inside the pill.
     `::after` below sweeps with translateX(220%) — about 500px to the right of
     a 235px pill. Nothing was clipping it, so on every pass it widened the
     document, the browser grew a horizontal scrollbar, and the whole page
     shifted in time with the animation. The `overflow: hidden` on the
     pseudo-element itself did nothing about this: that clips its children, not
     the element within its parent.
     `clip` rather than `hidden` because this must not become a scroll
     container, and a 6px margin so the two effects that are *meant* to sit
     slightly proud — the border arc at inset -1px, and the presence dot, which
     scales to 2.6 — are not cropped. */
  overflow: clip;
  overflow-clip-margin: 6px;
}
/* The travelling arc. A padding-box/border-box mask pair cuts the middle out
   of the gradient, leaving only the hairline — cheaper and crisper than
   stacking a blurred copy behind an opaque pill. */
.aider-glow {
  position: absolute; inset: -1px; border-radius: inherit; padding: 1.2px;
  pointer-events: none;
  background: conic-gradient(from var(--aider-ang),
              transparent 0 58%,
              color-mix(in srgb, var(--accent) 70%, transparent) 72%,
              var(--accent) 80%,
              color-mix(in srgb, var(--home) 80%, transparent) 88%,
              transparent 96%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  animation: aider-sweep 4.5s linear infinite;
}
/* A single pass of light across the pill, slow enough to be noticed once and
   then ignored. */
.aider-launch::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  overflow: hidden; pointer-events: none;
  background: linear-gradient(100deg, transparent 30%,
              color-mix(in srgb, var(--accent) 16%, transparent) 50%, transparent 70%);
  animation: aider-shine 6s ease-in-out infinite;
  mix-blend-mode: screen;
}
.aider-launch:focus-within { border-color: var(--accent); background: var(--surface); }

.aider-face {
  position: relative; z-index: 1; flex: none;
  width: 24px; height: 24px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--accent) 16%, var(--surface));
  color: var(--accent);
  animation: aider-breathe 3.2s ease-in-out infinite;
}
.aider-face svg { width: 15px; height: 15px; display: block; }
.aider-face-sm { width: 26px; height: 26px; }
.aider-face-sm svg { width: 16px; height: 16px; }
/* The "I am listening" dot: a live indicator, so it is a real state and not
   decoration — it sits on the avatar, where a presence dot belongs. */
.aider-ping {
  position: absolute; right: -1px; bottom: -1px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 0 2px var(--surface);
}
.aider-ping::after {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  background: var(--accent); animation: aider-ping 2.4s ease-out infinite;
}
.aider-input {
  position: relative; z-index: 1;
  width: 168px; border: 0; background: transparent; outline: none;
  color: var(--text); font: inherit; font-size: 13px; padding: 3px 0;
  transition: width .22s ease;
}
.aider-input::placeholder { color: var(--muted); }
.aider-launch:focus-within .aider-input { width: 230px; }
.aider-spark {
  position: relative; z-index: 1; flex: none;
  width: 16px; height: 16px; color: var(--accent);
  animation: aider-twinkle 2.8s ease-in-out infinite;
}
.aider-spark svg { width: 100%; height: 100%; display: block; }

@media (max-width: 1180px) {
  .aider-input { width: 96px; }
  .aider-launch:focus-within .aider-input { width: 150px; }
}
@media (max-width: 900px) {
  /* Down here the pill is a button: the field collapses and the avatar is the
     whole target, because 90px of input is not enough to write a question in
     and pretending otherwise is worse than opening the panel to do it. */
  .aider-input { width: 0; padding: 0; }
  .aider-launch:focus-within .aider-input { width: 0; }
  .aider-launch { gap: 4px; cursor: pointer; }
}

/* ---- the panel ---- */
.aider-modal {
  width: min(720px, calc(100vw - 24px));
  height: min(78vh, 760px);
  padding: 0; border: 1px solid var(--line); border-radius: 16px;
  background: var(--surface); color: var(--text);
  box-shadow: 0 24px 70px rgba(0, 0, 0, .42);
  flex-direction: column; overflow: hidden;
  opacity: 0; transform: translateY(10px) scale(.985);
  transition: opacity .2s ease, transform .2s cubic-bezier(.2, .8, .3, 1);
}
/* display goes on [open] and nowhere else.
   A closed <dialog> is hidden by `dialog:not([open]) { display: none }` in the
   browser's own stylesheet — but author rules beat the user-agent origin
   whatever their specificity, so a bare `display: flex` here un-hid it. The
   panel was then laid out in normal flow at min(78vh, 760px), invisible at
   opacity 0, and every page carried most of a blank screen below its footer.
   The search modal above never sets display, which is why it never did this. */
.aider-modal[open] { display: flex; }
.aider-modal.is-open { opacity: 1; transform: none; }
.aider-modal::backdrop { background: rgba(6, 8, 12, .55); backdrop-filter: blur(3px); }

.aider-head {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px; border-bottom: 1px solid var(--line);
  background: var(--surface-2); flex: none;
}
.aider-avatar {
  position: relative; flex: none;
  width: 32px; height: 32px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--accent) 18%, var(--surface));
  color: var(--accent);
  animation: aider-breathe 3.2s ease-in-out infinite;
}
.aider-avatar svg { width: 19px; height: 19px; display: block; }
.aider-avatar-xs { width: 26px; height: 26px; align-self: flex-start; margin-top: 2px; }
.aider-avatar-xs svg { width: 15px; height: 15px; }
.aider-id { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.aider-id strong { font-size: 14px; letter-spacing: -0.01em; }
.aider-id span { font-size: 11.5px; color: var(--muted); }
.aider-radar {
  margin-left: auto; font-size: 12px; color: var(--muted);
  text-decoration: none; border-bottom: 1px dotted var(--line); white-space: nowrap;
}
.aider-radar:hover { color: var(--accent); border-color: var(--accent); }
.aider-close {
  flex: none; width: 28px; height: 28px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--muted); font-size: 17px; line-height: 1; cursor: pointer;
}
.aider-close:hover { color: var(--text); border-color: var(--accent); }

.aider-thread {
  flex: 1 1 auto; overflow-y: auto; padding: 16px 14px 8px;
  display: flex; flex-direction: column; gap: 14px;
  scroll-behavior: smooth;
}
.aider-msg { display: flex; gap: 9px; max-width: 100%; animation: aider-rise .26s ease both; }
.aider-msg .aider-body { min-width: 0; }
.aider-you { justify-content: flex-end; }
.aider-you .aider-body {
  background: color-mix(in srgb, var(--accent) 14%, var(--surface-2));
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--line));
  border-radius: 14px 14px 4px 14px; padding: 8px 12px; max-width: 78%;
}
.aider-you p { margin: 0; font-size: 14px; }
.aider-ai .aider-body {
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: 14px 14px 14px 4px; padding: 11px 13px; max-width: calc(100% - 36px);
}
.aider-say { margin: 0; font-size: 14px; line-height: 1.55; }
.aider-say.is-typing::after {
  content: '▍'; margin-left: 1px; color: var(--accent);
  animation: aider-caret 1s steps(1) infinite;
}
.aider-note {
  margin: 9px 0 0; font-size: 12.5px; color: var(--muted);
  padding-left: 9px; border-left: 2px solid var(--line);
}
.aider-signoff {
  margin: 11px 0 0; padding-top: 9px; border-top: 1px dashed var(--line);
  font-size: 11.5px; line-height: 1.5; color: var(--muted);
}

/* ---- the thinking phase ---- */
.aider-msg.is-thinking .aider-body { display: flex; align-items: center; gap: 9px; }
.aider-dots { display: inline-flex; gap: 3px; flex: none; }
.aider-dots i {
  width: 5px; height: 5px; border-radius: 50%; background: var(--accent);
  animation: aider-dot 1.1s ease-in-out infinite;
}
.aider-dots i:nth-child(2) { animation-delay: .15s; }
.aider-dots i:nth-child(3) { animation-delay: .3s; }
.aider-think-line {
  font-size: 13px; color: var(--muted);
  transition: opacity .16s ease, transform .16s ease;
}
.aider-think-line.is-out { opacity: 0; transform: translateY(-3px); }

/* ---- the answer ---- */
.aider-picks { display: grid; gap: 8px; margin-top: 11px; }
.aider-pick {
  display: block; text-decoration: none; color: inherit;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--surface); padding: 9px 11px;
  animation: aider-rise .3s ease both;
  animation-delay: calc(var(--i, 0) * 45ms);
  transition: border-color .14s ease, transform .14s ease;
}
.aider-pick:hover { border-color: var(--accent); transform: translateX(2px); }
.ap-top { display: flex; align-items: baseline; gap: 10px; }
.ap-fixture { font-size: 13px; font-weight: 600; flex: 1 1 auto; min-width: 0; }
.ap-odds {
  flex: none; font-family: var(--mono); font-size: 14px;
  font-weight: 700; color: var(--accent);
}
.ap-label { font-size: 13.5px; margin-top: 1px; }
.ap-meta {
  display: flex; flex-wrap: wrap; gap: 4px 9px; margin-top: 4px;
  font-size: 11.5px; color: var(--muted);
}
.ap-pct { font-family: var(--mono); color: var(--text); font-weight: 600; }
.ap-track { margin-top: 5px; font-size: 11px; color: var(--muted); line-height: 1.45; }
.ap-untested { color: var(--away); }
.ap-bar {
  height: 3px; margin-top: 7px; border-radius: 2px;
  background: var(--surface-2); overflow: hidden;
}
.ap-bar i { display: block; height: 100%; background: var(--accent); }
.aider-acca {
  margin: 10px 0 0; font-size: 12px; line-height: 1.5;
  padding: 8px 10px; border-radius: 8px;
  background: color-mix(in srgb, var(--accent) 8%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 22%, var(--line));
}
.aider-more {
  display: inline-block; margin-top: 9px; font-size: 12.5px;
  color: var(--accent); text-decoration: none; border-bottom: 1px solid transparent;
}
.aider-more:hover { border-color: var(--accent); }
/* ---- taking the answer to a bookmaker ----
   The one action an answer leads to, so it is the one filled control in the
   bubble. Type is `button`, not `submit`, so the global submit rule near the
   top of this file cannot pad it out of shape the way it once did to the send
   arrow; padding and margin are still set explicitly in case that changes. */
.aider-acts {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px;
  margin-top: 12px;
}
.aider-acts .aider-slip {
  font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
  padding: 8px 15px; margin: 0; border-radius: 999px; border: 0;
  background: var(--accent); color: #0b0d11;
  transition: filter .15s ease, transform .15s ease;
}
.aider-acts .aider-slip:hover { filter: brightness(1.08); transform: translateY(-1px); }
.aider-slip-alt {
  font-size: 12.5px; color: var(--accent); text-decoration: none;
  border-bottom: 1px solid transparent;
}
.aider-slip-alt:hover { border-color: var(--accent); }
.aider-acts-sub { flex: 1 0 100%; font-size: 11.5px; color: var(--muted); line-height: 1.45; }
/* The slip sits above the thread in the top layer; keep it the site's slip. */
.aider-slip-modal .pk-body.is-busy { opacity: .55; pointer-events: none; }

.aider-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 11px; }
.aider-chip {
  font: inherit; font-size: 12px; cursor: pointer;
  padding: 5px 10px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface); color: var(--muted);
  transition: border-color .14s ease, color .14s ease;
}
.aider-chip:hover { border-color: var(--accent); color: var(--text); }

/* ---- the panel's own input ---- */
.aider-ask {
  position: relative; isolation: isolate; flex: none;
  display: flex; align-items: center; gap: 9px;
  margin: 8px 12px 12px; padding: 7px 8px 7px 7px;
  border-radius: 999px; background: var(--surface-2); border: 1px solid var(--line);
}
.aider-ask:focus-within { border-color: var(--accent); }
.aider-ask .aider-input { flex: 1 1 auto; width: auto; font-size: 14px; }
.aider-ask:focus-within .aider-input { width: auto; }
/* Selected as `.aider-ask .aider-send`, and the extra class is load-bearing.
   The global `button[type=submit]` rule near the top of this file sets
   `padding: 9px 20px`, and an attribute selector outranks a single class — so
   it won, and with border-box sizing 40px of horizontal padding on a 30px
   circle left the arrow zero pixels wide. The button looked like a blank green
   dot. Padding and margin are reset explicitly rather than merely overridden
   in case that rule grows. */
.aider-ask .aider-send {
  flex: none; width: 30px; height: 30px; padding: 0; margin: 0;
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; cursor: pointer; position: relative; z-index: 1;
  background: var(--accent); color: var(--bg);
  transition: opacity .15s ease, transform .15s ease;
}
.aider-ask .aider-send svg { width: 16px; height: 16px; display: block; flex: none; }
.aider-ask .aider-send:hover { transform: scale(1.06); filter: brightness(1.08); }
.aider-ask.is-busy { opacity: .6; pointer-events: none; }
.aider-ask.is-busy .aider-glow { animation-duration: 1.1s; }

/* ---- while you are typing ----
   The travelling arc is an idle animation: it exists to say "there is
   something here, ask it". Once you are actually typing that job is done, and
   a border crawling round the field you are writing in is just movement in the
   corner of your eye. So the stroke goes and the field lifts instead — a
   lighter ground reads as "active" without moving. Mixing in white rather than
   naming a colour keeps it correct in both themes: it lightens #1e222b and it
   lightens #f0f2f5. */
.aider-ask, .aider-launch { transition: background .18s ease, border-color .18s ease; }
.aider-glow { transition: opacity .18s ease; }
.aider-ask:focus-within .aider-glow,
.aider-launch:focus-within .aider-glow { opacity: 0; }
.aider-ask:focus-within,
.aider-launch:focus-within {
  background: color-mix(in srgb, #fff 9%, var(--surface-2));
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
}
/* The shine is part of the same idle layer and goes with it. */
.aider-launch:focus-within::after { animation: none; opacity: 0; }

/* ---- voice ---- */
.aider-speak {
  flex: none; width: 28px; height: 28px; padding: 0; border-radius: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--muted); cursor: pointer;
  transition: color .14s ease, border-color .14s ease;
}
.aider-speak svg { width: 15px; height: 15px; display: block; }
.aider-speak .wave1, .aider-speak .wave2 { opacity: .25; }
.aider-speak.is-on { color: var(--accent); border-color: var(--accent); }
.aider-speak.is-on .wave1 { opacity: 1; }
.aider-speak.is-on .wave2 { opacity: .65; }
.aider-speak:hover { color: var(--text); }

.aider-ask .aider-mic {
  position: relative; z-index: 1; flex: none;
  width: 28px; height: 28px; padding: 0; margin: 0; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); background: transparent;
  color: var(--muted); cursor: pointer;
  transition: color .14s ease, border-color .14s ease, background .14s ease;
}
.aider-ask .aider-mic svg { width: 15px; height: 15px; display: block; }
.aider-ask .aider-mic:hover { color: var(--text); border-color: var(--accent); }
.aider-ask .aider-mic.is-live {
  color: var(--bg); background: var(--away); border-color: var(--away);
}
/* A recording indicator has to be unmistakable, so this is the one pulse in
   the panel that keeps going: it marks a live microphone. */
.aider-mic-ring {
  position: absolute; inset: -1px; border-radius: 50%;
  border: 2px solid var(--away); opacity: 0; pointer-events: none;
}
.aider-mic.is-live .aider-mic-ring { animation: aider-ping 1.5s ease-out infinite; }

/* ---- rating ---- */
.aider-rate {
  display: flex; align-items: center; flex-wrap: wrap; gap: 7px;
  margin-top: 9px; padding-top: 9px; border-top: 1px dashed var(--line);
}
.aider-rate-q { font-size: 11.5px; color: var(--muted); }
.aider-rate-done { font-size: 11.5px; color: var(--accent); }
.aider-thumb {
  font: inherit; font-size: 13px; line-height: 1; cursor: pointer;
  padding: 4px 9px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface);
  transition: border-color .14s ease, transform .14s ease;
}
.aider-thumb:hover { border-color: var(--accent); transform: translateY(-1px); }
.aider-wanted { display: flex; gap: 6px; width: 100%; }
.aider-wanted-in {
  flex: 1 1 auto; min-width: 0; font: inherit; font-size: 12.5px;
  padding: 5px 10px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface); color: var(--text);
}
.aider-wanted-in:focus { outline: none; border-color: var(--accent); }
/* Same trap as the send button: `button[type=submit]` outranks a lone class,
   so this is selected through its parent and its padding set explicitly. */
.aider-wanted .aider-wanted-go {
  flex: none; font: inherit; font-size: 12px; font-weight: 600;
  padding: 5px 12px; margin: 0; border: 0; border-radius: 999px;
  background: var(--accent); color: var(--bg); cursor: pointer;
}
.aider-wanted .aider-wanted-go:hover { filter: brightness(1.08); }

@media (max-width: 560px) {
  .aider-modal { width: 100vw; height: 92vh; max-height: none; border-radius: 16px 16px 0 0; }
  .aider-radar { display: none; }
}

/* Motion off. What is left is a static hairline border, a static presence dot,
   and answers that appear complete — the same information, none of the sweep. */
@media (prefers-reduced-motion: reduce) {
  .aider-glow, .aider-face, .aider-avatar, .aider-spark, .aider-launch::after,
  .aider-ping::after, .aider-dots i, .aider-msg, .aider-pick, .aider-say.is-typing::after,
  .aider-mic.is-live .aider-mic-ring {
    animation: none !important;
  }
  /* The live-microphone ring is the one indicator that must survive: it says
     the machine is listening, which is a fact about the world, not decoration.
     It holds steady instead of pulsing. */
  .aider-mic.is-live .aider-mic-ring { opacity: 1; }
  .aider-thumb:hover { transform: none; }
  .aider-launch::after { display: none; }
  .aider-glow { background: var(--accent); opacity: .5; }
  .aider-modal { transition: none; opacity: 1; transform: none; }
  .aider-thread { scroll-behavior: auto; }
  .aider-pick:hover { transform: none; }
  .aider-ask .aider-send:hover { transform: none; }
}

/* ================= Slip modal and bookmaker export =======================
   The slip opens over the radar rather than navigating away, so a selection
   built across several pages can be checked and booked without losing the
   filters behind it. The body is slip.php's own `?partial=1` output, so the
   modal and the standalone page cannot disagree. */
.slip-modal .pk-head { justify-content: space-between; }
.slip-modal .pk-head > div:last-child { display: flex; align-items: center; gap: 12px; }
.slip-modal .pk-body { padding: 0 16px 16px; max-height: calc(86vh - 64px); }
.slip-modal .pk-body > h2:first-child { margin-top: 14px; }
.slip-modal .pk-body.is-busy { opacity: .55; pointer-events: none; }
.slip-modal .pk-body .card { margin-bottom: 14px; }

.slipx-pick { align-items: flex-end; }
.slipx-pick .field { flex: 0 1 260px; }
.slipx-pick select { width: 100%; }
.slipx-acts { margin-top: 14px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.slipx-acts .sub { color: var(--muted); font-size: 12.5px; }

/* The code is the one thing on the page somebody will retype into a phone, so
   it is set large, spaced, and selectable in a single tap. */
.slipx-code {
  display: flex; align-items: center; gap: 12px; margin-top: 8px;
}
.slipx-code code {
  font-size: 22px; font-weight: 700; letter-spacing: .14em;
  padding: 8px 14px; border-radius: 8px;
  background: var(--surface-2, rgba(127,127,127,.12));
  border: 1px solid var(--line); user-select: all;
}
.slipx-text {
  width: 100%; margin-top: 8px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px; line-height: 1.5; padding: 10px;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--surface); color: var(--text); resize: vertical;
}
.slipx details summary { cursor: pointer; color: var(--muted); font-size: 13px; }
.signoff {
  margin: 16px 0 4px; padding: 12px 14px;
  border-left: 3px solid var(--line); border-radius: 0 8px 8px 0;
  background: var(--surface-2, rgba(127,127,127,.08));
  color: var(--muted); font-size: 12.5px; line-height: 1.6;
}


/* ================= Fields ================================================
   Every text box, select and textarea on the site, in one place.

   `select` had no base rule at all before this. Every dropdown on the site
   fell back to the browser's own control, which is why a picker never quite
   matched the boxes beside it.

   The rule that keeps this from rotting again: **this block owns how a field
   looks, and a context may only say how big it is.** Size travels as custom
   properties rather than as another copy of the same six declarations, so a
   tighter field in a table row is one line setting --fld-py, not a fresh
   background, border, radius, transition, hover and focus that then have to be
   kept in step with these by hand. Three separate implementations had already
   drifted apart that way — the admin forms, the bookmaker forms, and the rest
   of the site — and all three are now this one.

   Because contexts no longer redeclare `background`, the chevron below needs
   no specificity trickery to survive: nothing is competing with it. */

:root {
  --field-bg: var(--surface-2);
  --field-line: var(--line);
  --field-ring: color-mix(in srgb, var(--accent) 22%, transparent);
  --field-radius: 9px;
  /* The three knobs a context is allowed to turn. */
  --fld-py: 9px;
  --fld-px: 11px;
  --fld-fs: 14px;
}

input[type=text], input[type=url], input[type=password], input[type=email],
input[type=search], input[type=number], input[type=date], input[type=time],
input[type=tel], input:not([type]), select, textarea {
  font: inherit;
  font-size: var(--fld-fs);
  color: var(--text);
  background-color: var(--field-bg);
  border: 1px solid var(--field-line);
  border-radius: var(--field-radius);
  padding: var(--fld-py) var(--fld-px);
  transition: border-color 140ms ease, box-shadow 140ms ease, background-color 140ms ease;
}

/* Hover only moves the border: a hint that the control is live. */
input:hover:not(:disabled):not(:focus),
select:hover:not(:disabled):not(:focus),
textarea:hover:not(:disabled):not(:focus) {
  border-color: color-mix(in srgb, var(--text) 26%, var(--field-line));
}

/* A tinted ring rather than an offset outline. The old 2px outline sitting 2px
   clear of the control read as a validation failure, which is why so much of
   the site had already turned it off and coloured the border instead. The lift
   to --surface is what the admin forms did and it is worth keeping: the active
   field comes forward. */
input:focus, select:focus, textarea:focus,
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: none;
  border-color: var(--accent);
  background-color: var(--surface);
  box-shadow: 0 0 0 3px var(--field-ring);
}

input::placeholder, textarea::placeholder { color: var(--muted); opacity: .75; }

input:disabled, select:disabled, textarea:disabled {
  opacity: .55; cursor: not-allowed;
  background-color: color-mix(in srgb, var(--muted) 8%, var(--field-bg));
}

/* Only once the field has been left, so it does not shout while typing. */
input:user-invalid, select:user-invalid, textarea:user-invalid {
  border-color: var(--away);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--away) 20%, transparent);
}

/* --- select ---------------------------------------------------------------
   Our own chevron, positioned off --fld-px so it stays correctly inset at
   every size without a per-context rule. */
select:not([multiple]):not([size]) {
  appearance: none; -webkit-appearance: none;
  cursor: pointer;
  padding-right: calc(var(--fld-px) + 21px);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%23939cad' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--fld-px) center;
  background-size: 11px 7px;
}
:root[data-theme="light"] select:not([multiple]):not([size]) {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%23626b7b' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* The dropdown list itself, on the platforms that let us near it. */
select option { background: var(--surface); color: var(--text); }
select optgroup { background: var(--surface); color: var(--muted); font-style: normal; }

/* The one dropdown meant to read as running text rather than as a box. */
.ccpick-control select {
  border: 0; background-color: transparent; color: var(--text);
  font-size: 13.5px; padding: 2px 16px 2px 0; max-width: 190px; cursor: pointer;
  background-position: right 1px center;
}
.ccpick-control select:focus { background-color: transparent; box-shadow: none; }

/* --- checkboxes and radios ------------------------------------------------ */
input[type=checkbox], input[type=radio] {
  accent-color: var(--accent);
  width: 15px; height: 15px;
  border: 0; padding: 0; background: none; border-radius: 0;
  box-shadow: none; cursor: pointer; vertical-align: -2px;
}
input[type=checkbox]:focus-visible, input[type=radio]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; box-shadow: none;
}
input[type=checkbox]:disabled, input[type=radio]:disabled { cursor: not-allowed; }

/* --- search --------------------------------------------------------------- */
input[type=search] { -webkit-appearance: none; }
input[type=search]::-webkit-search-cancel-button {
  -webkit-appearance: none; height: 13px; width: 13px; cursor: pointer;
  background: var(--muted);
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 4l8 8M12 4l-8 8' stroke='%23000' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* --- number: the spinners are unusable at these sizes --------------------- */
input[type=number] { -moz-appearance: textfield; }
input[type=number]::-webkit-outer-spin-button,
input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* --- range ---------------------------------------------------------------
   The confidence slider carried nothing but accent-color, which fills the
   track on some browsers and does nothing on others. */
input[type=range] {
  appearance: none; -webkit-appearance: none;
  height: 22px; background: none; cursor: pointer; padding: 0; border: 0;
}
input[type=range]:focus, input[type=range]:focus-visible { box-shadow: none; background: none; }
input[type=range]::-webkit-slider-runnable-track {
  height: 5px; border-radius: 999px;
  background: color-mix(in srgb, var(--text) 14%, var(--surface-2));
}
input[type=range]::-moz-range-track {
  height: 5px; border-radius: 999px;
  background: color-mix(in srgb, var(--text) 14%, var(--surface-2));
}
input[type=range]::-moz-range-progress { height: 5px; border-radius: 999px; background: var(--accent); }
input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none; margin-top: -6px;
  width: 17px; height: 17px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--bg);
  box-shadow: 0 1px 4px rgba(0,0,0,.35);
  transition: transform 120ms ease, box-shadow 120ms ease;
}
input[type=range]::-moz-range-thumb {
  width: 17px; height: 17px; border-radius: 50%; border: 2px solid var(--bg);
  background: var(--accent); box-shadow: 0 1px 4px rgba(0,0,0,.35);
}
input[type=range]:hover::-webkit-slider-thumb { transform: scale(1.12); }
input[type=range]:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 4px var(--field-ring); }
input[type=range]:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 4px var(--field-ring); }
.rangewrap output {
  font-family: var(--mono); font-size: 13px; color: var(--text);
  min-width: 3.4em; text-align: right;
}

/* --- a magnifier on the roomy search fields -------------------------------
   Not on .teamsearch, a narrow pill with a button welded to it and no room. */
.searchbig input[type=search],
.pr-filter input[type=search],
.pl-filter input[type=search] {
  padding-left: 34px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23939cad' stroke-width='1.7'%3E%3Ccircle cx='7' cy='7' r='4.5'/%3E%3Cpath d='M10.5 10.5L14 14' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: left 11px center;
  background-size: 15px;
}
:root[data-theme="light"] .searchbig input[type=search],
:root[data-theme="light"] .pr-filter input[type=search],
:root[data-theme="light"] .pl-filter input[type=search] {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23626b7b' stroke-width='1.7'%3E%3Ccircle cx='7' cy='7' r='4.5'/%3E%3Cpath d='M10.5 10.5L14 14' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* --- sizes ---------------------------------------------------------------
   The whole of the platform's field sizing, in one screen. Each of these used
   to be a full copy of the chrome above. */
.fx                        { --fld-fs: 13.5px; }
body[data-admin]           { --fld-py: 8px; --fld-fs: 13.5px; }
body[data-admin] .admin-list,
body[data-admin] table     { --fld-py: 6px; --fld-px: 9px; --fld-fs: 12.5px; --field-radius: 7px; }
.ccpick                    { --fld-py: 5px; --fld-px: 8px; --fld-fs: 13px; }
.geocell                   { --fld-py: 5px; --fld-px: 6px; --fld-fs: 12px; }
.oa-add                    { --fld-py: 5px; --fld-px: 7px; --fld-fs: 12.5px; }
.tk-edit-pick              { --fld-py: 7px; --fld-px: 10px; --fld-fs: 13px; }
.chan-table                { --fld-py: 6px; --fld-px: 8px; --fld-fs: 13px; }
.teamsearch                { --fld-py: 6px; --fld-px: 10px; --fld-fs: 13px; }
.pl-filter                 { --fld-py: 7px; --fld-fs: 13px; }
.pr-filter                 { --fld-fs: 13.5px; }

/* --- layout helpers -------------------------------------------------------- */
.field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.field > label, .field-label {
  font-size: 12.5px; font-weight: 600; color: var(--muted); letter-spacing: .01em;
}
.field-hint { font-size: 12px; color: var(--muted); line-height: 1.5; }
.field-row { display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-end; }
.field-row > .field { flex: 1 1 200px; }

/* A control and its button as one unit. */
.input-group { display: flex; align-items: stretch; min-width: 0; }
.input-group > :first-child { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.input-group > :last-child  { border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: -1px; }
.input-group > :focus { position: relative; z-index: 1; }

@media (prefers-reduced-motion: reduce) {
  input, select, textarea { transition: none; }
  input[type=range]::-webkit-slider-thumb { transition: none; }
}

/* ==========================================================================
   The landing page
   ==========================================================================
   The argument of this page is that the numbers were measured, so the page is
   built around a measurement rather than around a promise. Everything numeric
   is set in the mono face at size — this site's whole dialect is tabular
   figures, and the front door says so out loud — while the prose stays in the
   body face and out of the way. One animated moment, the ribbon, and nothing
   else competes with it. */

.hm-hero { padding: 12px 0 6px; max-width: 74ch; }
.hm-eyebrow {
  margin: 0 0 14px; font-family: var(--mono); font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .22em; color: var(--accent);
}
.hm-h1 {
  margin: 0 0 14px; font-size: clamp(2rem, 5.4vw, 3.4rem); font-weight: 800;
  letter-spacing: -0.035em; line-height: 1.06;
}
.hm-num {
  font-family: var(--mono); font-weight: 700; letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
}
.hm-live { color: var(--accent); }
.hm-sub {
  margin: 0 0 26px; font-size: 1.02rem; line-height: 1.6; color: var(--muted);
  max-width: 58ch;
}
.hm-sub b { color: var(--text); font-family: var(--mono); font-weight: 700; }

/* --- the signature: claimed against delivered --------------------------- */
.hm-ribbon { margin: 0 0 26px; padding: 0; }
.hm-ribbon-plot {
  display: flex; align-items: flex-end; gap: clamp(3px, .9vw, 9px);
  height: 190px; padding: 14px 0 0;
  border-bottom: 1px solid var(--line);
}
.hm-band { position: relative; flex: 1 1 0; height: 100%; min-width: 0; }
/* The bar is what happened. It rises to its measured rate on reveal, one band
   after the next, left to right — the order the plot is read in. */
.hm-bar {
  position: absolute; left: 0; right: 0; bottom: 0; height: 0;
  border-radius: 3px 3px 0 0;
  background: linear-gradient(180deg,
              color-mix(in srgb, var(--accent) 88%, transparent),
              color-mix(in srgb, var(--accent) 26%, transparent));
}
.hm-ribbon.is-in .hm-bar {
  animation: hm-rise 620ms cubic-bezier(.22,.61,.36,1) forwards;
  animation-delay: calc(var(--i) * 45ms);
}
@keyframes hm-rise { to { height: var(--d); } }
/* The claim is a hairline the bar should reach. It arrives after every bar has
   settled, so the eye compares rather than watches. */
.hm-claim {
  position: absolute; left: -2px; right: -2px; bottom: var(--c); height: 2px;
  background: var(--text); border-radius: 2px; opacity: 0;
}
.hm-ribbon.is-in .hm-claim {
  animation: hm-mark 320ms ease forwards;
  animation-delay: calc(760ms + var(--i) * 26ms);
}
@keyframes hm-mark { from { opacity: 0; transform: scaleX(.4); } to { opacity: .92; transform: none; } }
.hm-band.over .hm-bar { filter: none; }
.hm-tip {
  position: absolute; bottom: calc(var(--d) + 12px); left: 50%; transform: translateX(-50%);
  white-space: nowrap; font-family: var(--mono); font-size: 10.5px;
  padding: 5px 8px; border-radius: 7px; pointer-events: none;
  background: var(--surface); border: 1px solid var(--line); color: var(--text);
  opacity: 0; transition: opacity 140ms ease; z-index: 2;
}
.hm-band:hover .hm-tip, .hm-band:focus-within .hm-tip { opacity: 1; }
.hm-ribbon-foot {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  margin-top: 12px; font-size: 12px; color: var(--muted);
}
.hm-ribbon-foot > span { display: inline-flex; align-items: center; gap: 7px; }
.hm-key { display: inline-block; width: 16px; height: 9px; border-radius: 2px; }
.hm-key-bar { background: color-mix(in srgb, var(--accent) 70%, transparent); }
.hm-key-line { height: 2px; background: var(--text); }
.hm-gap { margin-left: auto; font-family: var(--mono); }
.hm-gap b { color: var(--accent); }

.hm-cta { display: flex; gap: 10px; flex-wrap: wrap; }
.hm-btn { padding: 11px 22px; font-size: 15px; }

/* --- who is playing ------------------------------------------------------ */
.hm-marquee {
  margin: 34px 0; padding: 14px 0; overflow: hidden;
  border-block: 1px solid var(--line);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.hm-marquee-track {
  display: flex; align-items: center; gap: 30px; width: max-content;
  animation: hm-scroll 68s linear infinite;
}
@keyframes hm-scroll { to { transform: translateX(-50%); } }
.hm-marquee:hover .hm-marquee-track { animation-play-state: paused; }
.hm-crest { display: inline-flex; opacity: .62; transition: opacity 160ms ease; }
.hm-crest:hover { opacity: 1; }

/* --- sections ------------------------------------------------------------ */
.hm-sec { margin: 40px 0; opacity: 0; transform: translateY(10px); }
.hm-sec.is-in { animation: hm-in 460ms cubic-bezier(.22,.61,.36,1) forwards; }
@keyframes hm-in { to { opacity: 1; transform: none; } }
.hm-h2 {
  margin: 0 0 14px; font-size: 1.12rem; font-weight: 700; letter-spacing: -0.015em;
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
}
.hm-h2-sub { font-size: .8rem; font-weight: 400; color: var(--muted); letter-spacing: 0; }

/* Next up */
.hm-next { display: grid; gap: 8px; }
.hm-fx {
  display: grid; grid-template-columns: 170px minmax(0, 1fr) 150px 108px;
  gap: 8px 18px; align-items: center; padding: 12px 14px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  color: inherit; text-decoration: none;
  transition: border-color 150ms ease, transform 150ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .hm-fx:hover { transform: translateY(-1px); border-color: color-mix(in srgb, var(--accent) 42%, var(--line)); }
}
@media (max-width: 900px) { .hm-fx { grid-template-columns: 1fr; } }
.hm-fx-when { display: flex; flex-direction: column; gap: 2px; font-size: 12px; }
.hm-fx-when time { font-family: var(--mono); color: var(--text); }
.hm-fx-comp { color: var(--muted); }
.hm-fx-teams { display: flex; align-items: center; gap: 8px; min-width: 0; flex-wrap: wrap; }
.hm-fx-side { display: inline-flex; align-items: center; gap: 7px; min-width: 0; }
.hm-fx-side b { font-weight: 600; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hm-fx-side.lead b { color: var(--accent); }
.hm-fx-v { font-family: var(--mono); font-size: 11px; color: var(--muted); }
.hm-fx-bar { display: flex; height: 7px; border-radius: 999px; overflow: hidden; background: var(--surface-2); }
.hm-fx-bar i { width: var(--w); }
.hm-fx-bar .h { background: var(--accent); }
.hm-fx-bar .d { background: var(--line); }
.hm-fx-bar .a { background: var(--home); }
.hm-fx-nums {
  display: flex; justify-content: space-between; gap: 6px;
  font-family: var(--mono); font-size: 12px; color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* Strongest calls */
.hm-top { display: grid; gap: 8px; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); }
.hm-pick {
  display: flex; flex-direction: column; gap: 3px; padding: 14px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  color: inherit; text-decoration: none;
  transition: border-color 150ms ease, transform 150ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .hm-pick:hover { transform: translateY(-2px); border-color: color-mix(in srgb, var(--accent) 42%, var(--line)); }
}
.hm-pick-fx { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
.hm-pick-fx span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hm-pick-label { font-size: 15px; font-weight: 650; margin-top: 4px; }
.hm-pick-meta { font-size: 12px; color: var(--muted); }
.hm-pick-num { display: flex; align-items: baseline; gap: 8px; margin-top: 8px; }
.hm-pick-num b {
  font-family: var(--mono); font-size: 1.5rem; font-weight: 700;
  letter-spacing: -0.03em; color: var(--accent);
}
.hm-pick-num span { font-family: var(--mono); font-size: 11.5px; color: var(--muted); }

/* The record */
.hm-figs { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); }
.hm-fig {
  padding: 16px 18px; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius);
}
.hm-fig b {
  display: block; font-family: var(--mono); font-size: clamp(1.5rem, 3vw, 2rem);
  font-weight: 700; letter-spacing: -0.04em; font-variant-numeric: tabular-nums;
}
.hm-fig span { display: block; margin-top: 3px; font-size: 12px; color: var(--muted); }
.hm-fig-gap { border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); }
.hm-fig-gap b { color: var(--accent); }
.hm-note { margin: 12px 0 0; font-size: 13px; line-height: 1.6; color: var(--muted); max-width: 70ch; }

/* What you can do */
.hm-does { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }
.hm-do {
  display: flex; flex-direction: column; gap: 6px; padding: 18px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  color: inherit; text-decoration: none;
  transition: border-color 150ms ease, transform 150ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .hm-do:hover { transform: translateY(-2px); border-color: color-mix(in srgb, var(--accent) 42%, var(--line)); }
}
.hm-do b { font-size: 15px; font-weight: 650; }
.hm-do span { font-size: 13px; line-height: 1.55; color: var(--muted); }

.hm-foot {
  margin: 36px 0 0; padding-top: 18px; border-top: 1px solid var(--line);
  font-size: 12.5px; line-height: 1.6; color: var(--muted); max-width: 72ch;
}

@media (prefers-reduced-motion: reduce) {
  .hm-sec { opacity: 1; transform: none; animation: none; }
  .hm-marquee-track { animation: none; }
  .hm-ribbon .hm-bar { height: var(--d); animation: none; }
  .hm-ribbon .hm-claim { opacity: .92; animation: none; }
}

/* --- select with a logo ---------------------------------------------------
   `<option>` cannot hold an image, so a dropdown that shows each bookmaker's
   mark has to be built from ordinary elements (assets/logoselect.js). The
   button below is deliberately styled from the same tokens as a real field
   rather than to look like one: if the field system changes, this changes
   with it instead of drifting into a near-match. */
.ls { position: relative; }
.ls-native {
  position: absolute; width: 1px; height: 1px;
  opacity: 0; pointer-events: none; margin: 0;
}
.ls-btn {
  display: flex; align-items: center; gap: 9px; width: 100%; text-align: left;
  font: inherit; font-size: var(--fld-fs); color: var(--text);
  background-color: var(--field-bg);
  border: 1px solid var(--field-line);
  border-radius: var(--field-radius);
  padding: var(--fld-py) calc(var(--fld-px) + 21px) var(--fld-py) var(--fld-px);
  cursor: pointer;
  transition: border-color 140ms ease, box-shadow 140ms ease, background-color 140ms ease;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%23939cad' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--fld-px) center;
  background-size: 11px 7px;
}
:root[data-theme="light"] .ls-btn {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%23626b7b' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.ls-btn:hover { border-color: color-mix(in srgb, var(--text) 26%, var(--field-line)); }
.ls-btn:focus-visible {
  outline: none; border-color: var(--accent);
  background-color: var(--surface); box-shadow: 0 0 0 3px var(--field-ring);
}
.ls.is-open .ls-btn { border-color: var(--accent); box-shadow: 0 0 0 3px var(--field-ring); }

.ls-list {
  position: absolute; z-index: 40; left: 0; right: 0; top: calc(100% + 5px);
  max-height: 288px; overflow-y: auto; padding: 5px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--field-radius);
  box-shadow: 0 12px 34px rgba(0,0,0,.34);
}
.ls-row {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 9px; border-radius: 7px; cursor: pointer;
  font-size: var(--fld-fs); color: var(--text);
}
.ls-row:hover, .ls-row.on { background: var(--surface-2); }
.ls-row[aria-selected="true"] {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--text);
}
.ls-row[aria-selected="true"] .ls-text { font-weight: 600; }

/* Bookmaker marks are every shape and several are white-on-transparent, so
   each sits on its own light tile rather than on the menu. Without it half of
   them vanish in dark mode. */
.ls-logo {
  flex: 0 0 auto; width: 22px; height: 22px; object-fit: contain;
  background: #fff; border-radius: 5px; padding: 2px;
}
.ls-initial {
  display: grid; place-items: center; background: var(--surface-2); color: var(--muted);
  font-size: 12px; font-weight: 700; padding: 0;
}
.ls-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

@media (prefers-reduced-motion: reduce) { .ls-btn { transition: none; } }
.ls-blank { background: none; }

/* One scrollbar in the pick/slip dialogs. The <dialog> has max-height 86vh and
   the browser's own overflow:auto, and .pk-body had its own cap that ignored
   the header and padding — so past a few legs both scrolled. The dialog is
   now a column that clips, and the body is the only thing that scrolls. */
.pk-modal[open] { display: flex; flex-direction: column; overflow: hidden; }
.pk-modal .pk-head, .pk-modal .pk-tools { flex: 0 0 auto; }
.pk-modal .pk-body, .slip-modal .pk-body { flex: 1 1 auto; min-height: 0; max-height: none; overflow: auto; }

/* ---------- did the forecast call it ----------
   Green for a hit, muted red for a miss, and the miss is as legible as the hit.
   A results table that marks its successes in colour and its failures in grey
   is telling half a story loudly. Colour is doubled by the word — "called it" /
   "missed" — so the column still reads with colour vision differences, in print,
   and when an operator turns the colour off. */
.vd {
  display: inline-block; font-size: 11.5px; font-weight: 600; line-height: 1;
  padding: 4px 8px; border-radius: 999px; white-space: nowrap;
  border: 1px solid transparent;
}
.vd-hit {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-color: color-mix(in srgb, var(--accent) 32%, transparent);
}
.vd-miss {
  color: var(--away);
  background: color-mix(in srgb, var(--away) 13%, transparent);
  border-color: color-mix(in srgb, var(--away) 30%, transparent);
}
.vd-unjudged { color: var(--muted); border-color: var(--line); }
/* The operator's "colour off" setting: keep the words, drop the paint. */
.vd-plain {
  color: var(--muted) !important; background: transparent !important;
  border-color: var(--line) !important;
}
.vd-tally {
  margin: 12px 0 0; font-size: 13px; line-height: 1.55;
  padding: 9px 12px; border-radius: 10px;
  background: var(--surface-2); border: 1px solid var(--line);
}


/* ---------- moving between sections ----------
   Every section is its own page, so "switching tabs" is a navigation. With
   cross-document view transitions the browser snapshots the outgoing page,
   loads the next, and animates between the two — no client-side router, and
   nothing changes for a browser that lacks it beyond a plain load.

   Three things move. The header holds still (it is named, so it is not part
   of the page's crossfade). The active marker — the underline on a wide
   screen, the highlighted tab in the phone bar, the lit sport pill — glides
   from the old tab to the new one because the two snapshots share a name.
   And the page itself slides: away to the left and in from the right when
   moving forward along the section order, the reverse when moving back. The
   head script sets data-nav-dir from what the click recorded. */
@view-transition { navigation: auto; }

.topbar   { view-transition-name: topbar; }
.m-tabbar { view-transition-name: tabbar; }
main      { view-transition-name: page; }
.sectionnav a.on { view-transition-name: section-active; }
.m-tab.on        { view-transition-name: tab-active; }
.tier-sports .sporttab.on { view-transition-name: sport-active; }

::view-transition-group(*) {
  animation-duration: 240ms;
  animation-timing-function: cubic-bezier(.2, .7, .2, 1);
}
::view-transition-old(root), ::view-transition-new(root) { animation-duration: 180ms; }

/* The marker morphs between positions; its label crossfades along the way. */
::view-transition-group(section-active),
::view-transition-group(tab-active),
::view-transition-group(sport-active) { animation-duration: 300ms; }

/* The page. Snapshots are stacked, so the old one slides out while the new
   one slides in beneath it. */
::view-transition-old(page) { animation: 200ms cubic-bezier(.4, 0, 1, 1) both vt-page-out-l; }
::view-transition-new(page) { animation: 280ms cubic-bezier(.2, .7, .2, 1) both vt-page-in-r; }
html[data-nav-dir="back"]::view-transition-old(page) { animation-name: vt-page-out-r; }
html[data-nav-dir="back"]::view-transition-new(page) { animation-name: vt-page-in-l; }
@keyframes vt-page-out-l { to   { transform: translateX(-28px); opacity: 0; } }
@keyframes vt-page-in-r  { from { transform: translateX(28px);  opacity: 0; } }
@keyframes vt-page-out-r { to   { transform: translateX(28px);  opacity: 0; } }
@keyframes vt-page-in-l  { from { transform: translateX(-28px); opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) {
    animation-duration: 1ms !important;
  }
}

/* ============================================================================
   AI prediction card (lib/ai.php). The ensemble's price, its footing and the
   reasons, rendered from a stored snapshot. Colours come from the theme
   variables so the card follows light and dark like everything else.
   ========================================================================== */
.ai-card { border-left: 3px solid var(--accent); }
.ai-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 12px; margin-bottom: 12px; }
.ai-head h2 { margin: 0; }
.ai-probs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 8px; }
.ai-tile { background: var(--surface-2); border-radius: 8px; padding: 10px 12px; border: 1px solid transparent; }
.ai-tile.lead { border-color: var(--accent); }
.ai-tile .k { color: var(--muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.04em;
              white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ai-tile .p { font-family: var(--mono); font-size: 26px; font-weight: 700; margin-top: 2px; }
.ai-tile .p small { font-size: 14px; font-weight: 500; color: var(--muted); margin-left: 1px; }
.ai-tile.home .p { color: var(--home); } .ai-tile.away .p { color: var(--away); }
.ai-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; margin-top: 14px; }
.ai-block h3, .ai-factors h3 { margin: 0 0 8px; font-size: 13px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.ai-scores { list-style: none; margin: 0; padding: 0; }
.ai-scores li { display: grid; grid-template-columns: 44px 1fr 56px; align-items: center; gap: 8px; padding: 3px 0; }
.ai-scores .mono { font-family: var(--mono); }
.ai-scores b { text-align: right; font-family: var(--mono); font-weight: 600; }
.ai-scorebar { display: block; height: 8px; background: var(--surface-2); border-radius: 4px; overflow: hidden; }
.ai-scorebar i { display: block; height: 100%; background: var(--accent); border-radius: 4px; }
.ai-mini { width: 100%; font-size: 13.5px; }
.ai-mini td { padding: 3px 6px 3px 0; border: 0; }
.ai-mini td.num { font-family: var(--mono); text-align: right; padding-right: 14px; }
.ai-meters { display: grid; gap: 8px; }
.ai-meter .k { color: var(--muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.04em; }
.ai-meter .v { font-family: var(--mono); font-size: 20px; font-weight: 650; }
.ai-meter .v small { font-size: 12px; color: var(--muted); font-weight: 500; }
.ai-meter .bar { height: 6px; background: var(--surface-2); border-radius: 3px; overflow: hidden; }
.ai-meter .bar i { display: block; height: 100%; border-radius: 3px; background: var(--muted); }
.ai-meter .bar i.high { background: var(--accent); }
.ai-meter .bar i.moderate { background: var(--away); }
.ai-factors { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 14px; margin-top: 16px; }
.ai-factors ul { margin: 0; padding-left: 18px; font-size: 14px; line-height: 1.45; }
.ai-factors li + li { margin-top: 4px; }
.ai-why { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 10px; }
.ai-why summary { cursor: pointer; font-weight: 600; }
.ai-why h4 { margin: 12px 0 4px; font-size: 13px; }
.ai-why p { margin: 0; font-size: 14px; line-height: 1.5; }
.ai-summary { margin: 14px 0 4px; font-size: 15px; line-height: 1.5; padding: 10px 12px; background: var(--surface-2); border-radius: 8px; }
.ai-foot { margin: 8px 0 0; }
@media (max-width: 720px) {
  .ai-probs { gap: 6px; }
  .ai-tile { padding: 8px 8px; }
  .ai-tile .p { font-size: 21px; }
}
.ai-tile .r { color: var(--muted); font-size: 11.5px; font-family: var(--mono); margin-top: 2px; }

/* ---------- watch at your bookmaker ----------
   A row of outbound links, each marked as what it is. The "this match" /
   "live lobby" tag matters: a link that lands on the fixture and one that
   lands on a front page are different promises, and the reader should know
   which they are clicking before they leave. */
.wl { padding: 12px 14px; }
.wl-lead { margin: 0 0 10px; font-size: 13px; color: var(--muted); }
.wl-list { display: flex; flex-wrap: wrap; gap: 8px; }
.wl-link {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 12px 7px 8px; border-radius: 999px; text-decoration: none;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--text);
  font-size: 13px; font-weight: 600; line-height: 1;
  transition: border-color .14s ease, transform .14s ease;
}
.wl-link:hover { border-color: var(--accent); transform: translateY(-1px); }
.wl-logo { width: 20px; height: 20px; border-radius: 5px; object-fit: contain; background: #fff; }
.wl-mono {
  width: 20px; height: 20px; border-radius: 5px; font-size: 10px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent); color: var(--bg);
}
.wl-kind {
  font-size: 10.5px; font-weight: 500; color: var(--muted);
  padding: 2px 7px; border-radius: 999px; border: 1px solid var(--line);
}
.wl-kind.is-exact { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 40%, transparent); }
.wl-legal { margin: 10px 0 0; font-size: 11px; line-height: 1.5; color: var(--muted); }
@media (prefers-reduced-motion: reduce) { .wl-link:hover { transform: none; } }

/* The paid-link disclosure under the ticket and convert panels. Same size and
   colour as .wl-legal, because it says the same thing and a reader who has
   learned to read one should not have to notice the other is different. */
.tk-aff-note { margin: 12px 0 0; font-size: 11px; line-height: 1.5; color: var(--muted); }

/* ---- affiliate admin ----
   The one row on that page that has to be seen: a bookmaker collecting clicks
   with no tag entered. Everything else on the page is information; this is
   money going out of the door, so it is coloured like the loss it is. */
.warn-row { background: color-mix(in srgb, var(--away) 10%, transparent); }
.aff-state {
  display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 11px;
  border: 1px solid var(--line); color: var(--muted); text-transform: lowercase;
}
.aff-state.live {
  color: var(--home); border-color: color-mix(in srgb, var(--home) 45%, transparent);
  background: color-mix(in srgb, var(--home) 12%, transparent);
}
.aff-state.applied { color: var(--fg); }
.aff-state.rejected { color: var(--away); border-color: color-mix(in srgb, var(--away) 45%, transparent); }

/* ---- language ----
   A native <select> in a pill that matches the appearance toggle beside it.
   Native on purpose: the reader who needs this control may not be able to read
   the page, so it has to be the most familiar control there is. */
.langpick {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  margin: 0 0 0 8px; height: var(--hdr-ctl-h); padding: 0 5px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--muted);
  transition: border-color .18s ease, color .18s ease;
}
.langpick:hover, .langpick:focus-within { border-color: var(--accent); color: var(--text); }
.langpick-ico { display: inline-flex; }
.langpick-ico svg { width: 14px; height: 14px; display: block; }
.langpick select {
  font: inherit; font-size: 12.5px; line-height: 1.2; color: inherit;
  background: transparent; border: 0; padding: 2px 0; margin: 0; max-width: 132px;
  cursor: pointer; appearance: auto; -webkit-appearance: menulist;
}
.langpick select:focus { outline: none; }
.langpick select option { color: var(--text); background: var(--surface); }
.m-sheet-prefs .langpick { margin: 0; }
/* Right-to-left pages: the appearance toggle's auto margin swaps sides. Inside
   the header cluster neither carries a margin at all — the wrapper's gap does
   that job, and its own auto margin is what pushes the group to the far side. */
[dir="rtl"] .themetoggle { margin-left: 0; margin-right: auto; }
[dir="rtl"] .langpick { margin: 0 8px 0 0; }
[dir="rtl"] .tier-prefs > .themetoggle,
[dir="rtl"] .tier-prefs > .langpick { margin: 0; }

/* ---- language switcher with flags ----
   logoselect.js draws its listbox over the native select; in the header the
   button has to be a pill, not a form field, and the list opens under it. */
.langpick .ls { display: inline-block; }
/* The flag sits next to its language, not across the pill from it: 4px, the
   same step the cluster uses between controls. The right padding is only what
   the caret needs. */
.langpick .ls-btn {
  width: auto; min-width: 0; gap: 4px; padding: 2px 16px 2px 3px; font-size: 12.5px;
  border: 0; border-radius: 999px; background-color: transparent; color: inherit;
  background-position: right 4px center;
}
.langpick .ls-btn:hover, .langpick .ls.is-open .ls-btn { box-shadow: none; background-color: transparent; }
/* Sized to the toggle's own icons, so the flag, the sun and the moon all read
   as one row of marks rather than three sizes of thing. */
.langpick .ls-logo { width: 18px; height: 13px; padding: 0; border-radius: 3px; background: none; object-fit: cover; }
.langpick .ls-initial { width: 18px; height: 13px; font-size: 10px; }
.langpick .ls-list { left: auto; right: 0; min-width: 210px; top: calc(100% + 8px); z-index: 60; }
.langpick .ls-row { padding: 6px 8px; font-size: 13px; }
[dir="rtl"] .langpick .ls-list { right: auto; left: 0; }
.m-sheet-prefs .langpick .ls-list { position: fixed; left: 12px; right: 12px; top: auto; bottom: 12px; max-height: 50vh; }

/* ---- Google's page translator ----
   It is told the language through its cookie and given no banner to show:
   the reader already chose from our switcher, and a second one that pushes
   the page down by 40px is not a feature. */
#google_translate_element, .goog-te-banner-frame, .skiptranslate, #goog-gt-tt,
.goog-te-balloon-frame, .goog-tooltip { display: none !important; }
body { top: 0 !important; position: static !important; }
font[style*="vertical-align"] { background: none !important; box-shadow: none !important; }

/* Starred selections: the best pick and the likeliest to land, both priced
   above 1.18 on a measured band. On the Predict page and the AI card. */
.pr-stars, .ai-stars { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; margin: 0 0 16px; }
.ai-stars { margin-top: 16px; }
.pr-star, .ai-star { position: relative; padding: 12px 14px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line); display: flex; flex-direction: column; gap: 3px; }
.pr-star.best, .ai-star.best { border-color: var(--accent); box-shadow: inset 3px 0 0 var(--accent); }
.pr-star.likely, .ai-star.likely { border-color: var(--home); box-shadow: inset 3px 0 0 var(--home); }
.pr-star.empty, .ai-star.empty { opacity: 0.7; box-shadow: none; }
.pr-star-k, .ai-star .k { font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); font-weight: 700; }
.pr-star.best .pr-star-k, .ai-star.best .k { color: var(--accent); }
.pr-star.likely .pr-star-k, .ai-star.likely .k { color: var(--home); }
.pr-star-label, .ai-star .l { font-size: 17px; font-weight: 700; line-height: 1.25; }
.pr-star-fam, .pr-star-why, .pr-star-none, .ai-star .sub { color: var(--muted); font-size: 12.5px; margin: 0; }
.pr-star-nums, .ai-star .n { display: flex; align-items: baseline; gap: 10px; margin-top: 2px; }
.pr-star-nums b, .ai-star .n b { font-family: var(--mono); font-size: 24px; font-weight: 700; }
.pr-star-odds, .ai-star .n span { font-family: var(--mono); color: var(--muted); font-size: 13px; }
.pr-bestrow.starred .pr-rank { background: var(--accent); color: #0b0d11; font-size: 13px; }
.pr-bestrow.starred.likely .pr-rank { background: var(--home); }

/* One selection per match on the Radar slip: the clash notice, the blocked
   button and the rows that pulse until one of them is removed. */
/* The notice floats above the pill as its own card, anchored to the bar's
   left edge, with a small pointer down at the bar it belongs to. */
.slipwarn { position: absolute; left: 0; right: 0; bottom: calc(100% + 12px);
            background: color-mix(in srgb, var(--away) 14%, var(--surface)); border: 1px solid var(--away);
            border-radius: 14px; padding: 12px 16px; font-size: 13.5px; display: flex; flex-direction: column; gap: 7px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.32); z-index: 21; }
.slipwarn::after { content: ''; position: absolute; right: 36px; bottom: -7px; width: 12px; height: 12px;
                   background: inherit; border-right: 1px solid var(--away); border-bottom: 1px solid var(--away);
                   transform: rotate(45deg); }
.pk-modal .slipwarn { position: static; margin: 0 16px 8px; box-shadow: none; }
.pk-modal .slipwarn::after { display: none; }
.slipwarn-head { font-weight: 700; color: var(--away); }
.slipwarn-head .sub { font-weight: 400; color: var(--muted); }
.slipwarn-match { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.slipwarn-fx { font-weight: 600; margin-right: 4px; }
.slipwarn-pick { display: inline-flex; align-items: center; gap: 6px; background: var(--surface-2); border-radius: 999px; padding: 3px 6px 3px 10px; }
.slipwarn-rm { background: var(--away); color: #0b0d11; border: 0; border-radius: 999px; padding: 2px 9px; font-size: 12px; font-weight: 600; cursor: pointer; }
.slipwarn-rm:hover { filter: brightness(1.08); }
.slipgo:disabled, .slipgo[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; filter: none; }
.rd tr.conflict { background: color-mix(in srgb, var(--away) 12%, transparent); box-shadow: inset 3px 0 0 var(--away); }
@keyframes slip-shake { 0%,100% { transform: translateX(0); } 20% { transform: translateX(-7px); } 40% { transform: translateX(6px); } 60% { transform: translateX(-4px); } 80% { transform: translateX(3px); } }
@keyframes slip-slidein { from { opacity: 0; transform: translateY(14px) scale(.98); } to { opacity: 1; transform: none; } }
@keyframes slip-pulse { 0% { background: color-mix(in srgb, var(--away) 45%, transparent); } 100% { background: color-mix(in srgb, var(--away) 12%, transparent); } }
.slipbar.shake { animation: slip-shake 420ms cubic-bezier(.36,.07,.19,.97) both; }
.slipwarn.slidein { animation: slip-slidein 260ms cubic-bezier(.2,.7,.2,1) both; }
.rd tr.conflict.pulse { animation: slip-pulse 900ms ease-out both; }
@media (prefers-reduced-motion: reduce) {
  .slipbar.shake, .slipwarn.slidein, .rd tr.conflict.pulse { animation: none; }
}

/* ---- bookmaker preference ----
   Same pill as the language switcher, with the book's logo instead of a flag.
   Logos are every shape, so they keep the light tile the slip dropdown uses. */
/* Square, and the same 18px as the flag beside it. The white tile stays —
   several bookmaker marks are white-on-transparent and vanish without it. */
.bookpick .ls-logo { width: 18px; height: 18px; border-radius: 5px; background: #fff; padding: 2px; object-fit: contain; }
.bookpick .ls-blank { background: none; }
.bookpick .ls-list { min-width: 200px; }

/* ---- "bookable at …" on the Radar ----
   Two gates answered nightly (fixture on their board, market in their ids);
   the third — open right now, at a price — is the slip's to answer. */
.rd-book {
  display: inline-flex; align-items: center; gap: 4px; margin-top: 4px;
  font-size: 11px; line-height: 1; padding: 3px 7px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--muted); white-space: nowrap;
}
.rd-book.on { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); background: color-mix(in srgb, var(--accent) 9%, transparent); }
.rd-book.off { opacity: .8; }
.rd-book img { width: 12px; height: 12px; border-radius: 3px; background: #fff; }

/* ---- menus an admin can see but the public cannot ----
   Shown only while previewing as admin: the item stays where it is, dimmed,
   with a small mark, so the broken thing can be worked on in place. */
.menu-hidden { opacity: .55; }
.menu-hidden-mark {
  display: inline-block; margin-left: 5px; padding: 1px 5px; border-radius: 999px;
  font-size: 9px; line-height: 1.3; letter-spacing: .04em; text-transform: uppercase;
  background: color-mix(in srgb, var(--away) 22%, transparent); color: var(--away); vertical-align: middle;
}

/* The legend under the Predict page's probability bar. Redundant beside a
   wide bar that carries its own names, so it only appears on a phone. */
.ts-legend { display: none; }

/* Prediction tiles and starred cards rise into place, in order. A small,
   short movement — enough to feel assembled rather than dumped. */
@keyframes rise-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.ai-tile, .ai-star, .pr-star, .ai-scores li { animation: rise-in 360ms cubic-bezier(.2,.7,.2,1) both; }
.ai-tile:nth-child(2), .ai-star:nth-child(2), .pr-star:nth-child(2), .ai-scores li:nth-child(2) { animation-delay: 60ms; }
.ai-tile:nth-child(3), .ai-scores li:nth-child(3) { animation-delay: 120ms; }
.ai-scores li:nth-child(4) { animation-delay: 180ms; }
.ai-scores li:nth-child(5) { animation-delay: 240ms; }
@media (prefers-reduced-motion: reduce) { .ai-tile, .ai-star, .pr-star, .ai-scores li { animation: none; } }
