/* ==========================================================================
   Gravity Boys — PMG Sports broadcast styling
   A dark control-room palette, condensed chrome, oversized numbers.
   ========================================================================== */

:root {
  /* ONE width for the whole site.

     There were four numbers doing this job — .wrap, .topbar__in, .notices and
     a run of if/elseif in layout.php picking a --bar-max — and they disagreed:
     the event page ran to 1860 while the bank and the cup ran to 1400, so
     moving between two pages of the same site moved both edges of it.

     Any width that appears in more than one place is a width that will drift.
     Everything that has to share an edge reads this token and nothing else. */
  --wrap-max:  1860px;
  --bg:        #080b11;
  --bg-2:      #0d1119;
  --panel:     #121826;
  --panel-2:   #172033;
  --line:      #223049;
  --line-soft: #1a2437;
  --ink:       #e9eef7;
  --ink-2:     #a9b6cc;
  --ink-3:     #6d7c96;
  --accent:    #ff7a18;
  --accent-2:  #ffb703;
  --live:      #ff3b47;
  --good:      #35d07f;
  --bad:       #ff5a5f;
  --info:      #4ea8de;
  --radius:    14px;
  --radius-sm: 9px;
  --shadow:    0 18px 50px rgba(0, 0, 0, .45);
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", "Roboto Mono", Menlo, Consolas, monospace;
  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

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

/* ---- scrollbars -----------------------------------------------------------
   A default scrollbar on a dark site is a bright grey slab, and there are
   enough scrolling regions here — the standings board, the running order, the
   tables, the course strip — that leaving them alone looks like an oversight.

   Both syntaxes, because they are not interchangeable: `scrollbar-color` is
   the standard and is all Firefox supports, while `::-webkit-scrollbar` is
   what Chrome and Safari actually honour. Declared on `html` and inherited,
   so a new scrolling box is styled without anybody remembering to.

   The track is transparent rather than coloured: these sit on panels of four
   different shades, and a fixed track colour is a visible stripe on three of
   them. */
html {
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}

*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: var(--line);
  border-radius: 6px;
  /* Inset by a transparent border, so the thumb is slimmer than the gutter it
     runs in and does not touch the content beside it. */
  border: 2px solid transparent;
  background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover { background: #33456a; background-clip: padding-box; }
*::-webkit-scrollbar-thumb:active { background: var(--accent); background-clip: padding-box; }
/* Where two scrollbars meet. Unstyled it is a pale square in the corner. */
*::-webkit-scrollbar-corner { background: transparent; }

/* On the picture, where any furniture has to be part of the broadcast rather
   than part of the browser. */
.bc-overlay *::-webkit-scrollbar { width: 6px; height: 6px; }
.bc-overlay *::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, .22); border: 1px solid transparent;
}
.bc-overlay * { scrollbar-color: rgba(255, 255, 255, .22) transparent; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background:
    radial-gradient(1200px 600px at 50% -10%, #16203300, #0000),
    linear-gradient(180deg, #0b0f18 0%, var(--bg) 60%);
  color: var(--ink);
  font: 15px/1.55 var(--sans);
  min-height: 100vh;
  overflow-x: hidden;
}

h1, h2, h3, h4 { margin: 0 0 .5em; line-height: 1.15; font-weight: 750; letter-spacing: -.015em; }
h1 { font-size: clamp(26px, 4.2vw, 42px); }
h2 { font-size: clamp(19px, 2.4vw, 26px); }
h3 { font-size: 17px; }
p { margin: 0 0 1em; }
a { color: inherit; text-decoration: none; }
a:hover { color: var(--accent); }
img, svg { max-width: 100%; }
small { font-size: 12px; }
hr { border: 0; border-top: 1px solid var(--line-soft); margin: 22px 0; }

.skip { position: absolute; left: -9999px; }
.skip:focus { left: 12px; top: 12px; z-index: 200; background: var(--accent); color: #111; padding: 10px 14px; border-radius: 8px; }

/* --- shared type utilities ------------------------------------------------ */
.kicker {
  font-size: 11px; font-weight: 800; letter-spacing: .2em; text-transform: uppercase;
  color: var(--accent); margin: 0 0 8px;
}
.kicker--muted { color: var(--ink-3); }
/* A kicker that has to name the sport as well as the moment: the sport in its
   own colour, the rest of the line in the muted one, so "GRAVITY BOYS · NEXT
   ON THE NETWORK" reads as two facts rather than one long shout. */
.kicker--sport { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; color: var(--ink-3); }
.kicker--sport .kicker__sport { color: var(--c, var(--accent)); }
.kicker--sport .kicker__sep { opacity: .5; }
.lede { color: var(--ink-2); font-size: 16px; max-width: 62ch; }
.muted { color: var(--ink-3); }
.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.big { font-size: clamp(28px, 5vw, 46px); font-weight: 800; letter-spacing: -.03em; }
.up { text-transform: uppercase; letter-spacing: .13em; font-size: 11px; font-weight: 800; }
.good { color: var(--good); }
.bad  { color: var(--bad); }

/* --- the brand switcher --------------------------------------------------- *
 * There used to be two bars: a strip of sports above a strip of pages. Honest
 * about the hierarchy, miserable to look at — two rows of links competing for
 * the same glance and a fifth of a laptop screen gone before any content.
 *
 * The mode lives here instead. This is simultaneously the sign saying where
 * you are and the control that changes it, which is the only reason one bar
 * can carry what two were carrying. */
.switcher { position: relative; flex: none; }
.switcher__face {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; gap: 10px;
  padding: 6px 10px 6px 6px; border-radius: 12px;
  border: 1px solid transparent;
}
.switcher__face::-webkit-details-marker { display: none; }
.switcher__face:hover { background: var(--panel); border-color: var(--line-soft); }
.switcher[open] .switcher__face { background: var(--panel-2); border-color: var(--line); }
/* Whichever crest is in here, it is landscape and it is not square: 729×534 for
   the network, and 620×383 to 620×492 across the sports. Height-led with width
   auto so each one keeps its own proportions; a square box would squash the
   wordmark, and a squashed wordmark is a misspelling. No blend mode — they
   carry real alpha channels, so blending would only wash the colour out.

   `aspect-ratio: auto 4 / 3` is the pre-load reservation. The keyword `auto`
   means the image's real ratio wins the moment it is known and the 4/3 is only
   ever the fallback — without it the slot is 0px wide until the crest lands and
   the name beside it jumps 50-odd pixels to the right on every first paint.
   It is a fallback rather than a fixed ratio precisely because the four marks
   disagree about their shape. */
.switcher__bug {
  height: 38px; width: auto; aspect-ratio: auto 4 / 3; display: block; flex: none;
  filter: drop-shadow(0 2px 8px rgba(0, 0, 0, .6));
}
.switcher__face:hover .switcher__bug { filter: drop-shadow(0 2px 12px rgba(255, 122, 24, .3)) brightness(1.06); }
.switcher__id { display: flex; flex-direction: column; line-height: 1.1; min-width: 0; }
.switcher__id b {
  font-size: 14px; font-weight: 850; letter-spacing: -.01em; color: var(--ink);
  white-space: nowrap;
}
/* The parent, not an action. It was accent-coloured while it read as a way
   out, which is exactly what made it look like a claim about where you were.
   Muted, it reads as what it is: the network this sport is on. */
.switcher__id i {
  font-style: normal; font-size: 9px; font-weight: 800; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-3); margin-top: 2px;
  white-space: nowrap;
}
.switcher__chev { width: 10px; height: 6px; color: var(--ink-3); flex: none; }
.switcher[open] .switcher__chev { transform: rotate(180deg); }

.switcher__panel {
  position: absolute; left: 0; top: calc(100% + 8px); z-index: 80; min-width: 290px;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 6px; box-shadow: var(--shadow);
}
.switcher__opt {
  display: flex; align-items: center; gap: 11px; padding: 9px 10px; border-radius: 9px;
  color: var(--ink-2);
}
.switcher__opt:hover { background: var(--panel); color: var(--ink); }
.switcher__opt.is-on { background: var(--panel); color: var(--ink); }
.switcher__opt.is-on .switcher__mark { border-color: var(--c, var(--accent)); color: var(--c, var(--accent)); }
/* The slot is landscape because the marks are: every one of these crests is
   wider than it is tall, and a square slot would letterbox them down to about
   nothing. The plate stays — it is what the selected row's accent border lands
   on, and it holds the column of marks in line whatever shape the art is. */
.switcher__mark {
  width: 46px; height: 32px; flex: none; display: grid; place-items: center;
  border-radius: 8px; border: 1px solid var(--line); padding: 2px;
  font-size: 10.5px; font-weight: 900; letter-spacing: .04em; color: var(--ink-3);
  background: rgba(0, 0, 0, .3);
}
/* Sized both ways with object-fit doing the letterboxing, rather than natural
   size held back by max-height — see the note on .sporttile__art img, which is
   the same trick for the same reason. */
.switcher__mark img {
  display: block; width: 100%; height: 100%;
  object-fit: contain; object-position: center;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, .6));
}
.switcher__text { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; flex: 1; }
.switcher__text b { font-size: 13px; font-weight: 750; }
.switcher__text i { font-style: normal; font-size: 11px; color: var(--ink-3); }
.switcher__state {
  font-size: 8.5px; font-weight: 900; letter-spacing: .16em; text-transform: uppercase;
  color: var(--c, var(--ink-3)); flex: none;
}
/* On air right now, which at most one row can be. The live red rather than the
   sport's accent, and a pulsing dot, because this is the same fact the LIVE
   pill and the What's on dot state and it should look like it. */
.switcher__state.is-live {
  color: var(--live); display: flex; align-items: center; gap: 5px;
}
.switcher__state.is-live::before {
  content: ''; width: 5px; height: 5px; border-radius: 50%;
  background: var(--live); animation: pulse 1.4s infinite;
}
/* Announced but not built: greyed back the same way the sport strip greys the
   ones that are not on, so what you can actually watch is what your eye lands
   on. Opacity alone left a full-colour crest sitting beside a "soon". */
.switcher__opt.is-soon { opacity: .6; }
.switcher__opt.is-soon .switcher__mark img { filter: grayscale(.6) brightness(.8); }
@media (max-width: 900px) {
  .switcher__id { display: none; }
  .switcher__panel { min-width: 260px; }
}

/* --- auditioning commentary in the control booth -------------------------- */
.voicepreview { display: flex; flex-direction: column; gap: 3px; }
.voiceline {
  display: grid; grid-template-columns: 58px minmax(0, 1fr) auto; gap: 12px; align-items: baseline;
  padding: 7px 10px; border-radius: 6px; border-left: 3px solid var(--line);
  background: rgba(255, 255, 255, .015);
}
.voiceline.is-missing { opacity: .55; }
.voiceline.is-playing { border-left-color: var(--accent); background: rgba(255, 122, 24, .12); }
.voiceline__at { font-family: var(--mono); font-size: 11.5px; color: var(--ink-3); }
.voiceline__text { font-size: 13.5px; }
.voiceline__meta { font-family: var(--mono); font-size: 10.5px; color: var(--ink-3); white-space: nowrap; }
@media (max-width: 700px) {
  .voiceline { grid-template-columns: 46px minmax(0, 1fr); }
  .voiceline__meta { grid-column: 2; }
}

/* --- the network page ----------------------------------------------------- */
.netlead { display: flex; align-items: center; gap: 26px; flex-wrap: wrap; }
.netlead__logo { width: 220px; height: auto; flex: none; filter: drop-shadow(0 6px 24px rgba(0,0,0,.6)); }
.netlead h1 { margin-bottom: .3em; }

/* Sport tiles. The artwork is a full-bleed title card for two of the three and
   a cut-out mark for the other, so the art sits on its own plate and is
   contained rather than cropped — no logo gets its corners cut off. */
.sportstrip { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.sportstrip--compact { gap: 12px; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }

.sporttile {
  display: flex; flex-direction: column; overflow: hidden;
  background: linear-gradient(180deg, var(--panel) 0%, #101623 100%);
  border: 1px solid var(--line-soft); border-top: 3px solid var(--c, #ff7a18);
  border-radius: var(--radius); color: inherit;
  transition: transform .16s cubic-bezier(.2,.8,.3,1), border-color .16s ease;
}
a.sporttile:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--c, #ff7a18) 60%, var(--line));
  color: inherit;
}

/* Every logo is shown whole. Filling the tile would crop whichever edge did not
   fit, and these are wordmarks — a cropped wordmark is a misspelling. Where the
   art does not fill the plate, the plate shows, which is what a plate is for. */
/* No plate. Every mark has a real alpha channel, so it sits on the card's own
   background and reads as part of it rather than as an image pasted onto it. */
.sporttile__art { position: relative; height: 156px; padding: 14px 16px 4px; }
.sportstrip--compact .sporttile__art { height: 124px; padding: 12px 12px 2px; }
/* The image box fills the space and object-fit does the letterboxing.
 *
 * Sizing it the other way round — natural size, held back by max-height: 100% —
 * does not work here: the box's height is not definite from the image's point
 * of view, so that percentage is dropped, the image keeps its full natural
 * height, and the overflow slices it. Which is what was happening to all three
 * wordmarks. */
.sporttile__art img {
  display: block; width: 100%; height: 100%;
  object-fit: contain; object-position: center;
  filter: drop-shadow(0 6px 14px rgba(0, 0, 0, .55));
}
/* Everything but the live sport is greyed back, so what is on tonight is the
   thing your eye lands on. */
.sporttile:not(.is-live) .sporttile__art img { filter: grayscale(.55) brightness(.72); }
a.sporttile:hover .sporttile__art img { filter: none; }

.sporttile__badge {
  position: absolute; left: 12px; top: 10px;
  background: rgba(6,10,18,.72); color: var(--ink-3);
  padding: 3px 8px; border-radius: 3px;
  font-size: 8.5px; font-weight: 900; letter-spacing: .2em; text-transform: uppercase;
}
.sporttile.is-live .sporttile__badge { background: var(--c, #ff7a18); color: #14090f; }

.sporttile__body { padding: 12px 14px 14px; display: flex; flex-direction: column; gap: 3px; flex: 1; }
.sporttile__name { font-weight: 800; font-size: 15px; letter-spacing: -.01em; }
.sporttile__tag { font-size: 11.5px; color: var(--ink-3); }
.sporttile__blurb { font-size: 13px; color: var(--ink-3); margin: 8px 0 0; }

/* --- top bar -------------------------------------------------------------- */
.topbar {
  position: sticky; top: 0; z-index: 60;
  background: rgba(8, 11, 17, .88);
  backdrop-filter: saturate(150%) blur(14px);
  border-bottom: 1px solid var(--line-soft);
}
.topbar--admin { background: #12080b; border-bottom-color: #3a1720; }
.topbar__in {
  max-width: var(--wrap-max); margin: 0 auto; padding: 0 18px;
  display: flex; align-items: center; gap: 14px; height: 64px;
}
/* Inside a sport the bar carries its colour along the bottom edge. Nothing
   below has to be told which sport it is showing — the page already looks
   like it, because --accent is the sport's. */
.app--sport .topbar { box-shadow: inset 0 -2px 0 var(--accent); }
.brand { display: flex; align-items: center; gap: 10px; color: var(--accent); flex: none; }
.brand:hover { color: var(--accent-2); }
/* The supplied logo is rendered on a dark backdrop; screen blending drops what
   is left of it into the dark chrome instead of showing a grey plate. */
.brand--logo img { height: 72px; width: auto; display: block; mix-blend-mode: screen; }
.brand--logo:hover img { filter: brightness(1.08); }
.brand--big.brand--logo img { height: 96px; margin: 0 auto; }
/* The network mark carries a real alpha channel and must not be blended. */
.brand--net img { mix-blend-mode: normal; filter: drop-shadow(0 4px 18px rgba(0, 0, 0, .6)); }
.brand--net.brand--big img { height: 116px; }
/* The sport's own strapline, so the header says which of the network's sports
   this is without repeating the name that is already in the logo. */
.brand__sport {
  font-size: 10px; font-weight: 800; letter-spacing: .2em; text-transform: uppercase;
  color: var(--ink-3); white-space: nowrap;
}
@media (max-width: 1080px) { .brand__sport { display: none; } }
@media (max-width: 760px) {
  .brand--logo img { height: 52px; }
  .topbar__in { height: 56px; }
}
.brand__text { display: flex; flex-direction: column; line-height: 1; }
.brand__text b { font-size: 15px; letter-spacing: .06em; color: var(--ink); font-weight: 800; }
.brand__text i { font-style: normal; font-size: 9.5px; letter-spacing: .28em; color: var(--accent); font-weight: 700; margin-top: 3px; }
.brand--big { justify-content: center; margin-bottom: 26px; }

.mainnav { display: flex; gap: 2px; overflow-x: auto; scrollbar-width: none; flex: 1; }
.mainnav::-webkit-scrollbar { display: none; }
.mainnav a {
  padding: 8px 11px; border-radius: 8px; font-size: 12.5px; font-weight: 650;
  color: var(--ink-2); white-space: nowrap; letter-spacing: .01em;
}
.mainnav a:hover { background: var(--panel); color: var(--ink); }
.mainnav a.is-active { background: var(--panel-2); color: var(--ink); box-shadow: inset 0 -2px 0 var(--accent); }

.topbar__right { display: flex; align-items: center; gap: 10px; flex: none; }
.livepip {
  display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px; border-radius: 999px;
  background: rgba(255, 59, 71, .14); border: 1px solid rgba(255, 59, 71, .4);
  color: var(--live); font-size: 11px; font-weight: 850; letter-spacing: .14em;
}
/* The pip wears the colour of whatever is actually on, which is not
   necessarily the sport you are browsing. */
.livepip { box-shadow: inset 2px 0 0 var(--c, var(--live)); }
.livepip__dot { width: 7px; height: 7px; border-radius: 50%; background: var(--live); animation: pulse 1.4s infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.8); } }

/* --- "what's on": the guide, hung off the header ------------------------- */
.whatson { position: relative; flex: none; }
.whatson__face {
  list-style: none; cursor: pointer; display: flex; align-items: center; gap: 7px;
  padding: 6px 11px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--panel); color: var(--ink-2);
  font-size: 12px; font-weight: 650; white-space: nowrap;
}
.whatson__face::-webkit-details-marker { display: none; }
.whatson__face:hover { border-color: var(--accent); color: var(--ink); }
.whatson[open] .whatson__face { border-color: var(--accent); color: var(--ink); background: var(--panel-2); }
.whatson__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ink-3); }
.whatson__dot--live { background: var(--live); animation: pulse 1.4s infinite; }
.whatson__at { font-size: 11px; color: var(--ink-3); }
/* Wide enough that three hours of guide fits without scrolling, which is the
   whole point of the window being three hours. */
.whatson__panel {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 80;
  width: min(920px, calc(100vw - 36px));
  background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px; box-shadow: var(--shadow);
}
@media (max-width: 760px) { .whatson__label { display: none; } }

/* --- the guide ------------------------------------------------------------ *
 * Sports down the side, time across the top, programmes filling their slots.
 * Blocks are positioned as a percentage of the window rather than snapped to a
 * column, so a fixture at 19:07 sits at 19:07.
 *
 * The scale is the thing that makes this a guide rather than a bar chart.
 * A half-hour slot has to be wide enough to hold an event name, which is about
 * 150px — so an hour is 300, and the grid scrolls rather than squeezing. It was
 * built at half this and every programme came out as a coloured chip with no
 * room for a word in it. */
.guide__title { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 10px; }
.guide__title .up { color: var(--ink-3); }
.guide__all { font-size: 11.5px; font-weight: 700; color: var(--accent); }

.guide__scroll { overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; }
/* Pixels per hour — the zoom. A block runs from its start to the next scheduled
   programme anywhere on the network, which keeps every block a readable width,
   and the timeline is scaled up so the names fit. The grid scrolls
   horizontally, so this is free: raise these two numbers to zoom in further,
   lower them to fit more of the afternoon on screen. */
.guide__grid { min-width: calc(168px + var(--hours, 3) * 760px); }
.guide--compact .guide__grid { min-width: calc(132px + var(--hours, 3) * 620px); }

.guide__ruler { display: flex; align-items: flex-end; height: 24px; }
.guide__rulerpad { width: 168px; flex: none; }
.guide--compact .guide__rulerpad { width: 132px; }
.guide__rulertrack { position: relative; flex: 1; height: 100%; }
.guide__tick {
  position: absolute; bottom: 3px; transform: translateX(-1px);
  font-family: var(--mono); font-size: 11px; font-weight: 600; color: var(--ink-3);
  padding-left: 7px; border-left: 1px solid var(--line); line-height: 1.1;
  white-space: nowrap;
}
.guide__tick small { font-size: 9px; opacity: .7; margin-left: 1px; }
.guide__nowlabel {
  position: absolute; bottom: 3px; transform: translateX(-50%);
  font-size: 8.5px; font-weight: 900; letter-spacing: .14em; text-transform: uppercase;
  color: var(--live);
}

.guide__row { display: flex; align-items: stretch; border-top: 1px solid var(--line-soft); }
.guide__row:last-child { border-bottom: 1px solid var(--line-soft); }
.guide__row.is-current { background: rgba(255, 255, 255, .02); }
.guide__row { --c: var(--accent); }

/* The row label. A sport, not a channel — so no number, and the name gets the
   room the number would have taken. */
.guide__sport {
  width: 168px; flex: none; display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; color: var(--ink-2);
  border-right: 1px solid var(--line-soft);
  box-shadow: inset 3px 0 0 var(--c);
}
.guide--compact .guide__sport { width: 132px; padding: 8px 10px; gap: 8px; }
.guide__sport:hover { color: var(--ink); background: rgba(255, 255, 255, .03); }
.guide__mark {
  display: grid; place-items: center; width: 28px; height: 28px; flex: none;
  border-radius: 7px; border: 1px solid color-mix(in srgb, var(--c) 50%, transparent);
  background: color-mix(in srgb, var(--c) 14%, transparent);
  color: var(--c); font-size: 10px; font-weight: 900; letter-spacing: .03em;
}
.guide--compact .guide__mark { width: 24px; height: 24px; font-size: 9px; }
.guide__sportname { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.guide__sportname b { font-size: 12.5px; font-weight: 750; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.guide--compact .guide__sportname b { font-size: 11.5px; }
.guide__sportname i { font-style: normal; font-size: 10px; color: var(--ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.guide__row.is-offair .guide__sport { opacity: .5; }

/* Tall enough for what is in a listing.
   A programme block carries three lines — the time, the name and what happened
   — and fifty-eight pixels is not three lines at these sizes, so the last one
   was being sliced through the middle by the block's own overflow. */
.guide__lane { position: relative; flex: 1; min-height: 84px; padding: 4px 0; }
.guide--compact .guide__lane { min-height: 72px; padding: 4px 0; }
.guide__nowline {
  position: absolute; top: 0; bottom: 0; width: 2px; background: var(--live);
  opacity: .6; z-index: 4; pointer-events: none;
}
.guide__nothing {
  position: absolute; inset: 0; display: flex; align-items: center; padding-left: 14px;
  font-size: 12px; color: var(--ink-3); font-style: italic;
}

/* One programme: a solid block that fills its slot, the way a listing does. */
.prog {
  position: absolute; top: 4px; bottom: 4px;
  display: flex; flex-direction: column; justify-content: center; gap: 1px;
  padding: 7px 11px; overflow: hidden;
  /* Each line is one row that ellipsises; none of them may be squeezed to
     make space, because a flex item that shrinks below its line height gets
     cut through the letters rather than shortened. */
  line-height: 1.25;
  /* A block's width is set by how long the programme runs, not by the page —
     so what fits inside it is a question about the block, and only a container
     query can ask it. */
  container-type: inline-size;
  background: linear-gradient(180deg, var(--panel-2) 0%, var(--panel) 100%);
  border: 1px solid var(--line);
  border-left: 4px solid var(--c);
  border-radius: 7px; color: var(--ink-2);
  /* The blocks butt up against each other; without this they share an edge and
     the row reads as one long box. */
  margin-right: 3px;
}
.guide--compact .prog { padding: 6px 9px; }
.prog:hover {
  background: var(--panel-2); color: var(--ink);
  border-color: var(--c); z-index: 5;
}
.prog__top { display: flex; align-items: center; gap: 6px; flex: none; min-height: 13px; }
.prog__when { font-size: 10.5px; font-weight: 600; color: var(--ink-3); letter-spacing: .02em; }
.prog__name {
  flex: none;
  font-size: 13px; font-weight: 750; color: var(--ink); line-height: 1.25;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.guide--compact .prog__name { font-size: 12px; }
.prog__meta {
  flex: none;
  font-size: 11px; color: var(--ink-3); line-height: 1.25;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* A block narrower than about three words has no room for the trailing line,
   and half a word of ellipsis is worse than nothing. */
@container (max-width: 120px) { .prog__meta { display: none; } }
.prog__live { font-size: 10px; color: var(--live); font-weight: 900; letter-spacing: .1em; }
.prog__flag {
  font-size: 8px; font-weight: 900; letter-spacing: .14em; color: var(--accent-2);
  border: 1px solid color-mix(in srgb, var(--accent-2) 45%, transparent);
  border-radius: 3px; padding: 0 4px;
}
.prog.is-live {
  background: linear-gradient(180deg, rgba(255, 59, 71, .18) 0%, rgba(255, 59, 71, .08) 100%);
  border-color: rgba(255, 59, 71, .5);
}
/* Already run. Dimmed so your eye goes to what is next, but not so far that
   the result becomes unreadable — the last one out is the one you came for. */
.prog.is-finished { opacity: .58; }
.prog.is-finished:hover { opacity: 1; }
.prog.is-champ { border-color: color-mix(in srgb, var(--accent-2) 60%, var(--line)); }

.guidehead { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; flex-wrap: wrap; margin-bottom: 20px; }
.guidehead__filters { display: flex; flex-direction: column; gap: 8px; align-items: flex-end; }
@media (max-width: 760px) { .guidehead__filters { align-items: flex-start; } }

/* --- segmented filters ---------------------------------------------------- */
.segbar { display: inline-flex; flex-wrap: wrap; gap: 3px; padding: 3px; background: var(--panel); border: 1px solid var(--line-soft); border-radius: 10px; }
.seg {
  padding: 6px 12px; border-radius: 7px; font-size: 12px; font-weight: 650;
  color: var(--ink-3); white-space: nowrap;
}
.seg:hover { color: var(--ink); background: var(--panel-2); }
.seg.is-on { background: var(--panel-2); color: var(--ink); box-shadow: inset 0 -2px 0 var(--c, var(--accent)); }
.seg.is-soon { opacity: .55; }
.segbar--sm .seg { padding: 4px 10px; font-size: 11px; }

/* A sport badge on a row that could have come from any of them. */
.sportchip {
  display: inline-grid; place-items: center; min-width: 26px; height: 20px; padding: 0 5px;
  border-radius: 5px; border: 1px solid color-mix(in srgb, var(--c, var(--accent)) 45%, transparent);
  background: color-mix(in srgb, var(--c, var(--accent)) 14%, transparent);
  color: var(--c, var(--accent));
  font-size: 9.5px; font-weight: 900; letter-spacing: .06em;
}
a.sportchip:hover { background: color-mix(in srgb, var(--c, var(--accent)) 26%, transparent); color: var(--ink); }
.entrant__tail { display: flex; align-items: center; gap: 8px; justify-self: end; }

/* --- board header --------------------------------------------------------- *
 * Every table that belongs to exactly one sport wears this. A standings table
 * looks identical whichever sport produced it, which is precisely why the
 * label cannot be a footnote. */
.boardhead {
  display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap;
  padding: 14px 16px; margin-bottom: 20px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--c) 13%, transparent), transparent 62%);
  border: 1px solid var(--line-soft); border-left: 4px solid var(--c);
  border-radius: var(--radius);
}
.boardhead__id { display: flex; align-items: center; gap: 14px; min-width: 0; }
.boardhead__mark {
  width: 46px; height: 46px; flex: none; display: grid; place-items: center;
  border-radius: 11px; border: 1px solid color-mix(in srgb, var(--c) 55%, transparent);
  background: color-mix(in srgb, var(--c) 15%, transparent); color: var(--c);
  font-size: 15px; font-weight: 900; letter-spacing: .02em;
}
.boardhead__words { min-width: 0; }
.boardhead__sport {
  font-size: 10.5px; font-weight: 800; letter-spacing: .18em; text-transform: uppercase; color: var(--c);
}
.boardhead__title { margin: 2px 0 0; font-size: clamp(22px, 3vw, 32px); }
.boardhead__sub { margin: 4px 0 0; font-size: 12.5px; }
.boardhead__switch { display: flex; gap: 5px; flex: none; }
.boardchip {
  display: grid; place-items: center; width: 38px; height: 34px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--panel);
  font-size: 11px; font-weight: 900; color: var(--ink-3);
}
a.boardchip:hover { color: var(--ink); border-color: var(--c); }
.boardchip.is-on { color: var(--c); border-color: var(--c); background: color-mix(in srgb, var(--c) 14%, transparent); }
.boardchip.is-soon { opacity: .4; cursor: not-allowed; }

/* --- per-sport tabs on a shared thing ------------------------------------- */
.sporttabs { display: flex; gap: 4px; margin: 0 0 18px; flex-wrap: wrap; }
.sporttab {
  display: flex; align-items: center; gap: 8px; padding: 7px 13px 7px 8px;
  border-radius: 10px; border: 1px solid var(--line-soft); background: var(--panel);
  color: var(--ink-3);
}
.sporttab:hover { color: var(--ink); border-color: var(--line); }
.sporttab.is-on {
  color: var(--ink); border-color: color-mix(in srgb, var(--c) 55%, transparent);
  background: color-mix(in srgb, var(--c) 10%, var(--panel));
  box-shadow: inset 0 -2px 0 var(--c);
}
.sporttab__mark {
  display: grid; place-items: center; width: 24px; height: 24px; border-radius: 6px;
  border: 1px solid var(--line); font-size: 9.5px; font-weight: 900; color: var(--c);
}
.sporttab__name { font-size: 12.5px; font-weight: 700; }
.tabs__mark { color: var(--accent); margin-left: 4px; font-weight: 900; }

/* The scope label over a block of figures. */
.statgrid--sport { position: relative; }
.statgrid__scope {
  position: absolute; top: -9px; left: 12px; padding: 1px 8px; border-radius: 4px;
  background: var(--bg-2); border: 1px solid color-mix(in srgb, var(--c) 50%, transparent);
  color: var(--c); font-size: 9px; font-weight: 900; letter-spacing: .16em; text-transform: uppercase;
}
.panel__head--sport h3 { display: flex; align-items: center; gap: 7px; }
.panel__sport {
  display: grid; place-items: center; min-width: 24px; height: 18px; padding: 0 5px; border-radius: 4px;
  border: 1px solid color-mix(in srgb, var(--c) 50%, transparent);
  background: color-mix(in srgb, var(--c) 14%, transparent);
  color: var(--c); font-size: 9px; font-weight: 900; letter-spacing: .05em;
}

/* --- competitor renders --------------------------------------------------- *
 * A box the shared WebGL canvas scissors its picture into. The flat drawing
 * ships inside and stays put until the renderer claims the box — and forever
 * if there is no WebGL context to be had. */
.playerview {
  position: relative; width: var(--size, 160px); height: var(--size, 160px);
  flex: none; display: grid; place-items: center;
}
.playerview__flat { display: block; line-height: 0; transition: opacity .25s ease; }
.playerview.is-live .playerview__flat { opacity: 0; }
.playerview svg { display: block; }

.roster { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); padding: 14px; }
.rostercard {
  display: flex; flex-direction: column; gap: 6px; padding: 10px 12px 12px;
  background: var(--panel); border: 1px solid var(--line-soft);
  border-top: 3px solid var(--tc, var(--accent)); border-radius: var(--radius-sm);
  color: inherit; transition: transform .16s cubic-bezier(.2,.8,.3,1), border-color .16s ease;
}
.rostercard:hover { transform: translateY(-3px); border-color: var(--tc, var(--accent)); color: inherit; }
.rostercard__art { position: relative; display: grid; place-items: center; }
.rostercard__num {
  position: absolute; left: 0; top: 0;
  font-family: var(--mono); font-size: 18px; font-weight: 800;
  color: var(--tc, var(--accent)); opacity: .55;
}
.rostercard__body { display: flex; flex-direction: column; gap: 2px; }
.rostercard__name { font-size: 13.5px; font-weight: 800; }
.rostercard__nick { font-size: 11.5px; color: var(--ink-3); font-style: italic; }
.rostercard__note { font-size: 11px; color: var(--ink-3); }
.rostercard .formline { margin-top: 4px; }

/* The sport's short mark on the broadcast bug. */
.bug__sport {
  font-size: 9px; font-weight: 900; letter-spacing: .1em;
  color: var(--sport, var(--accent)); align-self: center;
}

.wallet {
  font-family: var(--mono); font-size: 13px; font-weight: 700; color: var(--accent-2);
  background: var(--panel); border: 1px solid var(--line); border-radius: 8px; padding: 5px 10px;
}
.wallet small { color: var(--ink-3); margin-left: 5px; font-weight: 500; }
.chipbtn {
  font-size: 12px; font-weight: 650; padding: 6px 11px; border-radius: 999px;
  background: var(--panel); border: 1px solid var(--line); color: var(--ink-2);
}
.chipbtn:hover { border-color: var(--accent); color: var(--ink); }

.avatar {
  width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center;
  font-size: 12px; font-weight: 800; color: #0b0e14; flex: none;
  background: hsl(calc(var(--seed, 1) * 37deg) 68% 62%);
}
.usermenu { position: relative; }
.usermenu summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; gap: 8px;
  padding: 4px 8px 4px 4px; border-radius: 999px; border: 1px solid var(--line); background: var(--panel);
}
.usermenu summary::-webkit-details-marker { display: none; }
.usermenu__name { font-size: 12.5px; font-weight: 650; }
.usermenu__panel {
  position: absolute; right: 0; top: calc(100% + 8px); min-width: 190px; z-index: 70;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 6px; box-shadow: var(--shadow); display: flex; flex-direction: column;
}
.usermenu__panel a, .usermenu__panel button {
  padding: 9px 11px; border-radius: 7px; font-size: 13px; text-align: left;
  background: none; border: 0; color: var(--ink-2); font: inherit; cursor: pointer; width: 100%;
}
.usermenu__panel a:hover, .usermenu__panel button:hover { background: var(--panel); color: var(--ink); }
.usermenu__panel .is-highlight { color: var(--accent); font-weight: 700; }

/* --- layout --------------------------------------------------------------- */
/* One width, and it is --wrap-max. The modifiers are kept as no-ops rather
   than deleted: pages set them, the markup is full of them, and a class that
   silently stops existing is the kind of thing that comes back as "why is
   this page narrow" six months later. The control booth is not an exception
   either — leaving it at 1300 while the bar above it read the shared token
   would be one mismatch traded for another. */
.wrap { max-width: var(--wrap-max); margin: 0 auto; padding: 22px 18px 70px; }
.wrap--wide, .wrap--full, .wrap--admin { max-width: var(--wrap-max); }
.plainwrap { max-width: 560px; margin: 0 auto; padding: 46px 18px 70px; }
.plainwrap--wide { max-width: 1080px; }

.grid { display: grid; gap: 18px; }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.split { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 18px; align-items: start; }
.split--wide { grid-template-columns: minmax(0, 1fr) 400px; }
/* `minmax(0, 1fr)`, never a bare `1fr`. A single-column `1fr` is
   `minmax(auto, 1fr)`, whose `auto` minimum lets a wide child (the 16:9
   broadcast panel, a long unbroken string) stretch the column past the screen
   edge — the whole page then renders zoomed out to fit it. `minmax(0, …)` pins
   the minimum at zero so the child is made to fit the column instead. */
@media (max-width: 1080px) { .split, .split--wide { grid-template-columns: minmax(0, 1fr); } }

.stack { display: flex; flex-direction: column; gap: 18px; }
.row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.row--tight { gap: 6px; }
.row--between { justify-content: space-between; }
.spacer { flex: 1; }

/* --- panels --------------------------------------------------------------- */
.panel {
  background: linear-gradient(180deg, var(--panel) 0%, #101623 100%);
  border: 1px solid var(--line-soft); border-radius: var(--radius); padding: 18px;
}
.panel--flush { padding: 0; overflow: hidden; }
.panel--accent { border-color: rgba(255, 122, 24, .35); }
.panel__head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 18px; border-bottom: 1px solid var(--line-soft); background: rgba(255,255,255,.015);
}
.panel__head h2, .panel__head h3 { margin: 0; }
.panel__body { padding: 18px; }
/* For a body whose content brings its own edges — the guide, which is drawn
   right up to the panel's border on purpose. */
.panel__body--flush { padding: 10px 0 0; }
.panel__foot { padding: 12px 18px; border-top: 1px solid var(--line-soft); background: rgba(0,0,0,.16); }

.card {
  background: var(--panel); border: 1px solid var(--line-soft); border-radius: var(--radius-sm);
  padding: 14px; display: block;
}
.card:hover { border-color: var(--line); }

/* --- buttons -------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 11px 17px; border-radius: 9px; font: inherit; font-weight: 700; font-size: 13.5px;
  border: 1px solid var(--line); background: var(--panel-2); color: var(--ink);
  cursor: pointer; transition: transform .08s ease, background .15s ease, border-color .15s ease;
  text-align: center;
}
.btn:hover { background: #1d2840; color: var(--ink); border-color: #2c3c5c; }
.btn:active { transform: translateY(1px); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
.btn--primary { background: linear-gradient(180deg, #ff8a2b, #f06a05); border-color: #ffa050; color: #180c02; }
.btn--primary:hover { background: linear-gradient(180deg, #ffa04d, #ff7a18); color: #180c02; }
.btn--ghost { background: transparent; }
.btn--danger { background: #3a1116; border-color: #6b1f28; color: #ffb3b8; }
.btn--sm { padding: 7px 12px; font-size: 12.5px; }
.btn--lg { padding: 14px 24px; font-size: 15px; }
.btn--block { width: 100%; }

/* --- forms ---------------------------------------------------------------- */
label { display: block; font-size: 12px; font-weight: 700; letter-spacing: .06em;
        text-transform: uppercase; color: var(--ink-3); margin: 0 0 6px; }
input[type=text], input[type=email], input[type=password], input[type=number], input[type=search],
select, textarea {
  width: 100%; padding: 11px 13px; border-radius: 9px; font: inherit;
  background: #0b101a; border: 1px solid var(--line); color: var(--ink);
}
input:focus, select:focus, textarea:focus { outline: 2px solid rgba(255,122,24,.5); outline-offset: 1px; border-color: var(--accent); }
textarea { resize: vertical; min-height: 76px; }
.field { margin-bottom: 14px; }
.field__hint { font-size: 12px; color: var(--ink-3); margin-top: 6px; }
.check { display: flex; align-items: center; gap: 9px; }
.check input { width: auto; }
.check label { margin: 0; text-transform: none; letter-spacing: 0; font-size: 13.5px; color: var(--ink-2); font-weight: 500; }
fieldset { border: 1px solid var(--line-soft); border-radius: var(--radius-sm); padding: 14px; margin: 0 0 14px; }
legend { font-size: 11px; text-transform: uppercase; letter-spacing: .16em; color: var(--ink-3); padding: 0 6px; }

/* --- notices -------------------------------------------------------------- */
.notices { max-width: var(--wrap-max); margin: 14px auto 0; padding: 0 18px; display: grid; gap: 8px; }
.notices--inline { margin-bottom: 18px; padding: 0; }
.notice {
  padding: 11px 15px; border-radius: 9px; font-size: 13.5px; font-weight: 550;
  border: 1px solid var(--line); background: var(--panel);
}
.notice--success { border-color: rgba(53,208,127,.4); background: rgba(53,208,127,.09); color: #b6f0d1; }
.notice--error { border-color: rgba(255,90,95,.4); background: rgba(255,90,95,.09); color: #ffc4c6; }
.notice--info { border-color: rgba(78,168,222,.4); background: rgba(78,168,222,.09); color: #c1e2f6; }

/* --- tables --------------------------------------------------------------- */
.tablewrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table.data { width: 100%; border-collapse: collapse; font-size: 13.5px; min-width: 460px; }
table.data th {
  text-align: left; font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-3); font-weight: 800; padding: 10px 12px; border-bottom: 1px solid var(--line);
  white-space: nowrap; position: sticky; top: 0; background: var(--panel);
}
table.data td { padding: 10px 12px; border-bottom: 1px solid var(--line-soft); vertical-align: middle; }
table.data tbody tr:hover { background: rgba(255,255,255,.022); }
table.data .r { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; }
table.data .c { text-align: center; }
table.data tfoot td { color: var(--ink-3); font-size: 12px; }

/* On a phone a wide table must scroll *inside its own box*, never widen the
   page. A `min-width: 460px` table with real content runs to 600px+, and a
   mobile browser does not clip that against `body { overflow-x: hidden }` — it
   widens the layout viewport to fit it and renders the whole page shrunk to
   suit, which reads as "the site loads at 50%". Most `.data` tables already sit
   in a `.tablewrap`; this covers the ones that do not, without a wrapper in
   every view.

   `display: block` turns the table itself into the scroll box (its rows keep
   their table layout via an anonymous inner table, so columns still line up).
   `min-width: 0` drops the 460px floor that would otherwise re-widen the page;
   the content sizes itself and scrolls past the right edge instead. `overflow-y`
   is pinned to `visible`'s intent by clipping — a table never overflows
   vertically — so the coerced `auto` cannot add a phantom vertical scrollbar,
   and the sticky header (meaningless in a horizontal scroller) stands down. */
@media (max-width: 720px) {
  table.data {
    display: block;
    width: 100%;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
  }
  table.data thead th { position: static; }
}

/* --- team + competitor identity ------------------------------------------- */
.teamdot { width: 10px; height: 10px; border-radius: 3px; display: inline-block; flex: none; }
.teamtag {
  display: inline-flex; align-items: center; gap: 7px; font-weight: 700; font-size: 13px;
}
.teamtag__badge {
  width: 30px; height: 22px; border-radius: 5px; display: grid; place-items: center;
  font-size: 10px; font-weight: 900; letter-spacing: .04em; color: #0a0d14; flex: none;
}
.pos {
  display: inline-grid; place-items: center; min-width: 26px; height: 26px; padding: 0 6px;
  border-radius: 6px; background: var(--panel-2); font-family: var(--mono);
  font-weight: 800; font-size: 13px; border: 1px solid var(--line);
}
.pos--1 { background: linear-gradient(180deg, #ffd166, #e0a33e); color: #2a1a00; border-color: #ffe4a3; }
.pos--2 { background: linear-gradient(180deg, #dfe6f2, #b3bdcd); color: #1a1f28; border-color: #eef2f8; }
.pos--3 { background: linear-gradient(180deg, #d79a63, #b1783f); color: #2a1704; border-color: #e8bd93; }
.pos--dnf { background: #2a1418; color: #ff8f95; border-color: #4a1e26; }

.formline { display: inline-flex; gap: 3px; }
.formline span {
  width: 20px; height: 20px; border-radius: 4px; display: grid; place-items: center;
  font-family: var(--mono); font-size: 10.5px; font-weight: 700;
  background: var(--panel-2); color: var(--ink-2); border: 1px solid var(--line-soft);
}
.formline .f1 { background: rgba(255,209,102,.2); color: #ffd166; border-color: rgba(255,209,102,.4); }
.formline .f2, .formline .f3 { background: rgba(53,208,127,.14); color: #7fe0ac; border-color: rgba(53,208,127,.3); }
.formline .fx { background: rgba(255,90,95,.14); color: #ff9297; border-color: rgba(255,90,95,.3); }

.pill {
  display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 700; letter-spacing: .04em;
  background: var(--panel-2); border: 1px solid var(--line); color: var(--ink-2);
}
.pill--live { background: rgba(255,59,71,.15); border-color: rgba(255,59,71,.45); color: #ff8a8f; }
.pill--soon { background: rgba(255,183,3,.14); border-color: rgba(255,183,3,.4); color: var(--accent-2); }
.pill--done { background: rgba(109,124,150,.14); }
.pill--champ { background: linear-gradient(180deg,#ffd166,#e0a33e); border-color:#ffe4a3; color:#2a1a00; }

/* ==========================================================================
   Broadcast
   ========================================================================== */
.broadcast {
  position: relative; background: #05070b; border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow);
}
.broadcast--sticky { position: sticky; top: 100px; z-index: 30; }
.broadcast__stage { position: relative; aspect-ratio: 16 / 9; width: 100%; background: #05070b; overflow: hidden; }
.broadcast__stage canvas { position: absolute; inset: 0; width: 100% !important; height: 100% !important; display: block; z-index: 1; }

/* Fullscreen. The whole panel becomes the screen, black, with the 16:9 picture
   grown to the largest 16:9 that fits and centred in it — so the letterbox is
   balanced rather than a single black bar under a top-aligned picture, and on a
   phone in landscape the picture fills the height edge to edge. Without this the
   panel stayed `display:block` and the stage kept its in-flow position, which is
   exactly the "went fullscreen but the video didn't fill it" report.

   The standard and `-webkit-` (iOS Safari) pseudo-classes are written as
   separate selectors on purpose: a browser drops the *entire* selector list if
   it does not recognise one of them, so sharing a rule would lose it for both.
   These beat the mobile landscape cap on `.broadcast__stage` on specificity, so
   they win even while that media query is active. */
.broadcast:fullscreen,
.broadcast:-webkit-full-screen {
  width: 100vw; height: 100vh; max-width: none;
  display: flex; align-items: center; justify-content: center;
  background: #000; border: 0; border-radius: 0;
}
.broadcast:fullscreen .broadcast__stage,
.broadcast:-webkit-full-screen .broadcast__stage {
  width: min(100vw, calc(100vh * 16 / 9));
  height: min(100vh, calc(100vw * 9 / 16));
  max-height: 100vh; margin: 0;
}
.broadcast:fullscreen .bc-controls,
.broadcast:-webkit-full-screen .bc-controls {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 10; border-top: 0;
  background: linear-gradient(180deg, rgba(10, 14, 22, 0), rgba(10, 14, 22, .92));
}

/* Sits inside the stage so it can never reach down over the control bar. */
.bc-overlay { position: absolute; inset: 0; z-index: 3; pointer-events: none; font-family: var(--sans); }
.bc-overlay > * { pointer-events: none; }

/* top-left score bug */
.bug {
  position: absolute; left: 14px; top: 14px; display: flex; align-items: stretch;
  border-radius: 7px; overflow: hidden; box-shadow: 0 6px 24px rgba(0,0,0,.55);
  font-size: 12px; backdrop-filter: blur(6px); max-width: min(60%, 420px);
}
.bug__brand {
  background: linear-gradient(180deg,#ff8a2b,#e56a05); color: #14090f;
  padding: 7px 10px; font-weight: 900; letter-spacing: .12em; font-size: 10px; display: grid; place-items: center;
}
/* The sport's own mark, in the bug beside the network's.
   The `.bug ` prefix is doing real work: `.bug__sport` is also a chip in the
   header strip, where it is nine-pixel text and should stay that way. */
.bug .bug__sport {
  /* A 60%-white frosted panel behind the sport mark rather than the near-black
     the rest of the bug uses — kept deliberately fresh and light, and (with the
     `align-items: stretch` on `.bug`) full height, so it is a clean chip the
     same height as the network flash and the title strip rather than a short
     dark box with the track showing above and below it. */
  background: rgba(255,255,255,.6); padding: 4px 9px; display: grid; place-items: center;
  border-left: 1px solid rgba(255,255,255,.28);
}
.bug__mark { display: block; height: 24px; width: auto; max-width: 74px; object-fit: contain; }
.broadcast.is-mini .bug__mark { height: 18px; max-width: 56px; }
@media (max-width: 760px) { .bug__mark { height: 18px; max-width: 52px; } }
.bug__body { background: rgba(9,13,20,.85); padding: 7px 12px; display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.bug__title { font-weight: 750; letter-spacing: .02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bug__sub { font-size: 10.5px; color: var(--ink-3); letter-spacing: .1em; text-transform: uppercase; }
.bug__clock {
  background: rgba(9,13,20,.85); padding: 7px 12px; display: grid; place-items: center;
  font-family: var(--mono); font-weight: 700; border-left: 1px solid rgba(255,255,255,.08);
}
/* lap counter — only present on a multi-lap event */
.bug__lap {
  background: rgba(9,13,20,.85); padding: 7px 11px; display: flex; align-items: baseline; gap: 1px;
  font-family: var(--mono); border-left: 1px solid rgba(255,255,255,.08);
}
.bug__lap-n { font-weight: 800; font-size: 15px; color: #fff; }
.bug__lap-of { font-size: 11px; color: var(--ink-3); }
.bug__lap.is-final { color: var(--accent); animation: lapFlash 1.1s ease-in-out infinite; }
.bug__lap.is-final .bug__lap-n { color: var(--accent); }
@keyframes lapFlash { 0%, 100% { opacity: 1; } 50% { opacity: .6; } }

/* live badge */
.bc-live {
  position: absolute; right: 14px; top: 14px; display: flex; align-items: center; gap: 7px;
  background: rgba(255,59,71,.92); color: #fff; border-radius: 5px; padding: 5px 10px;
  font-size: 10.5px; font-weight: 900; letter-spacing: .18em; box-shadow: 0 6px 20px rgba(0,0,0,.5);
}
.bc-live i { width: 6px; height: 6px; border-radius: 50%; background: #fff; animation: pulse 1.3s infinite; }
.bc-live.is-replay { background: rgba(78,168,222,.92); }

/* right-hand running order */
.bc-order {
  position: absolute; right: 14px; top: 56px; width: 218px; max-height: calc(100% - 120px);
  display: flex; flex-direction: column; gap: 2px; overflow: hidden;
}
.bc-order__row {
  display: grid; grid-template-columns: 20px 20px 1fr auto; gap: 7px; align-items: center;
  /* The plate the text is legible against, and the only thing standing between
     white type and whatever the camera happens to be pointing at. It is set as
     background-*color* rather than the shorthand on purpose: every state below
     paints its tint as a background-*image*, which composites on top of this
     instead of replacing it. See the note above .is-gold. */
  background-color: rgba(9,13,20,.82); border-left: 3px solid var(--c, #666);
  padding: 4px 8px; font-size: 11.5px; border-radius: 0 4px 4px 0;
  /* Positions change by sliding — but the move is driven from app.js with
     element.animate(), not from here. A CSS transition has to survive a class
     being toggled between two frames and a `transition-duration` override
     applied to everything by the reduced-motion block, and it did not: this
     was written twice and silently killed both times. What is left here is
     only the hint that the row moves. */
  will-change: transform;
  position: relative;
}
.bc-order__row.is-me {
  background-image: linear-gradient(rgba(255,122,24,.28), rgba(255,122,24,.28));
}
.bc-order__pos { font-family: var(--mono); font-weight: 800; color: var(--ink-2); }
.bc-order__name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 600; }
.bc-order__gap { font-family: var(--mono); font-size: 10.5px; color: var(--ink-3); }
.bc-order__crest { width: 20px; height: 20px; display: block; }
/* The score, for a sport that has one as well as a result — Yeet Trials shows
   the mark in the gap column and the score, which is the mark plus what got
   destroyed reaching it, here. Zero width when empty, so a sport with no score
   is laid out exactly as it was before this existed. */
.bc-order__score {
  font-family: var(--mono); font-weight: 800; font-size: 12.5px;
  color: var(--sport, var(--accent)); text-align: right;
  white-space: nowrap;
}
/* A fifth column, and only when there is a fifth thing to put in it.
   Without this the score span had no column to sit in and grid put it on an
   implicit *second row* — so every competitor who had thrown took two lines and
   the board came apart. The row is 218px wide, so the extra column is paid for
   by tightening everything else and letting the name ellipsis, which is what
   the name column has always been for. */
.bc-order__row.has-score {
  grid-template-columns: 18px 18px 1fr auto auto;
  gap: 5px;
}
.bc-order__row.has-score .bc-order__gap { font-size: 9.5px; opacity: .72; }
.bc-order__row:not(.has-score) .bc-order__score { display: none; }

/* How much of this competitor is left, for a sport where that is the story.
   Chunkle Derby, and only that: a descent has nothing to report here and its
   rows are laid out exactly as they were before this existed, because the
   element is not built at all unless the renderer sends a number for it.

   It goes on the running order rather than on a board of its own. It had one —
   sixteen rows of name and bar down the other side of the picture — and two
   running orders facing each other across the stage is the viewer reading the
   same field twice to get one answer. */
.bc-order__row.has-hp {
  grid-template-columns: 18px 18px 1fr 34px auto;
  gap: 5px;
}
.bc-order__row.has-hp.has-score {
  grid-template-columns: 16px 16px 1fr 30px auto auto;
  gap: 4px;
}
.bc-order__hp {
  height: 6px; border-radius: 3px; overflow: hidden;
  background: rgba(255,255,255,.16);
}
.bc-order__hp i {
  display: block; height: 100%; width: 100%;
  background: #6fd36f; transition: width .3s ease;
}
.bc-order__hp.is-low i { background: #ffb703; }
.bc-order__hp.is-crit i { background: #ff5252; }
/* A wreck keeps its bar, empty, rather than losing it — a row with no bar at
   all reads as a competitor who has not started. */
.bc-order__row.is-out .bc-order__hp { opacity: .5; }

/* Home, and where.
   The left edge of the row is already the team colour, so the medal goes on
   the fill and the position number — the two parts of the row that are not
   spoken for. Podium first, then everyone else who got round, then anybody
   who left the course.

   Every one of these is background-IMAGE. They used to be `background`, which
   is shorthand and therefore reset background-color to transparent along the
   way — so the moment a row went gold it stopped being a dark plate with a
   gold wash on it and became a 34%-opacity gold wash over the live picture,
   and the white type was left to fight whatever was behind. Over a Gravity
   Boys course, mostly dark, that was survivable. Over a Yeet arena shot from
   above, which is a bright deck under stadium light, it is not readable at
   all. Painting the tint as an image layer leaves the plate underneath doing
   its job, and the row reads the same over any picture. */
.bc-order__row.is-gold {
  background-image: linear-gradient(90deg, rgba(255, 199, 44, .34), rgba(255, 199, 44, .10));
  border-left-color: #ffc72c;
}
.bc-order__row.is-silver {
  background-image: linear-gradient(90deg, rgba(206, 214, 224, .30), rgba(206, 214, 224, .08));
  border-left-color: #ced6e0;
}
.bc-order__row.is-bronze {
  background-image: linear-gradient(90deg, rgba(205, 127, 50, .30), rgba(205, 127, 50, .08));
  border-left-color: #cd7f32;
}
.bc-order__row.is-gold .bc-order__pos { color: #ffe08a; }
.bc-order__row.is-silver .bc-order__pos { color: #e8eef5; }
.bc-order__row.is-bronze .bc-order__pos { color: #f0b98a; }

/* Round, but not on the podium. Legible, and unmistakably finished.
   A flat tint still has to be an image to stack on the plate, hence a gradient
   between two identical stops. */
.bc-order__row.is-home {
  background-image: linear-gradient(rgba(53, 208, 127, .16), rgba(53, 208, 127, .16));
}
.bc-order__row.is-home .bc-order__gap { color: var(--good, #35d07f); }

/* Gone over the side. Dimmed, because they are still in the result and no
   longer in the race. */
.bc-order__row.is-out { opacity: .5; }
.bc-order__row.is-out .bc-order__gap { color: var(--bad, #ff5a5f); }

/* The moment of crossing gets a flash, so it reads as an event rather than as
   a row that quietly changed colour while you were looking elsewhere. */
.bc-order__row.is-gold, .bc-order__row.is-silver,
.bc-order__row.is-bronze, .bc-order__row.is-home { animation: orderHome .5s ease; }
@keyframes orderHome {
  0%   { filter: brightness(2.4); }
  100% { filter: brightness(1); }
}
/* The camera slate: which shot is live and on whom, on the picture. Top
   centre, because that is the one part of the frame nothing else uses. */
.bc-camid {
  position: absolute; top: 12px; left: 50%; transform: translateX(-50%);
  padding: 3px 10px; border-radius: 3px;
  background: rgba(6, 9, 14, .78); border: 1px solid rgba(255, 255, 255, .12);
  font-family: var(--mono); font-size: 11px; letter-spacing: .06em;
  color: var(--accent-2, #54e0a0); white-space: nowrap;
  opacity: 0; transition: opacity .2s ease; pointer-events: none;
}
.bc-camid.is-on { opacity: 1; }

/* Which shot is live, and on whom. Monospaced and fixed-width so it does not
   shove the buttons about every time the director cuts. */
.bc-cam {
  font-family: var(--mono); font-size: 11px; color: var(--accent-2, #54e0a0);
  min-width: 15ch; text-align: right; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}

/* Which build the browser is actually running. */
.debugtray__build {
  margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--line-soft);
  font-size: 10.5px; line-height: 1.4; word-break: break-word;
}
.debugtray__build b { color: var(--accent-2, #54e0a0); font-weight: 700; }

/* ---- shape lab -----------------------------------------------------------
   One canvas stretched over the whole grid, drawn once per card with the
   scissor set — six WebGL contexts would hit the browser's limit and start
   evicting each other. The cards are ordinary layout; the canvas just sits
   behind them and paints into their holes. */
.shapelab { position: relative; margin-top: 20px; }
.shapelab__canvas {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: 0;
}
.shapelab.is-broken .shapelab__canvas { display: none; }
.shapelab__bar {
  position: relative; z-index: 1;
  display: flex; flex-wrap: wrap; gap: 18px; align-items: center;
  padding: 12px 16px; margin-bottom: 16px;
  background: var(--panel, rgba(255,255,255,.03));
  border: 1px solid var(--line); border-radius: 10px;
}
.shapelab__ctl { display: flex; align-items: center; gap: 9px; font-size: 12.5px; }
.shapelab__ctl > span { color: var(--ink-2); white-space: nowrap; }
.shapelab__ctl input[type=range] { width: 120px; }
.shapelab__ctl--check { cursor: pointer; }

.shapelab__grid {
  position: relative; z-index: 1;
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
.shapecard {
  margin: 0; border: 1px solid var(--line); border-radius: 10px; overflow: hidden;
  background: linear-gradient(180deg, rgba(255,255,255,.02), rgba(0,0,0,.18));
}
/* Transparent, so the shared canvas shows through here and nowhere else. */
.shapecard__view { height: 210px; background: transparent; }
.shapecard figcaption { padding: 12px 14px 14px; border-top: 1px solid var(--line); }
.shapecard h3 { margin: 0 0 4px; font-size: 15px; }
.shapecard__blurb { margin: 0 0 10px; font-size: 12.5px; color: var(--ink-2); line-height: 1.45; }
.shapecard__stats { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 12px; margin: 0; }
.shapecard__stats > div { display: flex; justify-content: space-between; gap: 8px; }
.shapecard__stats dt { font-size: 11px; color: var(--ink-3); letter-spacing: .04em; }
.shapecard__stats dd { margin: 0; font-family: var(--mono); font-size: 11.5px; color: var(--ink-2); }
.shapelab__note { margin-top: 14px; font-size: 12.5px; }

/* ---- camera lab ---------------------------------------------------------- */
.camlab {
  display: grid; gap: 16px; margin-top: 20px;
  grid-template-columns: minmax(0, 1.55fr) minmax(280px, 1fr);
}
@media (max-width: 900px) { .camlab { grid-template-columns: 1fr; } }
.camlab__stage {
  position: relative; border: 1px solid var(--line); border-radius: 10px; overflow: hidden;
  background: #0a0e15;
}
.camlab__view { aspect-ratio: 16 / 9; width: 100%; cursor: grab; }
.camlab__view:active { cursor: grabbing; }
.camlab__view canvas { display: block; width: 100%; height: 100%; }
.camlab.is-broken .camlab__view { display: grid; place-items: center; }
.camlab__hud {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding: 8px 12px; background: linear-gradient(180deg, transparent, rgba(4, 7, 12, .85));
  pointer-events: none;
}
.camlab__shot { font-weight: 800; font-size: 13px; letter-spacing: .04em; }
.camlab__hint { font-size: 11px; color: var(--ink-3); }

.camlab__panel {
  border: 1px solid var(--line); border-radius: 10px; padding: 14px;
  background: var(--panel, rgba(255, 255, 255, .03));
  display: flex; flex-direction: column; gap: 14px;
}
.camlab__shots { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 6px; }
.camlab__pick {
  display: grid; gap: 1px; padding: 7px 8px; text-align: left; cursor: pointer;
  background: rgba(255, 255, 255, .04); border: 1px solid transparent; border-radius: 6px;
  color: var(--ink-2); font: inherit;
}
.camlab__pick b { font-size: 12px; color: var(--ink); }
.camlab__pick i { font-style: normal; font-size: 10px; color: var(--ink-3); letter-spacing: .08em; }
.camlab__pick.is-on { border-color: var(--accent); background: rgba(255, 122, 24, .14); }
/* A dot on anything that has been framed and saved, so you can see at a
   glance which shots are yours and which are still as shipped. */
.camlab__pick.is-edited b::after {
  content: ''; display: inline-block; width: 5px; height: 5px; margin-left: 5px;
  border-radius: 50%; background: var(--accent-2, #54e0a0); vertical-align: middle;
}

.camlab__ctls { display: grid; gap: 11px; }
.camlab__ctl { display: grid; gap: 3px; font-size: 12px; }
.camlab__ctl > span { display: flex; justify-content: space-between; color: var(--ink-2); }
.camlab__ctl em { font-style: normal; font-family: var(--mono); color: var(--ink); }
.camlab__ctl small { font-size: 10.5px; color: var(--ink-3); line-height: 1.35; }
.camlab__ctl input[type=range], .camlab__ctl select { width: 100%; }
.camlab__note { margin: 0; font-size: 12px; color: var(--ink-2); line-height: 1.45; }
.camlab__actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.camlab__status { font-size: 11.5px; color: var(--accent-2, #54e0a0); }
.bc-board__crest { width: 26px; height: 26px; display: block; }

/* ---- competitor labels ---------------------------------------------------
   A number stuck to a tumbling body spends most of a race facing away from
   the camera or buried inside its own shape, so the numbers live on the
   picture instead. Each one is a dot on the competitor, a hairline leading up
   and to the right, and the label on the end of it — the way a broadcast
   annotates a moving field.

   Positions come from the renderer every frame, so nothing here may cost a
   layout: only `transform` and `opacity` change. */
.bc-tags {
  position: absolute; inset: 0; pointer-events: none; overflow: hidden;
  /* Behind every piece of station furniture. These follow moving bodies and
     will happily wander under the running order or the lower third; when they
     do, the furniture wins. It is the fixed part of the picture. */
  z-index: 0;
}
.bug, .bc-order, .bc-lower, .bc-live, .bc-pre,
.bc-board, .bc-winner, .bc-ticker, .bc-inter, .bc-wipe,
.bc-replaybadge, .bc-replayframe, .bc-ident { z-index: 2; }
.bc-tags.is-hidden { opacity: 0; transition: opacity .12s linear; }
.bc-tag {
  position: absolute; left: 0; top: 0; will-change: transform, opacity;
  opacity: 0; transition: opacity .18s linear;
}
.bc-tag.is-on { opacity: 1; }

/* The dot that sits on the competitor. */
.bc-tag__dot {
  position: absolute; left: -3.5px; top: -3.5px; width: 7px; height: 7px;
  border-radius: 50%; background: var(--c, #fff);
  box-shadow: 0 0 0 1.5px rgba(6,9,14,.75), 0 0 7px rgba(0,0,0,.5);
}

/* The leader line: one hairline, rotated up and to the right. Drawn from the
   dot outward, so it always starts on the body. */
.bc-tag__line {
  position: absolute; left: 0; top: 0; height: 1px; width: 34px;
  background: linear-gradient(90deg, var(--c, #fff), rgba(255,255,255,.55));
  transform-origin: 0 50%; transform: rotate(-38deg);
  box-shadow: 0 0 4px rgba(0,0,0,.6);
}

.bc-tag__label {
  position: absolute; left: 27px; top: -33px;
  display: flex; align-items: center; gap: 6px;
  padding: 2px 7px 2px 4px; white-space: nowrap;
  background: rgba(8,12,18,.82); border-radius: 3px;
  border-left: 3px solid var(--c, #fff);
  backdrop-filter: blur(3px);
  font-size: 11px; line-height: 1.5; letter-spacing: .01em;
  box-shadow: 0 2px 10px rgba(0,0,0,.45);
}
.bc-tag__n {
  font-family: var(--mono); font-weight: 800; font-size: 11.5px;
  color: var(--c, #fff);
}
.bc-tag__name {
  font-weight: 600; color: #eef3fa;
  max-width: 116px; overflow: hidden; text-overflow: ellipsis;
}

/* Anything more than about twenty metres out gets quieter rather than
   disappearing, so the far side of the field stays readable without
   competing with whoever the camera is actually on. */
.bc-tag.is-far { opacity: .48; }
.bc-tag.is-far .bc-tag__name { display: none; }

@media (max-width: 720px) {
  .bc-tag__name { display: none; }
  .bc-tag__line { width: 22px; }
  .bc-tag__label { left: 18px; top: -26px; }
}

/* lower third */
.bc-lower {
  position: absolute; left: 14px; bottom: 58px; max-width: min(74%, 620px);
  display: flex; align-items: stretch; border-radius: 6px; overflow: hidden;
  box-shadow: 0 10px 30px rgba(0,0,0,.6);
  transform: translateY(14px); opacity: 0; transition: opacity .35s ease, transform .35s ease;
}
.bc-lower.is-on { transform: translateY(0); opacity: 1; }
.bc-lower__tag {
  background: linear-gradient(180deg,#ff8a2b,#e56a05); color: #14090f; padding: 9px 12px;
  font-size: 10px; font-weight: 900; letter-spacing: .14em; display: grid; place-items: center;
}
.bc-lower__text { background: rgba(9,13,20,.92); padding: 9px 15px; font-size: 13.5px; font-weight: 550; line-height: 1.35; }

/* station ident, bottom-right, above the ticker */
.bc-ident {
  position: absolute; right: 14px; bottom: 42px; width: 104px; height: auto;
  opacity: .5; mix-blend-mode: screen; filter: drop-shadow(0 2px 8px rgba(0, 0, 0, .6));
  transition: opacity .3s ease;
}
.broadcast.is-mini .bc-ident { width: 66px; bottom: 36px; }
@media (max-width: 760px) { .bc-ident { width: 72px; right: 8px; bottom: 38px; } }

/* bottom ticker */
.bc-ticker {
  position: absolute; left: 0; right: 0; bottom: 0; height: 30px; display: flex; align-items: center;
  background: rgba(6,9,14,.92); border-top: 1px solid rgba(255,255,255,.07); overflow: hidden;
}
.bc-ticker__label {
  background: var(--accent); color: #180c02; height: 100%; display: grid; place-items: center;
  padding: 0 11px; font-size: 9.5px; font-weight: 900; letter-spacing: .17em; flex: none;
}
/* The travel is driven from app.js by scrolling this view, not by transforming
   the track. A keyframe animating `transform` sits in the animation origin and
   silently wins over anything script sets, and a percentage keyframe has to
   guess the width of a max-content flex item inside an overflow-hidden flex
   container — guess it as the clamped width and the strip travels a few pixels
   and stops, which is a ticker that appears not to move at all.
 *
   scrollLeft has no such argument with the cascade. The keyframe below is kept
   only as a fallback for a page where the script never ran; app.js marks the
   track `is-driven` the moment it takes over, which switches it off. */
.bc-ticker__view { flex: 1; min-width: 0; overflow: hidden; position: relative; }
/* The track holds the sequence twice and slides exactly half its own width, so
   the second copy lands where the first started and the loop is seamless. No
   measurement, no script, nothing to fail silently. */
.bc-ticker__track {
  display: flex; flex: none; width: max-content; white-space: nowrap;
  will-change: transform; animation: tickerRoll 45s linear infinite;
}
@keyframes tickerRoll {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}
.bc-ticker:hover .bc-ticker__track { animation-play-state: paused; }
/* Both copies carry identical padding, so the track is exactly twice one
   sequence and -50% lands the second copy where the first began. Padding on
   the track instead — which is where it was — makes the travel half a padding
   too far and the loop jumps every time round. */
.bc-ticker__seq {
  display: flex; flex: none; gap: 34px; padding-right: 34px; padding-left: 18px;
}
.bc-ticker__track span { font-size: 11.5px; color: var(--ink-2); flex: none; }
.bc-ticker__track b { color: var(--accent-2); font-weight: 700; }

/* ---- competitor portrait --------------------------------------------------- */
.boyhead { display: flex; align-items: flex-start; gap: 18px; min-width: 0; }
.boyhead__portrait {
  flex: 0 0 auto; width: 132px; height: 132px; border-radius: 14px;
  background: radial-gradient(ellipse at 50% 32%, #1a2233, #0c111b 70%);
  border: 1px solid var(--line); display: grid; place-items: center;
  box-shadow: inset 0 -18px 34px rgba(0, 0, 0, .45);
}
.portrait { display: block; max-width: 100%; height: auto; }
/* Small inline version for cards and lists. */
.portrait--sm { width: 34px; height: 34px; }
@media (max-width: 620px) {
  .boyhead { gap: 12px; }
  .boyhead__portrait { width: 88px; height: 88px; }
  .boyhead__portrait .portrait { width: 76px; height: 76px; }
}

/* ---- the pre-race package -------------------------------------------------- *
 * Everything here is only on screen between coverage starting and the gate
 * dropping. It has one job: make forty-six seconds of a field that is not
 * moving yet feel like the start of something. */
.bc-pre { position: absolute; inset: 0; opacity: 0; visibility: hidden; transition: opacity .5s ease; }
.bc-pre.is-on { opacity: 1; visibility: visible; }

/* A light sweeping across the picture, slowly. */
.bc-pre__sweep {
  position: absolute; top: 0; bottom: 30px; width: 36%; left: -40%;
  background: linear-gradient(100deg, transparent, rgba(255,122,24,.10) 42%, rgba(255,255,255,.06) 52%, transparent);
  animation: preSweep 7.5s ease-in-out infinite;
}
@keyframes preSweep { 0% { left: -40%; } 65%, 100% { left: 110%; } }

.bc-pre__bracket { position: absolute; width: 34px; height: 34px; border: 2px solid rgba(255,122,24,.75); }
.bc-pre__bracket--tl { top: 10px; left: 10px; border-right: 0; border-bottom: 0; }
.bc-pre__bracket--tr { top: 10px; right: 10px; border-left: 0; border-bottom: 0; }
.bc-pre__bracket--bl { bottom: 40px; left: 10px; border-right: 0; border-top: 0; }
.bc-pre__bracket--br { bottom: 40px; right: 10px; border-left: 0; border-top: 0; }
.bc-pre.is-on .bc-pre__bracket { animation: preBracket .7s cubic-bezier(.2,.8,.3,1) both; }
@keyframes preBracket { from { opacity: 0; transform: scale(1.9); } to { opacity: 1; transform: scale(1); } }

/* Title block, top centre, under the score bug. */
.bc-pre__head { position: absolute; left: 50%; top: 15%; transform: translateX(-50%); text-align: center; max-width: 78%; }
.bc-pre__tag {
  display: inline-block; background: linear-gradient(180deg,#ff8a2b,#e56a05); color: #14090f;
  padding: 4px 14px; border-radius: 3px; font-size: 10px; font-weight: 900; letter-spacing: .32em;
  text-transform: uppercase; margin-bottom: 10px;
}
.bc-pre__name {
  font-size: clamp(20px, 3.4vw, 40px); font-weight: 850; letter-spacing: -.02em; line-height: 1.05;
  text-shadow: 0 4px 22px rgba(0,0,0,.85);
}
.bc-pre__where {
  font-size: 11px; font-weight: 800; letter-spacing: .26em; text-transform: uppercase;
  color: var(--ink-2); margin-top: 8px; text-shadow: 0 2px 10px rgba(0,0,0,.9);
}

/* Bottom-anchored stack: storyline card and clock on one row, team strip
   under it, the ticker under that. Nothing here is positioned against a
   guessed height, so nothing can land on top of anything else. */
.bc-pre__stack {
  position: absolute; left: 16px; right: 16px; bottom: 40px;
  display: flex; flex-direction: column; gap: 8px;
}
.bc-pre__row { display: flex; align-items: flex-end; justify-content: space-between; gap: 14px; }

/* The storyline card.
   Each one is a change of subject, so it gets a transition rather than a
   swap: the card wipes in from the left behind a bar of accent, and the kicker
   and the line arrive a beat apart. A fade alone reads as a hard cut at this
   size — there is not enough of it on screen for the eye to catch. */
.bc-pre__card {
  position: relative; overflow: hidden;
  flex: 1 1 auto; min-width: 0; max-width: 620px;
  background: rgba(9,13,20,.9); border-left: 3px solid var(--accent); border-radius: 0 6px 6px 0;
  padding: 9px 15px; opacity: 0; transform: translateX(-26px) skewX(-2deg);
  transition: opacity .38s ease, transform .5s cubic-bezier(.16,.84,.24,1);
  box-shadow: 0 12px 34px rgba(0,0,0,.6);
}
.bc-pre__card.is-on { opacity: 1; transform: translateX(0) skewX(0); }

/* The bar that runs across as it lands. */
.bc-pre__card::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(100deg,
    transparent 0%, rgba(255,122,24,.42) 45%, rgba(255,255,255,.55) 50%,
    rgba(255,122,24,.42) 55%, transparent 100%);
  transform: translateX(-110%);
}
.bc-pre__card.is-on::after { animation: preCardSweep .62s cubic-bezier(.2,.85,.25,1) forwards; }
@keyframes preCardSweep {
  from { transform: translateX(-110%); }
  to   { transform: translateX(110%); }
}

.bc-pre__card-kicker {
  font-size: 9.5px; font-weight: 900; letter-spacing: .28em; color: var(--accent);
  text-transform: uppercase; opacity: 0; transform: translateY(6px);
}
.bc-pre__card-text {
  font-size: 13px; font-weight: 550; line-height: 1.35; margin-top: 3px;
  opacity: 0; transform: translateY(8px);
}
.bc-pre__card.is-on .bc-pre__card-kicker { animation: preCardLine .4s ease .1s forwards; }
.bc-pre__card.is-on .bc-pre__card-text   { animation: preCardLine .45s cubic-bezier(.2,.8,.3,1) .2s forwards; }
@keyframes preCardLine { to { opacity: 1; transform: translateY(0); } }

/* Reduced motion is handled centrally — see the block at the end of this
   file. Turning the card's transition off here as well is what made "no
   animation at all" survive being fixed twice. */

/* Team strip. One row, scrolled rather than wrapped — a second row was what
   was landing on the storyline card. */
/* Who is at the gate.
   Wraps onto as many rows as it needs and centres every one of them, so an
   odd number does not leave a lonely chip hanging off one end. It used to be a
   single row that ran off the side behind a fade, which hid whoever happened
   to be drawn last. */
.bc-pre__field { display: grid; justify-items: center; gap: 6px; }
.bc-pre__field-label {
  font-size: 9px; font-weight: 900; letter-spacing: .26em; text-transform: uppercase;
  color: var(--ink-3);
}
.bc-pre__teams {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  gap: 6px; max-width: 100%;
}
.bc-pre__team {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 10.5px; font-weight: 750; letter-spacing: .06em;
  padding: 3px 9px 3px 5px; border-radius: 4px;
  background: rgba(9,13,20,.85); border-left: 3px solid var(--c, #666); white-space: nowrap;
  opacity: 0; flex: none;
}
.bc-pre__team .crest { width: 20px; height: 20px; display: block; flex: none; }
.bc-pre__team-name { overflow: hidden; text-overflow: ellipsis; max-width: 15ch; }
.bc-pre.is-on .bc-pre__team { animation: preTeam .5s cubic-bezier(.2,.8,.3,1) both; }
@keyframes preTeam { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }

/* The clock. */
.bc-pre__clock {
  flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 2px;
  background: rgba(6,10,18,.8); border: 1px solid rgba(255,122,24,.4); border-radius: 8px;
  padding: 7px 15px; backdrop-filter: blur(4px);
}
.bc-pre__clock-label { font-size: 9px; font-weight: 900; letter-spacing: .26em; color: var(--ink-3); text-transform: uppercase; }
.bc-pre__clock-n {
  font-family: var(--mono); font-size: clamp(22px, 3.2vw, 34px); font-weight: 800; color: #fff;
  font-variant-numeric: tabular-nums; line-height: 1;
}
.bc-pre__clock.is-imminent { border-color: var(--accent); animation: gatePulse 1s ease-in-out infinite; }
.bc-pre__clock.is-imminent .bc-pre__clock-n { color: var(--accent-2); }
@keyframes gatePulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 122, 24, .5); }
  50% { box-shadow: 0 0 0 12px rgba(255, 122, 24, 0); }
}

/* The last three seconds, straight down the middle. */
.bc-pre__punch {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  font-size: clamp(70px, 15vw, 190px); font-weight: 900; letter-spacing: -.05em; line-height: 1;
  color: #fff; text-shadow: 0 0 60px rgba(255,122,24,.85), 0 10px 40px rgba(0,0,0,.9);
  opacity: 0; pointer-events: none;
}
.bc-pre__punch.is-on { animation: prePunch .9s cubic-bezier(.16,.9,.3,1) both; }
@keyframes prePunch {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(2.1); }
  22% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  70% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(.86); }
}

/* The station ident sits bottom-right, exactly where the team strip runs.
   It is a watermark, so it gets out of the way rather than being drawn over. */
.bc-pre.is-on ~ .bc-ident,
.broadcast.is-preshow .bc-ident { opacity: .16; }

@media (max-width: 720px) {
  .bc-pre__card { font-size: 12px; }
  .bc-pre__clock { padding: 5px 11px; }
  .bc-pre__field { display: none; }
  .bc-pre__head { top: 12%; }
}

/* ---- closing sequence ------------------------------------------------------ */
.bc-replaybadge {
  position: absolute; left: 50%; top: 16px; transform: translateX(-50%) scale(.85);
  background: rgba(78, 168, 222, .95); color: #06101a; padding: 5px 16px; border-radius: 4px;
  font-size: 11px; font-weight: 900; letter-spacing: .3em;
  opacity: 0; transition: opacity .3s ease, transform .3s ease;
}
.bc-replaybadge.is-on { opacity: 1; transform: translateX(-50%) scale(1); animation: replayPulse 1.6s infinite; }
@keyframes replayPulse { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }

/* ---- replay framing -------------------------------------------------------- *
 * The picture is inset, tinted and cornered while a replay runs. It should be
 * impossible to mistake for the live race — that is the entire job. */
.bc-replayframe {
  position: absolute; inset: 0; opacity: 0; pointer-events: none;
  transition: opacity .35s ease;
}
.bc-replayframe.is-on { opacity: 1; }
.bc-replayframe::before {
  content: ''; position: absolute; inset: 14px;
  border: 2px solid rgba(78, 168, 222, .85); border-radius: 4px;
  box-shadow: 0 0 0 1px rgba(6, 16, 26, .8), inset 0 0 60px rgba(78, 168, 222, .13);
}
/* Vignette outside the frame, so the eye goes to the framed picture. */
.bc-replayframe::after {
  content: ''; position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(6, 16, 26, .55) 0, transparent 60px),
    linear-gradient(0deg, rgba(6, 16, 26, .55) 0, transparent 60px),
    linear-gradient(90deg, rgba(6, 16, 26, .5) 0, transparent 60px),
    linear-gradient(270deg, rgba(6, 16, 26, .5) 0, transparent 60px);
}
.bc-replayframe__c {
  position: absolute; width: 26px; height: 26px; border: 3px solid var(--accent);
}
.bc-replayframe__c--tl { top: 8px; left: 8px; border-right: 0; border-bottom: 0; }
.bc-replayframe__c--tr { top: 8px; right: 8px; border-left: 0; border-bottom: 0; }
.bc-replayframe__c--bl { bottom: 8px; left: 8px; border-right: 0; border-top: 0; }
.bc-replayframe__c--br { bottom: 8px; right: 8px; border-left: 0; border-top: 0; }
.bc-replayframe__tag {
  position: absolute; top: 22px; left: 24px; display: flex; align-items: center; gap: 7px;
  background: rgba(78, 168, 222, .95); color: #06101a; padding: 4px 12px; border-radius: 3px;
  font-size: 10px; font-weight: 900; letter-spacing: .28em;
}
.bc-replayframe__tag i {
  width: 7px; height: 7px; border-radius: 50%; background: #06101a;
  animation: replayPulse 1.1s infinite;
}
.bc-replayframe__speed {
  position: absolute; bottom: 24px; right: 26px;
  font-family: var(--mono); font-size: 11px; font-weight: 700; letter-spacing: .18em;
  color: #bfe2f7; text-shadow: 0 2px 8px rgba(0, 0, 0, .9);
}

/* ---- transition wipe ------------------------------------------------------- *
 * Two panels sweep in from the edges, meet, then part again. A cut hidden
 * inside the meeting looks directed; a cut on its own looks like a glitch. */
.bc-wipe { position: absolute; inset: 0; pointer-events: none; overflow: hidden; opacity: 0; }
.bc-wipe.is-running { opacity: 1; }
.bc-wipe__a, .bc-wipe__b {
  position: absolute; top: -10%; height: 120%; width: 62%;
  background: linear-gradient(120deg, #0b1220 0%, #101b2e 55%, var(--accent) 55%, var(--accent) 58%, #0b1220 58%);
}
.bc-wipe__a { left: -66%; transform: skewX(-12deg); }
.bc-wipe__b { right: -66%; transform: skewX(-12deg); }
.bc-wipe.is-running .bc-wipe__a { animation: wipeIn 1.05s cubic-bezier(.62, .02, .32, 1) forwards; }
.bc-wipe.is-running .bc-wipe__b { animation: wipeInB 1.05s cubic-bezier(.62, .02, .32, 1) forwards; }
@keyframes wipeIn {
  0% { left: -66%; } 42% { left: -6%; } 58% { left: -6%; } 100% { left: -66%; }
}
@keyframes wipeInB {
  0% { right: -66%; } 42% { right: -6%; } 58% { right: -6%; } 100% { right: -66%; }
}
.bc-wipe__seam {
  position: absolute; left: 50%; top: 0; width: 3px; height: 100%; margin-left: -1.5px;
  background: linear-gradient(180deg, transparent, var(--accent), transparent);
  opacity: 0;
}
.bc-wipe.is-running .bc-wipe__seam { animation: seamFlash 1.05s ease forwards; }
@keyframes seamFlash { 0%, 30% { opacity: 0; } 46% { opacity: 1; } 62% { opacity: 1; } 100% { opacity: 0; } }

/* ---- interstitial ---------------------------------------------------------- */
/* ---- interstitial --------------------------------------------------------
   This was a flat blue rectangle with a 54px logo sitting still in the middle
   of it. A network ident is a *move*: something sweeps across the frame, the
   mark arrives on it at size, light rakes over the mark, and it settles.

   Everything below is one composition on a shared timeline, so the parts
   arrive in an order rather than all at once. Transform and opacity only —
   nothing here can cost a layout mid-animation. */
.bc-inter {
  position: absolute; inset: 0; overflow: hidden;
  display: grid; place-content: center; justify-items: center;
  gap: 4px; text-align: center; pointer-events: none;
  opacity: 0; visibility: hidden;
  /* Deep, and angled, so it is not a flat wash of one colour. */
  background:
    radial-gradient(ellipse at 30% 20%, rgba(52, 96, 168, .55), transparent 62%),
    radial-gradient(ellipse at 72% 88%, rgba(255, 122, 24, .30), transparent 58%),
    linear-gradient(118deg, #060a12 0%, #0d1830 44%, #071021 100%);
  perspective: 1100px;
}
.bc-inter.is-on { opacity: 1; visibility: visible; }

/* The sweep: a hard-edged bar of light that crosses the frame and is what
   the logo appears to arrive on. */
.bc-inter::before {
  content: ''; position: absolute; top: -20%; bottom: -20%; left: -60%; width: 46%;
  background: linear-gradient(100deg,
    transparent, rgba(255, 255, 255, .06) 24%, rgba(120, 190, 255, .5) 50%,
    rgba(255, 255, 255, .9) 56%, rgba(120, 190, 255, .4) 62%, transparent);
  transform: skewX(-14deg); opacity: 0;
}
.bc-inter.is-on::before { animation: interSweep 1.05s cubic-bezier(.22, .9, .2, 1) forwards; }
@keyframes interSweep {
  0%   { left: -60%; opacity: 0; }
  12%  { opacity: 1; }
  78%  { opacity: 1; }
  100% { left: 118%; opacity: 0; }
}

/* Diagonal chevrons, drifting, so the background is never dead. */
.bc-inter::after {
  content: ''; position: absolute; inset: -30%;
  background: repeating-linear-gradient(118deg,
    rgba(255, 255, 255, .045) 0 2px, transparent 2px 46px);
  opacity: 0; animation: none;
}
.bc-inter.is-on::after { animation: interDrift 2.6s linear forwards; }
@keyframes interDrift {
  0%   { opacity: 0; transform: translateX(-40px); }
  22%  { opacity: 1; }
  100% { opacity: .8; transform: translateX(40px); }
}

/* The mark. Huge, and it arrives edge-on from the left, turning to face you
   as it lands — the move every sports network opens on. */
.bc-inter__logo {
  height: clamp(90px, 26vh, 210px); width: auto; margin-bottom: 2px;
  transform-style: preserve-3d; will-change: transform, opacity;
  /* The glow takes the sport's colour. It was a fixed blue, which reads as
     the sport blue happens to belong to rather than as whatever is on. */
  filter: drop-shadow(0 10px 40px rgba(0, 0, 0, .75))
          drop-shadow(0 0 22px var(--sport-glow, rgba(120, 190, 255, .35)));
  opacity: 0;
}
.bc-inter.is-on .bc-inter__logo { animation: interMark 1.15s cubic-bezier(.16, .84, .24, 1) forwards; }
@keyframes interMark {
  0%   { opacity: 0; transform: translateX(-56%) rotateY(-88deg) scale(1.5); }
  38%  { opacity: 1; }
  72%  { transform: translateX(0) rotateY(9deg) scale(1.04); }
  100% { opacity: 1; transform: translateX(0) rotateY(0deg) scale(1); }
}

/* Lens flare: an anamorphic streak with a hot core, thrown as the mark lands
   and gone almost immediately, the way a real one is. */
.bc-inter__flare {
  position: absolute; left: 50%; top: 46%; width: 150%; height: 5px;
  transform: translate(-50%, -50%) scaleX(0);
  background: linear-gradient(90deg,
    transparent, rgba(120, 190, 255, .35) 22%, rgba(210, 235, 255, .95) 48%,
    #ffffff 50%, rgba(210, 235, 255, .95) 52%, rgba(255, 168, 90, .35) 78%, transparent);
  opacity: 0; filter: blur(1px); pointer-events: none;
}
.bc-inter__flare::before {
  content: ''; position: absolute; left: 50%; top: 50%; width: 200px; height: 200px;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle, rgba(255, 255, 255, .85), rgba(140, 200, 255, .3) 32%, transparent 66%);
}
.bc-inter.is-on .bc-inter__flare { animation: interFlare .85s cubic-bezier(.1, .9, .2, 1) .48s forwards; }
@keyframes interFlare {
  0%   { opacity: 0; transform: translate(-50%, -50%) scaleX(.1); }
  22%  { opacity: 1; transform: translate(-50%, -50%) scaleX(1); }
  100% { opacity: 0; transform: translate(-50%, -50%) scaleX(1.15); }
}

.bc-inter__kicker {
  font-size: clamp(10px, 1.4vw, 13px); font-weight: 900; letter-spacing: .38em;
  color: var(--accent); text-transform: uppercase;
  opacity: 0; transform: translateY(10px);
}
.bc-inter.is-on .bc-inter__kicker { animation: interLine .5s ease .62s forwards; }

.bc-inter__title {
  font-size: clamp(24px, 5vw, 52px); font-weight: 900; letter-spacing: .01em; color: #fff;
  text-shadow: 0 4px 30px rgba(0, 0, 0, .85);
  opacity: 0; transform: translateY(14px);
}
.bc-inter.is-on .bc-inter__title { animation: interLine .55s cubic-bezier(.2,.8,.3,1) .74s forwards; }

/* An underline that wipes out from the middle under the title. */
.bc-inter__rule {
  height: 3px; width: min(340px, 52%); border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  transform: scaleX(0); opacity: 0;
}
.bc-inter.is-on .bc-inter__rule { animation: interRule .6s cubic-bezier(.2,.8,.3,1) .9s forwards; }

@keyframes interLine { to { opacity: 1; transform: translateY(0); } }
@keyframes interRule { to { opacity: 1; transform: scaleX(1); } }

/* Reduced motion for the ident is handled centrally — see the end of this
   file. The mark still arrives; only the sweep and the flare are dropped. */
@media (max-width: 560px) {
  .bc-replayframe::before { inset: 9px; }
  .bc-replayframe__tag { top: 15px; left: 15px; font-size: 9px; padding: 3px 9px; }
  .bc-replayframe__c { width: 18px; height: 18px; }
}

.bc-board {
  position: absolute; inset: 0; display: grid; place-items: center; padding: 4%;
  background: linear-gradient(180deg, rgba(5, 8, 13, .35), rgba(5, 8, 13, .88));
  opacity: 0; pointer-events: none; transition: opacity .45s ease;
}
.bc-board.is-on { opacity: 1; }
/* The card has to fit the panel, whatever is in it.
   Eight rows plus a logo, a title and a footer is taller than a 16:9 box at
   the old fixed padding — so everything scales with the panel height, the list
   scrolls if it still cannot fit, and nothing is ever cropped off the bottom
   with no way to reach it. */
.bc-board__in {
  width: min(560px, 92%); max-height: 100%;
  display: flex; flex-direction: column; min-height: 0;
  background: rgba(10, 14, 22, .92); border: 1px solid var(--line);
  border-radius: 12px; padding: clamp(8px, 2.2vh, 18px) clamp(12px, 2.4vw, 22px);
  box-shadow: 0 24px 70px rgba(0, 0, 0, .7);
  transform: translateY(14px); transition: transform .45s cubic-bezier(.2, .7, .3, 1);
}
.bc-board.is-on .bc-board__in { transform: translateY(0); }
.bc-board__logo {
  display: block; height: clamp(20px, 5vh, 46px); width: auto; margin: 0 auto 6px;
  mix-blend-mode: screen; flex: none;
}
.bc-board__title {
  text-align: center; font-size: clamp(9px, 1.6vh, 11px); font-weight: 900; letter-spacing: .3em;
  color: var(--accent); margin-bottom: clamp(5px, 1.4vh, 12px); flex: none;
}
.bc-board__list {
  list-style: none; margin: 0; padding: 0; display: grid; gap: clamp(2px, .5vh, 3px);
  min-height: 0; overflow-y: auto; overscroll-behavior: contain;
}
.bc-board__list li {
  display: grid; grid-template-columns: 26px 24px minmax(0, 1fr) auto auto;
  gap: clamp(5px, 1vw, 9px); align-items: center;
  padding: clamp(2px, .8vh, 5px) 9px; border-radius: 5px; background: rgba(255, 255, 255, .04);
  border-left: 3px solid var(--c, #666); font-size: clamp(10.5px, 1.9vh, 13px);
  opacity: 0; transform: translateX(-10px); animation: boardIn .45s forwards;
}
@keyframes boardIn { to { opacity: 1; transform: translateX(0); } }
.bc-board__list li:nth-child(1) { background: rgba(255, 209, 102, .16); }
.bc-board__pos { font-family: var(--mono); font-weight: 800; text-align: center; }
.bc-board__nm { font-weight: 650; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bc-board__tm { font-size: 10.5px; color: var(--ink-3); letter-spacing: .1em; }
.bc-board__ms { font-family: var(--mono); font-size: 12px; color: var(--ink-2); }
.bc-board__foot {
  text-align: center; margin-top: clamp(5px, 1.4vh, 12px);
  font-size: clamp(9px, 1.5vh, 10.5px); letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-3); flex: none;
}

.bc-winner {
  position: absolute; left: 50%; bottom: 22%; transform: translate(-50%, 18px);
  text-align: center; opacity: 0; transition: opacity .5s ease, transform .5s cubic-bezier(.2,.7,.3,1);
}
.bc-winner.is-on { opacity: 1; transform: translate(-50%, 0); }
.bc-winner__tag {
  display: inline-block; background: linear-gradient(180deg, #ffd166, #e0a33e); color: #2a1a00;
  padding: 3px 18px; border-radius: 3px; font-size: 10px; font-weight: 900; letter-spacing: .34em;
}
.bc-winner__name {
  font-size: clamp(24px, 4vw, 44px); font-weight: 850; letter-spacing: -.02em; margin-top: 6px;
  text-shadow: 0 4px 24px rgba(0, 0, 0, .85);
}
.bc-winner__time { font-family: var(--mono); font-size: 15px; color: var(--accent-2); }

@media (max-width: 760px) {
  .bc-board__in { padding: 12px 14px; }
  .bc-board__list li { font-size: 11.5px; padding: 3px 7px; }
  .bc-board__logo { height: 32px; }
}

/* pre-event card / countdown state */
.bc-card {
  position: absolute; inset: 0; display: grid; place-items: center; text-align: center;
  background: radial-gradient(700px 400px at 50% 40%, rgba(255,122,24,.09), transparent 70%), #070a10;
  padding: 24px;
}
.bc-card__in { max-width: 520px; }
.bc-count { font-family: var(--mono); font-size: clamp(34px, 7vw, 64px); font-weight: 800; letter-spacing: -.03em; }

/* controls */
.bc-controls {
  display: flex; align-items: center; gap: 8px; padding: 9px 12px;
  background: #0a0e16; border-top: 1px solid var(--line-soft); flex-wrap: wrap;
}
.bc-btn {
  background: var(--panel-2); border: 1px solid var(--line); color: var(--ink-2);
  border-radius: 7px; padding: 6px 10px; font-size: 12px; font-weight: 650; cursor: pointer; font-family: inherit;
}
.bc-btn:hover { color: var(--ink); border-color: #2c3c5c; }
.bc-btn.is-on { background: rgba(255,122,24,.18); border-color: var(--accent); color: #ffc48c; }
/* Wanted, but the browser has not granted audio yet — say so instead of
 * claiming to be on while nothing plays. */
.bc-btn.is-pending {
  background: rgba(255,122,24,.07); border-color: rgba(255,122,24,.4); color: #d8a072;
  animation: soundWait 1.6s ease-in-out infinite;
}
@keyframes soundWait { 0%, 100% { opacity: 1; } 50% { opacity: .58; } }
.bc-cam { font-family: var(--mono); font-size: 11px; color: var(--ink-3); letter-spacing: .1em; text-transform: uppercase; }

/* --- event page layout ---------------------------------------------------- *
 * The broadcast and the things you act on have to be visible at the same time.
 * On anything desktop-shaped the commit rail sits beside the television; below
 * that it stacks, and the secondary panels fold away.
 * -------------------------------------------------------------------------- */
.eventgrid {
  display: grid; gap: 18px; align-items: start;
  grid-template-columns: minmax(0, 1fr) clamp(330px, 25vw, 430px);
}
@media (max-width: 1023px) { .eventgrid { grid-template-columns: minmax(0, 1fr); } }

.eventgrid__main, .eventgrid__rail { display: flex; flex-direction: column; gap: 18px; min-width: 0; }

/* Cap the television so it can never swallow the viewport while stuck. The
   width follows from the height cap, keeping 16:9 and centring the panel. */
@media (min-width: 1024px) {
  .broadcast--sticky { position: sticky; top: 100px; z-index: 30; }
  .broadcast--sticky .broadcast__stage {
    max-height: min(56vh, 640px);
    width: min(100%, calc(min(56vh, 640px) * 16 / 9));
    margin: 0 auto;
    transition: max-height .32s cubic-bezier(.2, .7, .3, 1), width .32s cubic-bezier(.2, .7, .3, 1);
  }

  /* Scrolled down to read the card: the television steps back to half size and
     tucks right, so it is still watchable but the page below is usable. */
  .broadcast--sticky.is-mini {
    margin-left: auto;
    width: 52%;
    transition: width .32s cubic-bezier(.2, .7, .3, 1);
  }
  .broadcast--sticky.is-mini .broadcast__stage {
    max-height: min(30vh, 330px);
    width: min(100%, calc(min(30vh, 330px) * 16 / 9));
  }
  /* Overlay furniture that stops making sense at half size. */
  .broadcast.is-mini .bc-order { display: none; }
  .broadcast.is-mini .bc-ticker { height: 22px; }
  .broadcast.is-mini .bc-ticker__label { font-size: 8px; padding: 0 7px; }
  .broadcast.is-mini .bug { transform: scale(.82); transform-origin: top left; }
  .broadcast.is-mini .bc-lower { bottom: 30px; }
  .broadcast.is-mini .bc-lower__text { font-size: 12px; padding: 6px 11px; }
  .broadcast.is-mini .bc-controls { padding: 6px 10px; }
  .broadcast.is-mini .bc-controls .bc-btn { font-size: 11px; padding: 5px 8px; }
}
@media (max-width: 1023px) {
  /* Sticky plus a full-height 16:9 panel leaves no room to read anything. */
  .broadcast--sticky { position: static; }
}

/* Cap the television only when the phone is turned landscape to watch it. A
   full-width 16:9 panel is fine in portrait — its height follows from a width
   that already fits — and taller than the whole screen in landscape, which is
   the one "the TV is too big, I have to zoom out" case the mobile layout never
   handled. Portrait is deliberately left completely untouched.

   This is safe to give the stage a width (which keeps the 16:9 while the height
   is capped) ONLY because the grid columns the panel sits in are all
   `minmax(0, 1fr)`. With the old bare `1fr` (== `minmax(auto, 1fr)`) this
   width would have become the column's *minimum* and blown the whole column off
   the side of the screen — which is exactly the bug this replaced. See the
   `.hero` / `.split` rules.

   `svh`, not `vh`: `vh` is the viewport at its tallest (toolbar collapsed), so a
   panel sized to it overshoots while the toolbar is showing. `svh` is the
   smallest, so it fits with the chrome visible. A browser without `svh` drops
   both lines and keeps the full-width panel. */
@media (max-width: 1023px) and (orientation: landscape) {
  .broadcast__stage {
    max-height: 82svh;
    width: min(100%, calc(82svh * 16 / 9));
    margin-inline: auto;
  }
}

/* Foldable panels. The summary carries the panel heading. */
details.foldable { padding: 0; }
details.foldable > summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 14px 18px; border-bottom: 1px solid var(--line-soft);
  background: rgba(255, 255, 255, .015); font-weight: 750; font-size: 17px;
}
details.foldable > summary::-webkit-details-marker { display: none; }
details.foldable > summary::after {
  content: '▾'; color: var(--ink-3); font-size: 13px; transition: transform .2s ease;
}
details.foldable[open] > summary::after { transform: rotate(180deg); }
details.foldable > summary:hover { color: var(--ink); }
details.foldable > summary .samples { font-weight: 500; }
details.foldable .foldable__body { padding: 16px 18px; }
details.foldable .foldable__body--flush { padding: 0; }

/* --- event page ----------------------------------------------------------- */
.eventhead { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-start; justify-content: space-between; margin-bottom: 16px; }
.eventhead h1 { margin-bottom: 6px; }
.eventmeta { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; color: var(--ink-3); font-size: 13px; }

.tabs { display: flex; gap: 3px; border-bottom: 1px solid var(--line-soft); overflow-x: auto; scrollbar-width: none; }
.tabs::-webkit-scrollbar { display: none; }
.tabs button {
  background: none; border: 0; border-bottom: 2px solid transparent; color: var(--ink-3);
  padding: 11px 14px; font: inherit; font-size: 13px; font-weight: 650; cursor: pointer; white-space: nowrap;
}
.tabs button:hover { color: var(--ink-2); }
.tabs button.is-active { color: var(--ink); border-bottom-color: var(--accent); }
.tabpane { display: none; padding-top: 16px; }
.tabpane.is-active { display: block; }

/* your event strip */
.yours {
  border: 1px solid rgba(255,122,24,.3);
  background: linear-gradient(135deg, rgba(255,122,24,.09), rgba(255,122,24,.02));
  border-radius: var(--radius); padding: 16px;
}
.yours__grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.yours__item b { display: block; font-size: 14px; font-weight: 700; margin-top: 2px; }
.yours__item span { font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3); font-weight: 800; }

/* market */
.market { display: grid; gap: 8px; }
.mkt {
  display: grid; grid-template-columns: 1fr auto auto; gap: 10px; align-items: center;
  background: var(--panel); border: 1px solid var(--line-soft); border-radius: 9px; padding: 9px 12px;
  cursor: pointer; text-align: left; font: inherit; color: inherit; width: 100%;
}
.mkt:hover { border-color: var(--accent); background: var(--panel-2); }
.mkt.is-sel { border-color: var(--accent); background: rgba(255,122,24,.1); }
.mkt__name { font-weight: 600; font-size: 13.5px; }
.mkt__prob { font-family: var(--mono); font-size: 11px; color: var(--ink-3); }
.mkt__odds {
  font-family: var(--mono); font-weight: 800; font-size: 14px; color: var(--accent-2);
  background: rgba(255,183,3,.1); border: 1px solid rgba(255,183,3,.25); border-radius: 6px; padding: 3px 9px;
}

/* chat */
.chat { display: flex; flex-direction: column; height: 420px; }
.chat__log { flex: 1; overflow-y: auto; padding: 12px; display: flex; flex-direction: column; gap: 9px; }
.chat__msg { display: grid; grid-template-columns: 26px 1fr; gap: 9px; font-size: 13.5px; }
.chat__msg.is-system { grid-template-columns: 1fr; color: var(--accent-2); font-size: 12.5px; font-style: italic; }
.chat__who { font-size: 11.5px; font-weight: 750; color: var(--ink-2); margin-bottom: 1px; }
.chat__who i { font-style: normal; font-size: 9.5px; letter-spacing: .1em; padding: 1px 5px; border-radius: 3px; margin-left: 5px; color: #0a0d14; font-weight: 900; }
.chat__body { word-wrap: break-word; overflow-wrap: anywhere; color: var(--ink); }
.chat__form { display: flex; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--line-soft); }
.chat__form input { flex: 1; }
.chat__tabs { display: flex; gap: 2px; padding: 8px 12px 0; }
.chat__tabs button {
  background: none; border: 1px solid transparent; color: var(--ink-3); border-radius: 7px 7px 0 0;
  padding: 6px 11px; font: inherit; font-size: 12px; font-weight: 650; cursor: pointer;
}
.chat__tabs button.is-active { background: var(--panel-2); border-color: var(--line-soft); border-bottom-color: transparent; color: var(--ink); }

/* activity feed */
.feed { display: flex; flex-direction: column; }
.feed__item { padding: 11px 14px; border-bottom: 1px solid var(--line-soft); font-size: 13px; display: flex; gap: 10px; }
.feed__item:last-child { border-bottom: 0; }
.feed__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ink-3); margin-top: 7px; flex: none; }
.feed__item.imp-3 .feed__dot, .feed__item.imp-4 .feed__dot, .feed__item.imp-5 .feed__dot { background: var(--accent); }
.feed__time { color: var(--ink-3); font-size: 11px; margin-top: 3px; }

/* entrant / race card rows */
.entrant {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 12px; align-items: center;
  padding: 11px 12px; border-bottom: 1px solid var(--line-soft);
}
.entrant:last-child { border-bottom: 0; }
.entrant__num {
  width: 34px; height: 34px; border-radius: 8px; display: grid; place-items: center;
  font-family: var(--mono); font-weight: 800; font-size: 13px; color: #0a0d14;
}
/* Wider slot for a 12-hour clock — "10:45pm" does not fit a 34px square. */
.entrant__num--time {
  width: 54px; height: 30px; border-radius: 6px; font-size: 10.5px; letter-spacing: -.02em;
  background: var(--panel-2); color: var(--ink-2); border: 1px solid var(--line-soft);
}
.entrant__name { font-weight: 700; font-size: 14px; }
.entrant__note { font-size: 11.5px; color: var(--ink-3); margin-top: 2px; }
.entrant__odds { font-family: var(--mono); font-weight: 700; color: var(--accent-2); font-size: 13.5px; }

/* stat blocks */
.statgrid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
.stat { background: var(--panel); border: 1px solid var(--line-soft); border-radius: var(--radius-sm); padding: 13px; }
.stat__label { font-size: 10px; letter-spacing: .15em; text-transform: uppercase; color: var(--ink-3); font-weight: 800; }
.stat__value { font-size: 24px; font-weight: 800; letter-spacing: -.02em; margin-top: 3px; font-family: var(--mono); }
.stat__sub { font-size: 11.5px; color: var(--ink-3); margin-top: 2px; }

/* sample-size marker: always visible, as the brief demands */
.samples { font-family: var(--mono); font-size: 11px; color: var(--ink-3); }

/* hero */
.hero {
  display: grid; gap: 22px; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  align-items: start; margin-bottom: 26px;
}
/* minmax(0, 1fr), not a bare 1fr — see the note on `.split` above. This is the
   grid the home-page television lives in, and the bare `1fr` here is what let
   the panel blow the column off the side of the screen on a phone. */
@media (max-width: 1080px) { .hero { grid-template-columns: minmax(0, 1fr); } }

/* team cards */
.teamcard { position: relative; overflow: hidden; border-radius: var(--radius); border: 1px solid var(--line-soft); }
.teamcard__top { padding: 18px; position: relative; }
.teamcard__top::after {
  content: ''; position: absolute; inset: 0; opacity: .16;
  background: radial-gradient(340px 160px at 15% 0%, var(--tc, #888), transparent 70%);
}
.teamcard__name { font-size: 21px; font-weight: 800; letter-spacing: -.02em; position: relative; }
.teamcard__meta { font-size: 12px; color: var(--ink-3); position: relative; }
.teamcard__body { padding: 0 18px 18px; }

/* course strip */
.coursestrip { display: flex; gap: 3px; overflow-x: auto; padding-bottom: 6px; scrollbar-width: thin; }
.coursemod {
  flex: none; width: 96px; border-radius: 8px; border: 1px solid var(--line-soft);
  background: var(--panel); padding: 9px; font-size: 11px;
}
.coursemod b { display: block; font-size: 11.5px; margin-bottom: 3px; font-weight: 700; line-height: 1.2; }
.coursemod span { color: var(--ink-3); font-size: 10px; text-transform: uppercase; letter-spacing: .08em; }
.coursemod--hidden { opacity: .35; filter: grayscale(1); }
.coursemod__bar { height: 3px; border-radius: 2px; margin-top: 7px; }

/* misc */
.empty { text-align: center; padding: 34px 18px; color: var(--ink-3); }
.empty h3 { color: var(--ink-2); }
.badgegrid { display: flex; flex-wrap: wrap; gap: 8px; }
.badge {
  display: inline-flex; align-items: center; gap: 7px; padding: 7px 11px; border-radius: 999px;
  background: var(--panel-2); border: 1px solid var(--line); font-size: 12px; font-weight: 650;
}
.badge b { font-size: 15px; }
.badge--t4, .badge--t5 { border-color: rgba(255,183,3,.5); background: rgba(255,183,3,.1); color: var(--accent-2); }

.verify code {
  display: block; font-family: var(--mono); font-size: 11.5px; word-break: break-all;
  background: #0a0f19; border: 1px solid var(--line-soft); border-radius: 8px; padding: 11px; color: var(--ink-2);
}

/* ==========================================================================
   Admin debug tray — a pull-out drawer clipped to the right edge
   ========================================================================== */
.debugtray {
  position: fixed; top: 50%; right: 0; z-index: 90;
  transform: translateY(-50%) translateX(calc(100% - 30px));
  transition: transform .28s cubic-bezier(.2, .7, .3, 1);
  display: flex; align-items: stretch;
  max-height: 88vh;
}
.debugtray.is-open { transform: translateY(-50%) translateX(0); }

.debugtray__tab {
  width: 30px; flex: none; cursor: pointer; border: 0; padding: 0;
  background: linear-gradient(180deg, #ff8a2b, #e56a05); color: #180c02;
  border-radius: 10px 0 0 10px; font: inherit; font-weight: 900;
  box-shadow: -6px 0 24px rgba(0, 0, 0, .5);
  /* Closed, the handle is only as tall as its own label. The tray is a stretch
     flex row, so without this the tab grows to the full height of the panel
     beside it (up to `max-height`) and hangs down the whole edge of the screen
     while nothing is even open. It runs full height only once pulled out. */
  align-self: center;
}
.debugtray.is-open .debugtray__tab { align-self: stretch; }
.debugtray__tab span {
  display: block; writing-mode: vertical-rl; transform: rotate(180deg);
  letter-spacing: .28em; font-size: 10px; padding: 14px 0;
}
.debugtray__tab:hover { filter: brightness(1.08); }

.debugtray__panel {
  width: 288px; background: var(--panel-2); border: 1px solid var(--line);
  border-right: 0; border-radius: 10px 0 0 10px; overflow: hidden;
  box-shadow: -14px 0 44px rgba(0, 0, 0, .55); display: flex; flex-direction: column;
}
.debugtray__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 14px; border-bottom: 1px solid var(--line-soft); background: rgba(255, 122, 24, .1);
}
.debugtray__body { padding: 14px; overflow-y: auto; }
.debugtray__body .btn { margin-bottom: 6px; text-align: left; justify-content: flex-start; }
.debugtray__state {
  background: #0b101a; border: 1px solid var(--line-soft); border-radius: 8px;
  padding: 10px; margin-bottom: 12px; font-size: 13px;
}
.debugtray__note { font-size: 11px; color: var(--ink-3); margin: 10px 0 0; line-height: 1.45; }
.debugtray__links { display: flex; gap: 10px; margin-top: 10px; font-size: 11.5px; }
.debugtray__links a { color: var(--ink-3); }
.debugtray__links a:hover { color: var(--accent); }

@media (max-width: 700px) {
  .debugtray { top: auto; bottom: 12px; transform: translateY(0) translateX(calc(100% - 30px)); max-height: 70vh; }
  .debugtray.is-open { transform: translateY(0) translateX(0); }
  .debugtray__panel { width: min(288px, calc(100vw - 42px)); }
}

.foot { border-top: 1px solid var(--line-soft); background: #070a10; margin-top: 40px; }
.foot__in { max-width: 1400px; margin: 0 auto; padding: 28px 18px; display: grid; gap: 16px;
            grid-template-columns: minmax(0, 1.4fr) auto; align-items: start; }
.foot__in b { font-size: 13px; letter-spacing: .1em; }
.foot__in p { color: var(--ink-3); font-size: 12px; margin: 6px 0 0; max-width: 60ch; }
.foot nav { display: flex; flex-wrap: wrap; gap: 14px; font-size: 12.5px; color: var(--ink-2); }
.foot__sig { grid-column: 1 / -1; color: var(--ink-3); font-size: 11.5px; margin: 0; }
@media (max-width: 700px) { .foot__in { grid-template-columns: 1fr; } }

/* --- mobile --------------------------------------------------------------- */
@media (max-width: 760px) {
  /* The nav drops to its own row, so the bar is two rows tall on a phone and
     the sticky offsets below have to agree with that or the broadcast panel
     parks itself over the navigation. */
  .topbar__in { height: auto; min-height: 54px; gap: 10px; padding-top: 8px; padding-bottom: 8px; flex-wrap: wrap; }
  /* The right cluster — What's on, the LIVE pill, the group, the wallet, the
     avatar — is five pills in a row that has no business being wider than a
     phone. It was `flex: none`, so it neither wrapped nor shrank: past about
     360px the last pill ran off the right edge, and a row wider than the
     viewport is exactly what makes a mobile browser pan and zoom the whole
     page. It takes its own line here and wraps within it. */
  .topbar__right { flex: 1 1 100%; flex-wrap: wrap; justify-content: flex-end; min-width: 0; row-gap: 6px; }
  .mainnav { order: 3; width: 100%; }
  .brand__text b { font-size: 13.5px; }
  .wallet { font-size: 12px; padding: 4px 8px; }
  .usermenu__name { display: none; }
  .bc-order { display: none; }
  .bug { max-width: 68%; font-size: 11px; }
  .bc-lower { left: 8px; right: 8px; bottom: 44px; max-width: none; }
  .broadcast--sticky { position: static; border-radius: var(--radius); margin: 0; }
  .wrap { padding: 14px 14px 60px; }
  .chat { height: 340px; }
  h1 { font-size: 25px; }

  /* The switcher panel and the guide both want more width than a phone has
     to the right of where they hang, so they centre instead. */
  .switcher__panel { left: 0; right: auto; min-width: min(320px, calc(100vw - 28px)); }
  .whatson__panel { right: -8px; padding: 10px; }
  .boardhead { padding: 12px; }
  .boardhead__mark { width: 38px; height: 38px; font-size: 13px; }
  .roster { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); padding: 10px; gap: 10px; }
  .playerview { width: min(var(--size, 160px), 118px); height: min(var(--size, 160px), 118px); }
}

/* There is no `prefers-reduced-motion` block here, and that is deliberate.

   There was one, and it opened with a blanket `*` reset to `.01ms` with a
   handful of exemptions clawing back the parts that are the product. What it
   produced was a network ident with its background sweep, its drifting
   chevrons and its lens flare all `display: none` and its mark allowed
   nine-tenths of a second — a logo appearing on a flat rectangle, which is
   what "the splash has literally zero movement" was.

   The operator has asked twice for the ident to play in full regardless of
   the setting, and it is their network. Nothing here honours it now, and the
   motion is the same for everybody.

   **There are two idents on this site and they live in different files.**
   This one — `.bc-inter` — is the interstitial inside the broadcast panel.
   The commercial break has its own, `.adident`, in `ads.css`. Fixing one and
   testing the other cost several rounds: if a change to "the ident" appears
   to do nothing, check which of the two is actually on screen. */


/* ===========================================================================
   The television, on a small screen
   ---------------------------------------------------------------------------
   The panel is a 16:9 box and every piece of furniture on it — the bug, the
   running order, the lower third, the ticker — was authored in fixed pixels
   against a panel about a thousand wide. On a phone that panel is around 358
   across and 201 tall, so a 218px running order covers three-fifths of the
   picture and a 13.5px lower third is half the height of the screen it is on.

   There *were* rules that shrank all of this, for the half-size sticky state,
   and every one of them sat inside `@media (min-width: 1024px)` — so the one
   place the furniture was already known to be too big was the one place the
   fix could never apply.

   **Sized against the panel, not the viewport.** A breakpoint is the wrong
   instrument here: the panel is 100% of a phone, 52% of a scrolled desktop and
   something else again in the control booth, and its width — not the window's —
   is what decides whether a caption fits. `cqw` is a percentage of the panel,
   so one set of rules covers every one of those cases and the half-size desktop
   state stops needing its own copy.

   The reference is a 1000px panel, which makes 1cqw ≈ 10px and every number
   below legible against the px value it replaces: a 24px logo is 2.4cqw. Each
   is clamped so it never grows past what was authored and never collapses to
   nothing on a very narrow one.

   In `@supports` with the px values left in place above, because a browser
   without container queries must get the original stylesheet rather than a
   pile of dropped declarations.
   =========================================================================== */

@supports (container-type: inline-size) {
  .broadcast__stage { container-type: inline-size; container-name: tv; }

  /* -- the bug ------------------------------------------------------------ */
  .bug        { font-size: clamp(8px, 1.2cqw, 12px);
                top: clamp(6px, 1.4cqw, 14px); left: clamp(6px, 1.4cqw, 14px); }
  .bug__brand { font-size: clamp(7px, 1cqw, 10px);
                padding: clamp(3px, .7cqw, 7px) clamp(5px, 1cqw, 10px); }
  .bug__sport { font-size: clamp(6px, .9cqw, 9px); }
  .bug .bug__sport { padding: clamp(2px, .4cqw, 4px) clamp(4px, .9cqw, 9px); }
  .bug__mark  { height: clamp(13px, 2.4cqw, 24px); max-width: clamp(38px, 7.4cqw, 74px); }
  .bug__body  { padding: clamp(3px, .7cqw, 7px) clamp(6px, 1.2cqw, 12px); }
  .bug__sub   { font-size: clamp(6.5px, 1.05cqw, 10.5px); }
  .bug__clock { padding: clamp(3px, .7cqw, 7px) clamp(6px, 1.2cqw, 12px); }
  .bug__lap   { padding: clamp(3px, .7cqw, 7px) clamp(5px, 1.1cqw, 11px); }
  .bug__lap-n { font-size: clamp(9px, 1.5cqw, 15px); }
  .bug__lap-of{ font-size: clamp(7px, 1.1cqw, 11px); }
  .bc-live    { font-size: clamp(6.5px, 1.05cqw, 10.5px);
                padding: clamp(3px, .5cqw, 5px) clamp(5px, 1cqw, 10px);
                top: clamp(6px, 1.4cqw, 14px); }

  /* -- the running order --------------------------------------------------
     The one that actually swallows the picture. It also goes entirely below a
     very narrow panel: an eight-name list rendered four pixels tall is not a
     smaller running order, it is a smear over the thing you are watching. */
  .bc-order          { width: clamp(96px, 21.8cqw, 218px);
                       top: clamp(28px, 5.6cqw, 56px);
                       right: clamp(6px, 1.4cqw, 14px);
                       max-height: calc(100% - clamp(56px, 12cqw, 120px)); }
  .bc-order__row     { font-size: clamp(7px, 1.15cqw, 11.5px);
                       padding: clamp(2px, .4cqw, 4px) clamp(4px, .8cqw, 8px); }
  .bc-order__gap     { font-size: clamp(6.5px, 1.05cqw, 10.5px); }
  .bc-order__crest   { width: clamp(11px, 2cqw, 20px); height: clamp(11px, 2cqw, 20px); }
  .bc-order__score   { font-size: clamp(8px, 1.25cqw, 12.5px); }
  .bc-order__hp      { height: clamp(3px, .6cqw, 6px); }

  /* -- the lower third and the ticker ------------------------------------- */
  .bc-lower       { left: clamp(6px, 1.4cqw, 14px);
                    bottom: clamp(26px, 5.8cqw, 58px);
                    max-width: min(78%, 620px); }
  .bc-lower__text { font-size: clamp(8.5px, 1.35cqw, 13.5px);
                    padding: clamp(4px, .9cqw, 9px) clamp(7px, 1.5cqw, 15px); }
  .bc-lower__tag  { font-size: clamp(6.5px, 1cqw, 10px);
                    padding: clamp(4px, .9cqw, 9px) clamp(6px, 1.2cqw, 12px); }
  .bc-ticker        { height: clamp(16px, 3cqw, 30px); }
  .bc-ticker__label { font-size: clamp(6px, .95cqw, 9.5px); padding: 0 clamp(5px, 1.1cqw, 11px); }

  /* -- the boards --------------------------------------------------------- */
  .bc-board__ms     { font-size: clamp(8px, 1.2cqw, 12px); }
  .bc-board__crest  { width: clamp(14px, 2.6cqw, 26px); height: clamp(14px, 2.6cqw, 26px); }
  .bc-winner__time  { font-size: clamp(10px, 1.5cqw, 15px); }
  .bc-camid         { font-size: clamp(7px, 1.1cqw, 11px);
                      padding: clamp(2px, .3cqw, 3px) clamp(5px, 1cqw, 10px);
                      top: clamp(6px, 1.2cqw, 12px); }

  /* The half-size desktop state no longer needs its own copy of any of this —
     it is simply a narrower panel, and every rule above already knows that. */
}

/* Below this the running order is unreadable at any size that would also leave
   the picture visible, so it goes. The information is on the board at the end
   and in the page under the panel; the picture is the thing that cannot be
   anywhere else. */
@media (max-width: 560px) {
  .bc-order { display: none; }
}

/* ---------------------------------------------------------------------------
   And the cards *inside* it, which were sized in viewport height

   `.bc-inter__logo` was `clamp(90px, 26vh, 210px)`. **`vh` is the window; the
   card is inside a 16:9 panel whose height has nothing to do with the window.**
   On a phone that resolves to a 210px mark inside a 201px panel — and the
   interstitial is a centred grid of mark, kicker, title and rule, so ~300px of
   content is centred in 201px and the mark, being first, loses its top half.
   The reported symptom was exactly that: a logo half off the top of the frame.

   Every one of these means "a share of the panel", so every one becomes `cqw`.
   The panel is 16:9, so its height is 0.5625 of its width and there is no need
   for a height container: a share of the width says the same thing. The figures
   are chosen to reach the authored maximum at about a 1200px panel, and the
   minimums come down because the old ones were floors for a full-size screen.

   The four `vh` values left in the file are on `.broadcast--sticky
   .broadcast__stage` itself, and they are correct — capping the television
   against the window is exactly what they are for.
   ------------------------------------------------------------------------- */

@supports (container-type: inline-size) {
  .bc-inter__logo    { height: clamp(40px, 17.5cqw, 210px); }
  .bc-board__logo    { height: clamp(12px, 3.8cqw, 46px); }
  .bc-board__title   { font-size: clamp(7px, .92cqw, 11px);
                       margin-bottom: clamp(3px, 1cqw, 12px); }
  .bc-board__list    { gap: clamp(1px, .25cqw, 3px); }
  .bc-board__list li { padding: clamp(1px, .42cqw, 5px) clamp(4px, .8cqw, 9px);
                       font-size: clamp(7px, 1.08cqw, 13px); }
  .bc-board__foot    { margin-top: clamp(3px, 1cqw, 12px);
                       font-size: clamp(6.5px, .88cqw, 10.5px); }
  .bc-inter__kicker  { font-size: clamp(7px, 1.05cqw, 13px); }
  .bc-inter__title   { font-size: clamp(14px, 4.3cqw, 52px); }
}

/* The club badge, wherever a team is named — see `team_mark()`.
   A fixed box so a row's height does not depend on which crest is in it: the
   shapes are different heights at the same width and a table whose rows breathe
   as you scroll is worse than one with dots on it. */
.teammark {
  display: inline-grid;
  place-items: center;
  flex: none;
  line-height: 0;
}
.teammark svg { display: block; width: 100%; height: 100%; }

/* ---------------------------------------------------------------------------
   The interstitial, retimed and given something to do

   It holds for 2400ms (`hold` on the `inter` step in broadcast.js) and the
   build used to occupy the first 1.6s of that, with the last third static —
   the same fault the break's ident had, in a different file. Everything below
   is timed against the real hold, and there are now three things moving at
   once rather than one: a slow push on the whole frame, the mark's arrival,
   and the background crossing behind it.

   The push is on `.bc-inter` itself so it carries the backdrop, the mark and
   the type together — one slow camera move rather than three elements each
   doing something. That also sidesteps the fact that a single element cannot
   run two `transform` animations at once: the mark's arrival is its own, and
   the push belongs to the frame around it.
   ------------------------------------------------------------------------- */

.bc-inter.is-on { animation: interPush 2.4s cubic-bezier(.25, .6, .3, 1) both; }
@keyframes interPush {
  0%   { transform: scale(1.045); }
  100% { transform: scale(1); }
}

/* The mark: bigger arrival, and it keeps drifting after it lands rather than
   stopping dead two-thirds of the way through the card. */
.bc-inter.is-on .bc-inter__logo { animation: interMark 1.45s cubic-bezier(.16, .84, .24, 1) .06s forwards; }
@keyframes interMark {
  0%   { opacity: 0; transform: translateX(-64%) rotateY(-96deg) scale(1.85); }
  34%  { opacity: 1; }
  70%  { transform: translateX(0) rotateY(11deg) scale(1.06); }
  86%  { transform: translateX(0) rotateY(-3deg) scale(.995); }
  100% { opacity: 1; transform: translateX(0) rotateY(0deg) scale(1); }
}

/* The background. The blade crosses for most of the card instead of a beat of
   it, and the chevrons drift the whole way rather than stopping at 2.6s on a
   card that ends at 2.4. */
.bc-inter.is-on::before { animation: interSweep 1.5s cubic-bezier(.22, .9, .2, 1) forwards; }
.bc-inter.is-on::after  { animation: interDrift 2.4s linear forwards; }
@keyframes interDrift {
  0%   { opacity: 0; transform: translateX(-56px); }
  18%  { opacity: 1; }
  100% { opacity: .85; transform: translateX(56px); }
}

/* The type, spaced out across the second half so the card keeps arriving. */
.bc-inter.is-on .bc-inter__kicker { animation: interLine .5s ease .78s forwards; }
.bc-inter.is-on .bc-inter__title  { animation: interLine .62s cubic-bezier(.2,.8,.3,1) .98s forwards; }
.bc-inter.is-on .bc-inter__rule  { animation: interRule .6s cubic-bezier(.2,.8,.3,1) 1.18s forwards; }
.bc-inter.is-on .bc-inter__flare { animation: interFlare .9s cubic-bezier(.1, .9, .2, 1) .62s forwards; }

/* The break mount inside the television fills it.
   ---------------------------------------------------------------------------
   `ads.css` gives every mount `position: relative` so the junction's own boxes
   have something to sit in. On the standalone preview the mount *is* the frame
   and has its own size, so that is enough. Inside the panel it is a wrapper
   around `.bc-card`, which is `position: absolute; inset: 0` — and a relatively
   positioned wrapper with no height of its own is what that card then resolves
   against. It collapsed, and the panel went black.

   Scoped to the stage on purpose: making *every* mount absolute would break the
   control booth's preview, where the mount is the outer frame rather than a
   wrapper inside one. */
.broadcast__stage > [data-break-mount] { position: absolute; inset: 0; }

/* A junction on air IS the picture.
   ---------------------------------------------------------------------------
   `.bc-overlay` — the bug, the running order, the lower third, the standings
   board — is `z-index: 3` inside the stage. The break mount had no z-index at
   all, so every commercial played *underneath* the panel's own furniture: an
   opaque FULL TIME board over the top of a running junction, with the break's
   corner bug showing through wherever the overlay happened to be transparent.

   Which is why the answer to "I have never seen a single commercial" was that
   they had been playing all along, behind the board.

   Above the overlay, and only while `has-break` is set, so the moment the
   junction hands back the panel's furniture is exactly where it was. A channel
   showing a commercial is showing the commercial — not the commercial with the
   last programme's classification over it. */
.broadcast__stage > [data-break-mount].has-break { z-index: 5; }
