/* WatchAlongs v2 — the whole visual system for the new homepage and watch pages.
   Hand-maintained static file (no build step; the VPS has no node toolchain).
   Brand: the logo's own palette. Orange #D64010 leads (live, action), sky #29ABE2
   supports (focus, links), gold #FFC60B counts, indigo #2E318F deepens the dark. */

:root {
	--bg: #0d0f15; --bg-raise: #14171f; --bg-card: #171b25;
	--line: rgba(255,255,255,.08); --line-strong: rgba(255,255,255,.16);
	--ink: #eef0f5; --ink-dim: #97a0b2; --ink-faint: #8b93a6;
	--brand: #D64010; --brand-soft: rgba(214,64,16,.14);
	--sky: #29ABE2; --gold: #FFC60B; --indigo: #2E318F;
	--r: 14px; --pad: clamp(12px, 3vw, 28px);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
/* No element may make the PAGE pan sideways; wide things scroll inside themselves. */
html { overflow-x: clip; }
/* The hidden attribute maps to a UA style that ANY author display property overrides --
   so .card { display:block } silently defeated hidden and filtering changed nothing on
   screen. One rule restores the contract everywhere, permanently. */
[hidden] { display: none !important; }
html { scrollbar-color: #333a4a var(--bg); }
body {
	background:
		radial-gradient(1100px 500px at 85% -10%, rgba(46,49,143,.25), transparent 60%),
		radial-gradient(900px 420px at -10% 0%, rgba(214,64,16,.10), transparent 55%),
		var(--bg);
	color: var(--ink);
	font: 15px/1.45 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
	-webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; }
button { font: inherit; }
.shell { max-width: 1320px; margin: 0 auto; padding: 0 var(--pad); }
.shell.narrow { max-width: 1100px; }

/* ---------- top bar ---------- */
.top { display: flex; align-items: center; gap: 16px; padding: 14px 0; }
.brand { display: flex; align-items: center; gap: 10px; white-space: nowrap; }
.brand img { height: 44px; width: auto; }
.brand .word { display: flex; align-items: baseline; font-weight: 800; font-size: 21px; letter-spacing: -.02em; }
.brand .w { background: linear-gradient(100deg, var(--brand), var(--gold)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.brand .a { color: var(--sky); }
.brand small { font-weight: 600; font-size: 10px; color: var(--ink-faint); letter-spacing: .14em; text-transform: uppercase; margin-left: 8px; align-self: center; }
@media (max-width: 500px) { .brand .word { display: none; } }

.search { flex: 1; max-width: 460px; margin-left: auto; position: relative; }
.search input {
	width: 100%; background: var(--bg-raise); border: 1px solid var(--line); border-radius: 999px;
	color: var(--ink); padding: 9px 16px 9px 38px; font-size: 14px; outline: none;
	transition: border-color .15s, box-shadow .15s;
}
.search input:focus { border-color: var(--sky); box-shadow: 0 0 0 3px rgba(41,171,226,.15); }
.search svg { position: absolute; left: 13px; top: 50%; translate: 0 -50%; opacity: .5; }

.locale { display: flex; gap: 8px; align-items: center; }
.locale label { font-size: .68rem; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-faint); }
.locale select {
	appearance: none; -webkit-appearance: none;
	background: var(--bg-raise) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6'><path d='M1 1l4 4 4-4' fill='none' stroke='%2397a0b2' stroke-width='1.6' stroke-linecap='round'/></svg>") no-repeat right 12px center;
	border: 1px solid var(--line); border-radius: 999px; color: var(--ink);
	font-size: 13px; padding: 8px 30px 8px 14px; cursor: pointer;
}
.locale select:hover { border-color: var(--line-strong); }
.locale select:focus { outline: none; border-color: var(--sky); box-shadow: 0 0 0 3px rgba(41,171,226,.15); }
.locale select option { background: #14171f; color: var(--ink); }
@media (max-width: 860px) { .locale label { display: none; } }

/* ---------- the mobile burger (item 408) ----------
   Richard, 2026-08-19: "Mobile only add a burger menu top right and move 'Streams' and
   'Auto UK' into there." MOBILE ONLY is half the ask and the half a desktop reviewer never
   sees, so desktop is spelled out first and the phone rules are additions to it.

   display:contents is what lets ONE copy of each control serve both: above 640px the
   .nav-menu wrapper contributes no box at all, so .mainnav and .locale are flex items of
   .top exactly as they were before this item -- no duplicated markup, no second
   #locale-region for locale.js to miss. The wrapper does sit before .search in the DOM
   now (the panel has to be one element), so the row order is stated rather than left to
   source order: brand, Streams, search, region. Change these and desktop moves. */
.nav-menu { display: contents; }
.top > .brand { order: 1; }
.top .mainnav { order: 2; }
.top > .search { order: 3; }
.top .locale { order: 4; }
.nav-toggle {
	display: none;   /* mobile only -- the 640px block below is the ONLY place it appears */
	order: 5; align-items: center; justify-content: center;
	width: 42px; height: 38px; padding: 0;
	background: var(--bg-raise); border: 1px solid var(--line); border-radius: 12px;
	color: var(--ink-dim); cursor: pointer; transition: border-color .13s, color .13s;
}
.nav-toggle:hover { color: var(--ink); border-color: var(--line-strong); }
.nav-toggle:focus-visible { outline: none; border-color: var(--sky); box-shadow: 0 0 0 3px rgba(41,171,226,.15); }
/* Three bars from one element: the middle is the box, the outer two are its borders,
   so there is a single span to keep aria-hidden. */
.nav-toggle-bars, .nav-toggle-bars::before, .nav-toggle-bars::after {
	display: block; width: 18px; height: 2px; border-radius: 2px; background: currentColor;
}
.nav-toggle-bars { position: relative; }
.nav-toggle-bars::before, .nav-toggle-bars::after { content: ""; position: absolute; left: 0; }
.nav-toggle-bars::before { top: -6px; }
.nav-toggle-bars::after { top: 6px; }

@media (max-width: 640px) {
	/* The search box is untouched here, deliberately: it keeps the full-width second row it
	   has always had, on every page. Only Streams and the region picker move. */
	.top { flex-wrap: wrap; position: relative; }
	.search { order: 3; flex-basis: 100%; max-width: none; }
	/* order 2 puts the burger beside the brand on the FIRST row -- after .search (order 3)
	   it would land under the full-width search box instead of top right. */
	.nav-toggle { display: inline-flex; order: 2; margin-left: auto; }
	.nav-menu {
		display: none; position: absolute; top: 100%; right: 0; z-index: 60;
		flex-direction: column; align-items: stretch; gap: 12px;
		min-width: 200px; padding: 14px;
		background: var(--bg-raise); border: 1px solid var(--line-strong);
		border-radius: var(--r); box-shadow: 0 18px 40px rgba(0,0,0,.55);
	}
	.top.nav-open .nav-menu { display: flex; }
	.nav-menu .mainnav { flex-direction: column; gap: 8px; }
	.nav-menu .mainnav a { text-align: center; }
	.nav-menu .locale { flex-direction: column; align-items: stretch; gap: 6px; }
	/* The label comes back INSIDE the menu: on the header row "Region" is cut at 860px for
	   space and the control still reads as a region by sitting there, but a bare "Auto" in
	   a list of menu items says nothing. */
	.nav-menu .locale label { display: block; }
	.nav-menu .locale select { width: 100%; }
}

.back {
	display: inline-flex; align-items: center; gap: 6px; margin-left: auto;
	background: var(--bg-raise); border: 1px solid var(--line); border-radius: 999px;
	color: var(--ink-dim); font-size: 13px; font-weight: 600; padding: 7px 14px;
	transition: all .13s;
}
.back:hover { color: var(--ink); border-color: var(--line-strong); }

/* ---------- live rail ---------- */
.live-head { display: flex; align-items: center; gap: 10px; margin: 18px 0 4px; }
.live-head h2 { font-size: 17px; font-weight: 700; letter-spacing: .01em; }
.pulse { width: 9px; height: 9px; border-radius: 50%; background: var(--brand); box-shadow: 0 0 0 0 rgba(214,64,16,.6); animation: pulse 1.8s infinite; }
@keyframes pulse { 70% { box-shadow: 0 0 0 9px rgba(214,64,16,0); } 100% { box-shadow: 0 0 0 0 rgba(214,64,16,0); } }
.live-head .count { color: var(--gold); font-weight: 700; font-size: 14px; }
.live-head .arrows { margin-left: auto; display: flex; gap: 8px; }
.rail-btn {
	width: 34px; height: 34px; border-radius: 50%; border: 1px solid var(--line-strong);
	background: var(--bg-raise); color: var(--ink); font-size: 16px; line-height: 1;
	cursor: pointer; display: grid; place-items: center; transition: all .13s;
}
.rail-btn:hover { border-color: var(--sky); color: var(--sky); }
@media (hover: none) { .live-head .arrows { display: none; } }
/* padding-top: the scroll container clips vertically, and a hovered card lifts 3px plus
   its ring -- without headroom the top of the card is shaved off mid-hover. */
.rail { display: grid; grid-auto-flow: column; grid-auto-columns: clamp(240px, 30vw, 330px); gap: 14px; overflow-x: auto; padding: 8px 0 10px; scroll-snap-type: x mandatory; scrollbar-width: thin; scroll-behavior: smooth; }
.rail > * { scroll-snap-align: start; }
.rail.compact { grid-auto-columns: clamp(220px, 26vw, 300px); }

/* ---------- sticky filter strip ---------- */
/* Full-bleed on purpose: the strip is a BODY-level element, so it is simply full width
   with a normal .shell inside. The old negative-margin trick (built for a strip nested
   inside a padded shell) overflowed the viewport by a pad each side at body level --
   the page could pan sideways and every left edge on mobile looked cut off. */
.strip { position: sticky; top: 0; z-index: 40; background: rgba(13,15,21,.86); backdrop-filter: blur(10px); border-bottom: 1px solid var(--line); margin: 6px 0 0; padding: 8px 0 10px; }
.chip-row { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; align-items: center; }
.chip-row::-webkit-scrollbar { display: none; }
.chip-row + .chip-row { margin-top: 8px; }
.chip-row .push { margin-left: auto; }
/* Desktop sees every chip at a glance: rails WRAP instead of hiding a scrollable tail
   (Richard: the sport row still scrolled). Phones keep the horizontal swipe -- a wrap
   there would eat half the screen. */
@media (min-width: 861px) {
	.chip-row { flex-wrap: wrap; overflow-x: visible; }
}
.chip {
	flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px;
	background: var(--bg-raise); border: 1px solid var(--line); border-radius: 999px;
	color: var(--ink-dim); font-size: 13px; font-weight: 600; padding: 6px 13px;
	cursor: pointer; user-select: none; transition: all .13s;
}
.chip:hover { border-color: var(--line-strong); color: var(--ink); }
.chip.on { background: var(--brand); border-color: var(--brand); color: #fff; }
.chip.day.on { background: var(--sky); border-color: var(--sky); }
.chip .n { font-size: 11px; font-weight: 700; color: var(--gold); }
.chip.on .n { color: rgba(255,255,255,.85); }
/* Item 165: a zero chip used to fade WHOLE (opacity .38), which took the label to 1.98:1 --
   unreadable while still clickable. Dim the frame instead; the label keeps full contrast. */
.chip.zero { color: var(--ink-dim); border-color: rgba(255,255,255,.05); background: rgba(20,23,31,.45); }
.chip img { width: 14px; height: 14px; }
.divider { flex: 0 0 1px; align-self: stretch; background: var(--line); margin: 0 4px; }
/* Item 165: second stop re-derived. It was #d13c10, chosen to sit under the OLD brand
   #F15A24. Against #D64010 the two are near-identical and the gradient reads flat, so this
   applies the same proportional darkening the original pair used. A derived value, not one
   Richard picked -- say if you want it flat or a different shade. */
.chip.live-toggle.on { background: linear-gradient(100deg, var(--brand), #B92B07); }
/* Seasonal chips: gold-edged, present only while their window is open. */
.chip.season { border-color: rgba(255,198,11,.35); color: var(--gold); }
.chip.season.on { background: linear-gradient(100deg, var(--gold), #d9a509); color: #0d0f15; border-color: var(--gold); }
.chip.season.on .n { color: rgba(13,15,21,.75); }
.rail-label { flex: 0 0 auto; font-size: 10px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-faint); width: 40px; }
.show-label { display: none; }
.day-filter-rows { display: flex; flex-direction: column; }
@media (min-width: 861px) {
	/* Keep the established desktop order while Show and Date remain independent rails. */
	.day-filter-rows { flex-direction: row; align-items: center; gap: 8px; }
	.day-filter-rows .chip-row + .chip-row { margin-top: 0; }
	#day-row { order: 1; flex: 1 1 auto; }
	#show-row { order: 2; flex: 0 0 auto; }
}

/* ---------- day heading + grid ---------- */
.day-head { display: flex; align-items: baseline; gap: 12px; margin: 22px 0 14px; }
/* h1 as well as h2: the homepage heading became an h1 (it had none, so the document
   started at h2 and jumped to h3). Same size on purpose -- this is a semantics fix,
   not a redesign. */
.day-head h1, .day-head h2 { font-size: clamp(18px, 2.6vw, 24px); font-weight: 800;
	letter-spacing: -.01em; margin: 0; }
.day-head span { color: var(--ink-faint); font-size: 13.5px; }
.day-head h2 .day-title-full, .day-head h2 .day-title-short { color: inherit; font-size: inherit; }
.day-title-short { display: none; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(250px, 100%), 1fr)); gap: 14px; padding-bottom: 40px; }

/* ---------- cards ---------- */
.card {
	position: relative; display: block; background: var(--bg-card);
	border: 1px solid var(--line); border-radius: var(--r); overflow: hidden;
	transition: transform .16s, border-color .16s, box-shadow .16s;
}
.card:hover { transform: translateY(-3px); border-color: var(--line-strong); box-shadow: 0 12px 30px -12px rgba(0,0,0,.7); }
/* The live treatment ("pulse ring", chosen at /live-highlight-mock): a brand-orange ring
   that breathes, the card-scale echo of the badge's pulsing dot. Keyed off data-live so
   the 30s badge flipper turns it on and off with no extra JS; with reduced motion it is
   a steady ring. Its beacon-sweep sibling is reserved for future featured events. */
.card[data-live="1"] { border-color: var(--brand); box-shadow: 0 0 0 1px var(--brand), 0 0 14px 0 rgba(214,64,16,.25); }
@media (prefers-reduced-motion: no-preference) {
	.card[data-live="1"] { animation: wal-ring 2.6s ease-in-out infinite; }
}
/* A real breath: near-off at the trough, full ring at the peak. The first cut's trough
   still glowed, so fifty live cards read as "permanently outlined" (Richard's screenshot)
   rather than pulsing. */
@keyframes wal-ring {
	0%, 100% { border-color: rgba(214,64,16,.4); box-shadow: 0 0 0 1px rgba(214,64,16,.18), 0 0 4px 0 rgba(214,64,16,.06); }
	50%      { border-color: var(--brand); box-shadow: 0 0 0 2px var(--brand), 0 0 26px 2px rgba(214,64,16,.55); }
}

/* ---------- the Featured-now stage (replaced the Live rail, item 33) ---------- */
.stage { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 16px; }
.stage[data-count="1"] { grid-template-columns: 1fr; }
.stage-slot { position: relative; aspect-ratio: 16/9; border-radius: var(--r); overflow: hidden; border: 1px solid rgba(255,198,11,.4); background: #05070b; }
.stage-slot iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.stage-player-holder { position: absolute; inset: 0; }
.stage-player-holder iframe, .stage-player-holder div { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.stage-kind { position: absolute; top: 10px; left: 10px; z-index: 3; font-size: 10px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; background: var(--brand); color: #fff; padding: 3px 9px; border-radius: 999px; }
.stage-kind.gold { background: var(--gold); color: #0d0f15; }
/* The poster covers the iframe until v2.js activates the slot (instantly on desktop;
   on tap for phones) -- no player boots without a decision. */
.stage-poster { position: absolute; inset: 0; z-index: 2; border: 0; cursor: pointer; background-size: cover; background-position: center; display: grid; place-items: center; }
.stage-poster::after { content: ""; position: absolute; inset: 0; background: rgba(5,8,12,.35); }
.stage-play { position: relative; z-index: 1; width: 64px; height: 64px; border-radius: 50%; background: rgba(10,12,18,.8); border: 2px solid #fff; color: #fff; font-size: 22px; display: grid; place-items: center; padding-left: 5px; }
.stage-slot.on .stage-poster { display: none; }
.stage-overlay {
	position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
	padding: 34px 14px 12px; pointer-events: none;
	background: linear-gradient(transparent, rgba(5,8,12,.9) 72%);
	display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap;
}
.stage-overlay > * { pointer-events: auto; }
.stage-overlay h2 { font-size: 16px; font-weight: 800; line-height: 1.3; }
.stage-overlay .fact { color: var(--gold); font-size: 12px; font-weight: 700; }
.stage-cta { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }
.stage-cta a { background: var(--brand); color: #fff; font-size: 12px; font-weight: 700; padding: 7px 12px; border-radius: 999px; text-decoration: none; white-space: nowrap; }
.stage-cta a.quiet { background: rgba(10,12,18,.78); }
.stage-pick-btn { background: var(--brand); color: #fff; font-size: 12px; font-weight: 700; padding: 7px 12px; border-radius: 999px; border: 0; cursor: pointer; white-space: nowrap; }
.stage-picker {
	position: absolute; right: 10px; bottom: 54px; z-index: 4;
	width: min(320px, calc(100% - 20px)); max-height: 64%;
	background: rgba(13,15,21,.97); border: 1px solid var(--line-strong);
	border-radius: 12px; overflow: hidden;
}
/* Two sliding panes, mobile-menu style: level 1 (shortlist + categories) slides left
   to reveal level 2 (that category's live streams). */
.stage-views { display: flex; width: 200%; transition: transform .22s ease; }
/* Fixed max-height + overscroll containment: `inherit` resolved to nothing inside the
   flex row, so the list had no scrollbar of its own and the wheel scrolled the PAGE --
   which also hid the category browser below the fold (Richard: "not implemented"). */
.stage-view { width: 50%; max-height: 320px; overflow-y: auto; overscroll-behavior: contain; padding: 6px; display: flex; flex-direction: column; gap: 2px; scrollbar-width: thin; }
.stage-picker { display: block; }
.stage-picker.lvl2 .stage-views { transform: translateX(-50%); }
.stage-cats-head { font-size: 10px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-faint); padding: 10px 10px 4px; border-top: 1px solid var(--line); margin-top: 4px; }
.stage-event-row { display: flex; align-items: center; gap: 8px; background: rgba(255,198,11,.08); border: 1px solid rgba(255,198,11,.3); color: var(--gold); font-size: 13px; font-weight: 700; text-align: left; padding: 9px 10px; border-radius: 8px; cursor: pointer; }
.stage-event-row:hover { background: rgba(255,198,11,.16); }
.stage-event-row .ch { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stage-cat-row { display: flex; align-items: center; gap: 8px; background: none; border: 0; color: var(--ink-dim); font-size: 13px; font-weight: 600; text-align: left; padding: 8px 10px; border-radius: 8px; cursor: pointer; }
.stage-cat-row:hover { background: var(--bg-raise); color: var(--ink); }
.stage-cat-row .ch { flex: 1; }
.stage-cat-row .n { color: var(--gold); font-weight: 700; font-size: 12px; }
.stage-back { background: none; border: 0; color: var(--sky); font-size: 13px; font-weight: 700; text-align: left; padding: 8px 10px; cursor: pointer; }
.stage-option { display: flex; align-items: center; gap: 8px; background: none; border: 0; color: var(--ink); font-size: 13px; font-weight: 600; text-align: left; padding: 8px 10px; border-radius: 8px; cursor: pointer; }
.stage-option:hover { background: var(--bg-raise); }
.stage-option.on { background: var(--brand-soft); color: var(--brand); }
.stage-option .ch { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stage-option .opt-thumb { flex: 0 0 58px; aspect-ratio: 16/9; border-radius: 6px; background-size: cover; background-position: center; border: 1px solid var(--line); }
.stage-option .badge-live { position: static; font-size: 9px; padding: 2px 7px; }
.stage-picker-more { display: block; padding: 8px 10px; font-size: 12px; color: var(--sky); text-decoration: none; border-top: 1px solid var(--line); margin-top: 4px; }
.stage-chosen { color: var(--ink-dim); font-weight: 600; }
/* Once the video is actually playing, the chrome gets out of the shot: Twitch's player
   refuses to autoplay when anything overlays it ("style visibility" in its embed error),
   and a clean frame simply looks better. Hovering the slot (CSS :hover reaches ancestors
   of a cross-origin iframe) or opening the picker brings it back. */
.stage-slot.on .stage-kind, .stage-slot.on .stage-overlay { opacity: 0; visibility: hidden; transition: none; }
.stage-slot.on .stage-overlay > * { pointer-events: none; }
/* Hide is INSTANT (transition only on the reveal side): Twitch's player runs its
   autoplay visibility check the moment it boots, and a 0.25s fade-out meant the check
   could still catch the chrome mid-fade and refuse ("style visibility"). Its logger
   also re-fires that warning whenever the chrome is hover-revealed over a playing
   video -- console noise, harmless: gesture-initiated play is exempt. */
.stage-slot.on:hover .stage-kind, .stage-slot.on:hover .stage-overlay,
.stage-slot.on:focus-within .stage-overlay,
.stage-slot.on:has(.stage-picker:not([hidden])) .stage-overlay { opacity: 1; visibility: visible; transition: opacity .25s; }
.stage-slot.on:hover .stage-overlay > *,
.stage-slot.on:focus-within .stage-overlay > *,
.stage-slot.on:has(.stage-picker:not([hidden])) .stage-overlay > * { pointer-events: auto; }
@media (max-width: 860px) {
	/* Phones and small tablets: one slot, tap-to-play -- bandwidth and CPU are the
	   visitor's, and the second slot returns on bigger screens. Playback starts from a
	   tap here, so nothing gates on visibility: the chrome can stay. */
	.stage { grid-template-columns: 1fr; }
	.stage-slot:nth-child(2) { display: none; }
	.stage-overlay h2 { font-size: 14px; }
	.stage-slot.on .stage-kind, .stage-slot.on .stage-overlay { opacity: 1; visibility: visible; }
	.stage-slot.on .stage-overlay > * { pointer-events: auto; }
}

/* ---------- featured event ---------- */
/* The hero band: one occasion elevated above "Live right now". Set by
   `flask feature-event <event-key>`; expires by itself. */
.feat-band {
	display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
	margin-top: 16px; padding: 13px 16px;
	background: linear-gradient(100deg, rgba(255,198,11,.12), rgba(214,64,16,.10) 55%, transparent);
	border: 1px solid rgba(255,198,11,.35); border-radius: var(--r);
}
.feat-tag { font-size: 10px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; color: #0d0f15; background: var(--gold); padding: 3px 9px; border-radius: 999px; }
.feat-band h2 { font-size: 18px; font-weight: 800; }
.feat-live { display: inline-flex; align-items: center; gap: 6px; color: var(--brand); font-size: 13px; font-weight: 700; }
.feat-actions { margin-left: auto; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.feat-cta {
	background: var(--brand); color: #fff; font-size: 13px; font-weight: 700;
	padding: 8px 14px; border-radius: 999px; text-decoration: none;
}
.feat-cta:hover { filter: brightness(1.08); }
.feat-all { color: var(--ink-dim); font-size: 13px; font-weight: 600; text-decoration: none; }
.feat-all:hover { color: var(--ink); }
.event-head.is-featured { border-left: 3px solid var(--gold); padding-left: 14px; }
.event-head .feat-tag { display: inline-block; margin-bottom: 6px; }

/* Featured member cards: a quiet gold edge always... */
.card.featured { border-color: rgba(255,198,11,.45); }
/* ...and the beacon sweep (Option B from /live-highlight-mock) while LIVE -- reserved
   exclusively for featured members, which is what keeps it special. A conic comet
   orbits behind the content; the inset mask leaves a 2px rim; the thumb/body inset
   keeps the rim visible (the thumb is otherwise full-bleed and would cover it). */
/* The sweep replaces the pulse ring on these cards -- both at once is noise. */
.card.featured[data-live="1"] { border-color: rgba(214,64,16,.5); animation: none; box-shadow: none; }
.card.featured[data-live="1"]::before {
	content: ""; position: absolute; inset: -75%; z-index: 0;
	background: conic-gradient(from 0deg, transparent 0deg 265deg,
		rgba(214,64,16,.95) 315deg, var(--gold) 342deg, transparent 360deg);
}
@media (prefers-reduced-motion: no-preference) {
	.card.featured[data-live="1"]::before { animation: wal-sweep 3.2s linear infinite; }
}
.card.featured[data-live="1"]::after {
	content: ""; position: absolute; inset: 2px; z-index: 0;
	background: var(--bg-card); border-radius: calc(var(--r) - 2px);
}
.card.featured[data-live="1"] .thumb, .card.featured[data-live="1"] .card-body { position: relative; z-index: 1; }
.card.featured[data-live="1"] .thumb { margin: 2px 2px 0; border-radius: calc(var(--r) - 3px) calc(var(--r) - 3px) 0 0; }
.card.featured[data-live="1"] .card-body { margin: 0 2px 2px; }
@keyframes wal-sweep { to { transform: rotate(1turn); } }
.thumb { aspect-ratio: 16/9; background-size: cover; background-position: center; position: relative; }
.thumb::after { content: ""; position: absolute; inset: 55% 0 0; background: linear-gradient(transparent, rgba(9,11,16,.72)); }
.badge-live, .badge-time {
	position: absolute; top: 10px; left: 10px; z-index: 2;
	font-size: 11px; font-weight: 800; letter-spacing: .05em;
	padding: 4px 9px; border-radius: 7px; display: inline-flex; align-items: center; gap: 5px;
}
.badge-live { background: var(--brand); color: #fff; }
.badge-live i { width: 6px; height: 6px; border-radius: 50%; background: #fff; animation: pulse 1.8s infinite; }
.badge-time { background: rgba(10,12,18,.78); color: var(--ink); backdrop-filter: blur(4px); }
.badge-ended { background: rgba(10,12,18,.78); color: var(--ink-faint); }
.badge-plat { position: absolute; top: 10px; right: 10px; z-index: 2; width: 26px; height: 26px; border-radius: 8px; background: rgba(10,12,18,.78); backdrop-filter: blur(4px); display: grid; place-items: center; }
.badge-plat img { width: 15px; height: 15px; }
.badge-plat.multi { width: auto; display: flex; padding: 0 4px; gap: 1px; }
.platform-choice { width: 24px; height: 24px; display: grid; place-items: center; border-radius: 6px; }
.platform-choice:hover, .platform-choice:focus-visible { background: rgba(255,255,255,.18); outline: none; }
.card-body { padding: 11px 13px 13px; }
.card h3 { font-size: 14px; line-height: 1.35; font-weight: 700; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-height: 2.7em; }
.card .meta { margin-top: 7px; font-size: 12.5px; color: var(--ink-dim); display: flex; align-items: center; gap: 7px; min-width: 0; }
.card .meta .ch { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card .meta .cat { flex: 0 0 auto; color: var(--sky); font-weight: 600; }
.card .meta .dot { flex: 0 0 auto; opacity: .4; }
.rail .card h3 { font-size: 15px; }

/* Phones: two-up -- the thumbnail carries a half-width card fine, the type just has to
   respect it. */
@media (max-width: 640px) {
	/* ITEM 334 -- the header and filter stack ate the space above the fold on a phone.
	   THE HEADING WRAPPED TO THREE LINES because .day-head is ONE flex row holding the
	   heading, the status span AND the day-search input. The search box does not shrink
	   below its content, so the h2 absorbed the loss and "Thursday 13 August" broke across
	   three lines. Shrinking the font would not have fixed that -- the cause is the row,
	   not the size. So the search takes its own full-width line here and the heading gets
	   the width back.

	   THE ONE-LINE FIX IS flex-wrap, AND SOMEBODY ALREADY TRIED. Line 801 sets
	   `.day-search { width: 100% }` at this same breakpoint -- the right intent, and inert,
	   because .day-head is display:flex with the default nowrap, so a 100%-width child just
	   shrinks instead of dropping to its own line. Adding flex-wrap here makes that existing
	   rule do what it was written to do; no second width rule is needed and adding one would
	   leave two places to change it. */
	.day-head { flex-wrap: wrap; gap: 8px; margin: 14px 0 10px; }
	.day-head h1, .day-head h2 { font-size: 17px; line-height: 1.2; }
	.day-head span { font-size: 12px; }
	.day-title-full { display: none; }
	.day-title-short { display: inline; }
	#day-sub { display: none; }

	/* The DATE / SPORT / MORE rows plus the sort toggles: same controls, less padding.
	   Deliberately NOT hiding any of them -- the item asks for compact and scannable
	   "without losing function", and a filter you cannot reach is not compact. */
	.chip { font-size: 12.5px; padding: 5px 11px; }
	.day-month { display: none; }
	/* Item 430: without the month, "Wed 19 5" reads as one number. The count
	   sits in its own pill so it cannot be the date. .chip.zero already dims
	   an empty day; mute the pill there so empty looks empty, not "no badge". */
	.chip.day .n {
		display: inline-flex; align-items: center; justify-content: center;
		min-width: 1.35em; padding: 1px 5px; border-radius: 999px;
		background: rgba(255, 198, 11, .16); color: var(--gold);
		font-variant-numeric: tabular-nums; line-height: 1.35;
	}
	.chip.day.on .n {
		background: rgba(255, 255, 255, .18); color: #fff;
	}
	.chip.day.zero .n {
		background: transparent; color: var(--ink-faint);
		box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .12);
	}
	.chip.day.on.zero .n {
		background: rgba(255, 255, 255, .12); color: rgba(255, 255, 255, .7);
		box-shadow: none;
	}
	.chip-row { gap: 6px; }
	.chip-row + .chip-row { margin-top: 6px; }
	.strip .shell { padding-top: 8px; padding-bottom: 8px; }
	/* Show and Date are separate rails: Show stacks above while Date keeps its single-line
	   horizontal scroll, matching Sport and More. */
	.show-label { display: block; }
	#day-row { flex-wrap: nowrap; overflow-x: auto; }

	.grid { grid-template-columns: 1fr 1fr; gap: 10px; }
	/* Live-rail cards at phone width: clearly smaller than the viewport, with the next
	   card peeking in as the scroll invitation. */
	.rail { grid-auto-columns: min(60vw, 240px); }
	.rail.compact { grid-auto-columns: min(54vw, 220px); }
	.card h3 { font-size: 12.5px; min-height: 2.75em; }
	.card .meta { font-size: 11px; gap: 5px; }
	.card .meta .cat { display: none; }
	.card-body { padding: 9px 10px 11px; }
	.badge-live, .badge-time { font-size: 10px; padding: 3px 7px; top: 7px; left: 7px; }
	.badge-plat { width: 22px; height: 22px; top: 7px; right: 7px; }
	.badge-plat img { width: 13px; height: 13px; }
	.rail { grid-auto-columns: 78vw; }
}

/* ---------- watch page ---------- */
.player { position: relative; aspect-ratio: 16/9; background: #05070b; border-radius: var(--r); overflow: hidden; border: 1px solid var(--line); box-shadow: 0 30px 80px -30px rgba(0,0,0,.8); }
.player iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.player .panel { position: absolute; inset: 0; background-size: cover; background-position: center; display: flex; align-items: flex-end; }
.player .panel .note { width: 100%; padding: 1rem; background: linear-gradient(transparent, rgba(5,8,12,.92) 60%); font-size: 14px; }
.player .panel .note .small { color: var(--ink-dim); font-size: 12.5px; margin-top: 3px; }

/* A card whose video probably will not play in the visitor's region: dimmed, never
   hidden -- the region signal is coarse and the platform is the real enforcer. */
.card.geo-warn { opacity: .55; }
.card.geo-warn .thumb::before { content: "\1F310"; position: absolute; right: 8px; bottom: 8px; z-index: 2; font-size: 14px; }

/* ---------- tv pairing ---------- */
.tv-page { text-align: center; padding-top: 7vh; padding-bottom: 40px; }
.tv-page h1 { font-size: clamp(22px, 4vw, 34px); font-weight: 800; }
#tv-code { font-size: clamp(64px, 15vw, 150px); font-weight: 800; letter-spacing: .06em; color: var(--gold); margin: 3vh 0 2vh; font-variant-numeric: tabular-nums; line-height: 1; }
.tv-hint { font-size: clamp(15px, 2.2vw, 21px); color: var(--ink-dim); line-height: 1.65; }
.tv-sub { margin-top: 3vh; color: var(--ink-faint); font-size: 13px; }
.pair-page h1 { font-size: 24px; font-weight: 800; margin-top: 14px; }
.pair-page .sub { color: var(--ink-dim); margin: 8px 0 16px; }
.pair-h2 { font-size: 15px; font-weight: 700; margin: 22px 0 10px; }
#pair-code { width: 220px; font-size: 30px; letter-spacing: .25em; text-align: center; background: var(--bg-raise); border: 1px solid var(--line-strong); border-radius: 12px; color: var(--ink); padding: 10px 6px; font-variant-numeric: tabular-nums; }
#pair-code:focus { outline: none; border-color: var(--sky); }
#pair-status { margin: 12px 0 0; color: var(--sky); font-size: 14px; min-height: 1.5em; }
#pair-options { display: flex; flex-direction: column; gap: 8px; max-width: 460px; padding-bottom: 20px; }
.pair-option { text-align: left; justify-content: flex-start; }

/* ---------- live chat beside the player ---------- */
.watch-stage { display: flex; gap: 14px; align-items: stretch; }
.watch-stage .player { flex: 1 1 auto; min-width: 0; }
#chat-panel { flex: 0 0 340px; border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; background: var(--bg-raise); }
#chat-panel iframe { width: 100%; height: 100%; border: 0; display: block; }
#chat-toggle.on { border-color: var(--sky); color: var(--sky); }
@media (max-width: 999px) {
	.watch-stage { flex-direction: column; }
	#chat-panel { flex: 0 0 auto; height: 440px; }
}

.headline { display: flex; gap: 14px; align-items: flex-start; margin: 16px 0 6px; flex-wrap: wrap; }
.headline h1 { font-size: clamp(16px, 2.4vw, 22px); font-weight: 800; line-height: 1.3; flex: 1 1 300px; }
.actions { display: flex; gap: 8px; flex: 0 0 auto; }
.btn {
	display: inline-flex; align-items: center; gap: 7px; border-radius: 999px;
	font-size: 13.5px; font-weight: 700; padding: 9px 16px; cursor: pointer;
	border: 1px solid var(--line-strong); background: var(--bg-raise); color: var(--ink);
	transition: all .13s;
}
.btn:hover { border-color: var(--sky); color: var(--sky); }
.btn.primary { background: var(--brand); border-color: var(--brand); color: #fff; }
.btn.primary:hover { filter: brightness(1.08); color: #fff; }

.meta-row { display: flex; align-items: center; gap: 10px; color: var(--ink-dim); font-size: 13.5px; flex-wrap: wrap; margin-bottom: 26px; }
.tag { background: var(--bg-raise); border: 1px solid var(--line); border-radius: 7px; padding: 3px 9px; font-size: 12px; font-weight: 700; }
.tag.live { background: var(--brand); border-color: var(--brand); color: #fff; display: inline-flex; align-items: center; gap: 5px; }
.tag.live i { width: 6px; height: 6px; border-radius: 50%; background: #fff; animation: pulse 1.8s infinite; }
.tag.cat { color: var(--sky); }

.related h2 { font-size: 16px; font-weight: 800; margin: 4px 0 12px; }

/* ---------- multiview ---------- */
/* Now an h1 (it was a span, so /multi -- the most complex page here -- offered no
   heading navigation at all). margin:0 keeps the tools row visually identical. */
.multi-title { font-weight: 800; font-size: 15px; color: var(--ink-dim);
	letter-spacing: .04em; text-transform: uppercase; margin: 0; }
.multi-shell { padding-bottom: 20px; }
.multi-grid { display: grid; gap: 12px; position: relative; }
.multi-grid[data-count="0"] { grid-template-columns: 1fr; }
.multi-grid[data-count="1"] { grid-template-columns: 1fr; }
.multi-grid[data-count="2"] { grid-template-columns: 1fr 1fr; }
/* Three streams: the first tile is the headliner -- big on the left, the other two
   stacked beside it. Reorder buttons decide who headlines. */
.multi-grid[data-count="3"] { grid-template-columns: 2fr 1fr; grid-template-rows: 1fr 1fr; }
.multi-grid[data-count="3"] .tile[data-index="0"] { grid-row: span 2; aspect-ratio: auto; }
.multi-grid[data-count="4"] { grid-template-columns: 1fr 1fr; }
@media (max-width: 900px) {
	.multi-grid { grid-template-columns: 1fr !important; grid-template-rows: none !important; }
	.multi-grid .tile { grid-row: auto !important; aspect-ratio: 16/9 !important; }
}

.tile { position: relative; aspect-ratio: 16/9; background: #05070b; border: 2px solid var(--line); border-radius: var(--r); overflow: hidden; transition: border-color .2s, box-shadow .2s; }
.tile.audio { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft), 0 14px 40px -18px rgba(214,64,16,.5); }
.tile-player, .tile-player iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.tile-note { position: absolute; inset: 0; display: grid; place-items: center; padding: 1rem; color: var(--ink-dim); font-size: 13px; text-align: center; }
.tile-chrome {
	position: absolute; inset: auto 0 0 0; z-index: 3; display: flex; align-items: center; gap: 8px;
	padding: 8px 10px; background: linear-gradient(transparent, rgba(5,8,12,.85));
	opacity: 0; transition: opacity .15s;
}
.tile:hover .tile-chrome, .tile:focus-within .tile-chrome { opacity: 1; }
@media (hover: none) { .tile-chrome { opacity: 1; } }
.tile-name { flex: 1; font-size: 12.5px; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tile-buttons { display: flex; gap: 6px; }
.tile-btn {
	width: 30px; height: 30px; border-radius: 8px; border: 1px solid rgba(255,255,255,.2);
	background: rgba(10,12,18,.7); color: var(--ink); font-size: 13px; cursor: pointer;
	display: grid; place-items: center; transition: all .13s;
}
.tile-btn:hover { border-color: var(--sky); color: var(--sky); }
.tile.audio .audio-btn { background: var(--brand); border-color: var(--brand); color: #fff; }
.tile-audio-hint { position: absolute; top: 10px; left: 10px; z-index: 3; font-size: 11px; font-weight: 700; background: rgba(10,12,18,.78); padding: 4px 9px; border-radius: 7px; color: var(--gold); }

/* The audio layer (Richard's spec): rolling over a MUTED tile tints the whole tile with a
   semi-transparent brand-dark wash and raises a large transparent OUTLINE speaker -- so
   there is never any doubt which stream you are over -- and one click anywhere on the
   wash moves the sound there. The tile that HAS audio gets no layer: its player controls
   (including YouTube's centre play) stay fully reachable, and rolling over it changes
   nothing because there is nothing to switch. */
.tile-audio-layer {
	position: absolute; inset: 0; z-index: 3;
	display: grid; place-items: center;
	background: linear-gradient(rgba(214,64,16, .16), rgba(214,64,16, .16)), rgba(10, 12, 18, .55);
	cursor: pointer;
	opacity: 0; pointer-events: none;
	transition: opacity .16s;
}
.tile-audio-layer .spk { width: 96px; height: 96px; color: #fff; filter: drop-shadow(0 4px 18px rgba(0,0,0,.6)); scale: .9; transition: scale .16s; }
.tile:not(.audio):hover .tile-audio-layer, .tile:not(.audio) .tile-audio-layer:focus-visible { opacity: 1; pointer-events: auto; }
.tile:not(.audio):hover .tile-audio-layer .spk { scale: 1; }
.tile.audio .tile-audio-layer { display: none; }
/* Touch has no hover: muted tiles carry a small always-on speaker glyph instead of the
   full wash, and a tap on it switches ears. */
@media (hover: none) {
	.tile:not(.audio) .tile-audio-layer {
		opacity: 1; pointer-events: auto; background: none;
		place-items: end start; padding: 0 0 46px 10px;
	}
	.tile:not(.audio) .tile-audio-layer .spk { width: 34px; height: 34px; scale: 1; background: rgba(10,12,18,.7); border-radius: 8px; padding: 5px; }
}

/* Item 202: the same layer, on the homepage cards. Deliberately a copy of the rules above
   rather than a shared selector -- a card is not a tile (different aspect, different
   chrome, a link rather than a player) and merging them would drag multiview's sizing onto
   a 185px-wide phone card. Same behaviour, same shape, smaller speaker.
   Hover only tints. The sound moves on CLICK (Richard, 2026-08-19); nothing here reacts to
   the pointer beyond becoming visible. */
.card { position: relative; }
.card .thumb { position: relative; }
.card-audio-layer {
	position: absolute; inset: 0; z-index: 3;
	display: grid; place-items: center;
	background: linear-gradient(rgba(214,64,16, .16), rgba(214,64,16, .16)), rgba(10, 12, 18, .55);
	cursor: pointer;
	opacity: 0; pointer-events: none;
	transition: opacity .16s;
}
.card-audio-layer .spk { width: 52px; height: 52px; color: #fff; filter: drop-shadow(0 4px 18px rgba(0,0,0,.6)); scale: .9; transition: scale .16s; }
.card:not(.audio):hover .card-audio-layer, .card:not(.audio) .card-audio-layer:focus-visible { opacity: 1; pointer-events: auto; }
.card:not(.audio):hover .card-audio-layer .spk { scale: 1; }
/* The card that HAS the sound loses its layer, exactly as the audible tile does: its own
   link stays plainly clickable, and there is nothing left to switch to. */
.card.audio { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft), 0 14px 40px -18px rgba(214,64,16,.5); }
.card.audio .card-audio-layer { display: none; }
/* Touch has no hover: a small always-on speaker instead of the full wash, so the control
   is reachable on the phones that are most of this page's traffic. */
@media (hover: none) {
	.card:not(.audio) .card-audio-layer {
		opacity: 1; pointer-events: auto; background: none;
		place-items: end start; padding: 0 0 8px 8px;
	}
	.card:not(.audio) .card-audio-layer .spk { width: 30px; height: 30px; scale: 1; background: rgba(10,12,18,.7); border-radius: 8px; padding: 4px; }
}
/* Spoken by app.js on every switch (role=status), so a sound starting somewhere down a
   200-card page is reportable by someone who cannot see the stage. */
.audio-note { margin: 0 0 10px; font-size: 13px; font-weight: 700; color: var(--gold); }

/* main navigation (shared partial) */
.mainnav { display: flex; gap: 6px; }
.mainnav a {
	padding: 7px 15px; border-radius: 999px; font-size: 13.5px; font-weight: 700;
	color: var(--ink-dim); border: 1px solid transparent; transition: all .13s;
}
.mainnav a:hover { color: var(--ink); border-color: var(--line-strong); }
.mainnav a.on { background: var(--brand-soft); color: var(--brand); border-color: rgba(214,64,16,.35); }

/* multiview toolbar + fullscreen */
/* flex-wrap: the tools row (4 layout glyphs + Saved + Save + TV + Fullscreen) reaches
   ~650px of min-content; unwrapped it forced phones to zoom the whole page out to fit
   (QA Engineer's phone finding, 18 Jul -- innerWidth 652 on a 390px device). */
.multi-tools { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; flex-wrap: wrap; }
.multi-tools .spacer { flex: 1; }
/* Sync: the button reports what it did in words, because "did anything happen?" is the
   first question after pressing it -- the pictures move, but only by seconds. */
.sync-note { font-size: 12px; font-weight: 600; color: var(--gold); max-width: 100%; }
.sync-note-act { background: none; border: 1px solid var(--line); color: var(--sky); border-radius: 999px; font-size: 11px; font-weight: 700; padding: 2px 9px; margin-left: 7px; cursor: pointer; }
.sync-note-act:hover { border-color: var(--brand); color: #fff; }
/* Who's-ahead badges (Measure, or after a listen): top-right so they clear the
   audio hint (top-left) and the chrome (bottom). "most live" reads green. */
.tile-lag { position: absolute; top: 8px; right: 8px; z-index: 4; font-size: 11px; font-weight: 800; background: rgba(10,12,18,.88); color: var(--gold); border: 1px solid var(--line); padding: 3px 9px; border-radius: 999px; pointer-events: none; }
.tile-lag.lead { color: #7dffa0; }
.tile-lag.dim { color: var(--ink-dim); }
/* Viewer counts (>=100 and fresh only, server-guarded): inside the LIVE badge on
   cards, alongside LIVE in picker rows. */
.badge-live .vc { font-weight: 800; margin-left: 2px; opacity: .95; }
.stage-option .opt-vc { flex: none; font-size: 11px; font-weight: 800; color: var(--gold); margin-left: 4px; }
/* ★ Feature-for-me on live cards; 👎 belongs to the For-you slot only. */
.card-star { position: absolute; right: 8px; bottom: 8px; z-index: 3; width: 30px; height: 30px; border-radius: 50%; border: 1px solid var(--line); background: rgba(10,12,18,.85); color: #cfd6e4; font-size: 15px; line-height: 1; cursor: pointer; opacity: 0; transition: opacity .15s, color .15s, border-color .15s; }
.card:hover .card-star, .card-star:focus-visible { opacity: 1; }
.card-star:hover { color: var(--gold); border-color: var(--gold); }
.card-star.on { opacity: 1; color: var(--gold); border-color: var(--gold); }
@media (hover: none) { .card-star { opacity: .85; } }
.stage-notforme { background: rgba(10,12,18,.78); color: var(--ink-dim); border: 1px solid var(--line); border-radius: 999px; font-size: 12px; font-weight: 700; padding: 7px 12px; cursor: pointer; white-space: nowrap; }
.stage-notforme:hover { color: #fff; border-color: var(--brand); }
.stage-slot.star-flash { animation: wal-fresh 1.5s ease-out 1; }
/* A card that arrived by async injection announces itself once, then is ordinary. */
.card.fresh { animation: wal-fresh 1.8s ease-out 1; }
@keyframes wal-fresh {
	0% { box-shadow: 0 0 0 2px var(--gold), 0 0 22px 2px rgba(255,198,11,.35); }
	100% { box-shadow: 0 0 0 2px transparent; }
}
/* Channel suggestions under the omnibox (#10). */
/* The per-tile correction row appears once Sync has been used: hidden by default so a
   plain multiview stays uncluttered, and it sits above the tile chrome so hovering to
   nudge never fights the audio/remove buttons. */
.tile-nudge { display: none; position: absolute; left: 8px; right: 8px; bottom: 42px; z-index: 4; gap: 4px; align-items: center; justify-content: center; }
.multi-grid.sync-on .tile-nudge { display: flex; }
.nudge-btn { background: rgba(10,12,18,.86); color: var(--ink); border: 1px solid var(--line); border-radius: 999px; font-size: 11px; font-weight: 700; padding: 4px 8px; cursor: pointer; line-height: 1; }
.nudge-btn:hover { border-color: var(--brand); color: #fff; }
.nudge-val { font-size: 11px; font-weight: 800; color: var(--ink-dim); background: rgba(10,12,18,.86); border-radius: 999px; padding: 4px 8px; min-width: 34px; text-align: center; }
.nudge-val.set { color: var(--gold); }
/* Swap arrows sit IN THE GAP between two tiles, on a layer over the grid: inside a tile
   they were clipped by its overflow and, being hover-revealed, only ever appeared on the
   tile the mouse was over. They are always visible now -- they ARE the reordering
   control, so hiding them until hover hid the feature. */
.swap-layer { position: absolute; inset: 0; pointer-events: none; z-index: 6; }
.tile-swap { position: absolute; transform: translate(-50%, -50%); pointer-events: auto; width: 30px; height: 30px; border-radius: 50%; border: 1px solid var(--line); background: rgba(10,12,18,.94); color: var(--ink); font-size: 14px; line-height: 1; cursor: pointer; display: grid; place-items: center; opacity: .9; transition: opacity .15s, background .15s, border-color .15s; }
.tile-swap:hover, .tile-swap:focus-visible { opacity: 1; border-color: var(--brand); color: #fff; background: var(--brand); }
@media (hover: none) {
	/* No hover on touch: the arrows are the only way to reorder, so they stay put. */
	.tile-swap { opacity: .85; }
}
.multi-grid:fullscreen { background: var(--bg); padding: 10px; gap: 10px; overflow: auto; align-content: stretch; }
.multi-grid:fullscreen .tile { aspect-ratio: auto; height: 100%; min-height: 0; }
.multi-grid[data-count="1"]:fullscreen { grid-template-rows: 1fr; }
.multi-grid[data-count="2"]:fullscreen { grid-template-rows: 1fr; }
.multi-grid[data-count="3"]:fullscreen, .multi-grid[data-count="4"]:fullscreen { grid-template-rows: 1fr 1fr; }


/* ---------- multiview layout modes ---------- */
/* Switcher: four glyph buttons. */
.layout-switch { display: flex; gap: 4px; background: var(--bg-raise); border: 1px solid var(--line); border-radius: 999px; padding: 3px; }
.layout-switch button { border: 0; background: none; border-radius: 999px; padding: 6px 9px; cursor: pointer; display: grid; place-items: center; }
.layout-switch svg { width: 18px; height: 18px; }
.layout-switch svg rect { fill: var(--ink-faint); transition: fill .13s; }
.layout-switch button:hover svg rect { fill: var(--ink); }
.layout-switch button.on { background: var(--brand); }
.layout-switch button.on svg rect { fill: #fff; }

/* The in-video add pill: semi-transparent, appears on tile hover, never outside the
   viewing area. Sits above the audio wash and stops its click. */
.tile-add-pill {
	position: absolute; top: 10px; right: 46px; z-index: 5;
	background: rgba(10, 12, 18, .62); backdrop-filter: blur(4px);
	border: 1px solid rgba(255, 255, 255, .3); border-radius: 999px;
	color: #fff; font-size: 12px; font-weight: 700; padding: 6px 12px; cursor: pointer;
	opacity: 0; pointer-events: none; transition: opacity .15s, background .15s;
}
.tile:hover .tile-add-pill { opacity: 1; pointer-events: auto; }
.tile-add-pill:hover { background: var(--brand); border-color: var(--brand); }
@media (hover: none) { .tile-add-pill { opacity: .9; pointer-events: auto; } }
/* Adaptive is the one mode with NO visible empty positions, so its pill cannot be
   hover-only -- Richard looked for a way to add and found nothing. Faint but present,
   full strength on hover. */
.mode-adaptive .tile-add-pill { opacity: .55; pointer-events: auto; }
.mode-adaptive .tile:hover .tile-add-pill { opacity: 1; }
/* The fixed-geometry modes show their empty positions instead -- a pill would be a second
   door to the same room. And a full grid needs no door at all. */
.mode-quad .tile-add-pill, .mode-theatre .tile-add-pill, .mode-focus .tile-add-pill { display: none; }
.multi-grid[data-count="4"] .tile-add-pill { display: none !important; }

/* Which add affordance exists per mode: quad shows its fixed slots; the standalone
   empty-state box serves the other modes only when there are no tiles at all. */
.quad-slot { display: none; }
.mode-quad .quad-slot { display: grid; place-items: center; align-content: center; gap: 6px; cursor: pointer; background: var(--bg-raise); border: 2px dashed var(--line-strong); color: var(--ink-dim); font: inherit; font-weight: 700; }
.mode-quad .quad-slot:hover { border-color: var(--sky); color: var(--sky); }
.mode-quad .add-tile { display: none; }
.mode-quad.multi-grid { grid-template-columns: 1fr 1fr !important; grid-template-rows: none !important; }
.mode-quad .tile { grid-row: auto !important; aspect-ratio: 16/9 !important; }

/* Theatre and focus: tiles are positioned with inline grid-area by multi.js (pure CSS
   coordinates -- moving iframes in the DOM would restart their streams). */
.mode-theatre.multi-grid { grid-template-columns: 3fr 1fr !important; grid-auto-rows: 1fr; height: min(74vh, 820px); }
.mode-theatre .tile { aspect-ratio: auto !important; min-height: 0; grid-row: auto; }
.mode-theatre .tile.stage { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
.mode-focus.multi-grid { grid-template-columns: repeat(3, 1fr) !important; grid-template-rows: 1fr 110px !important; height: min(74vh, 820px); }
.mode-focus .tile { aspect-ratio: auto !important; min-height: 0; }
.mode-focus .tile.stage { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
.mode-theatre .tile:not(.stage), .mode-focus .tile:not(.stage) { cursor: pointer; }
.mode-theatre .add-tile, .mode-focus .add-tile { display: none; }
.mode-theatre .tile:not(.stage) .tile-chrome, .mode-focus .tile:not(.stage) .tile-chrome { display: none; }
.mode-focus .tile:not(.stage) .tile-audio-layer, .mode-theatre .tile:not(.stage) .tile-audio-layer { display: none; }
/* Promoting is the click on the whole small tile; a promote hint replaces the chrome. */
.tile .promote-hint { display: none; }
.mode-theatre .tile:not(.stage):hover .promote-hint, .mode-focus .tile:not(.stage):hover .promote-hint {
	display: block; position: absolute; inset: auto 8px 8px auto; z-index: 5;
	font-size: 10px; font-weight: 800; color: #fff; background: rgba(10,12,18,.78);
	padding: 4px 8px; border-radius: 7px; pointer-events: none;
}

/* The empty positions exist in the fixed-geometry modes too -- theatre's bench and
   focus's strip show their free seats as add targets, same look as quad's. */
.mode-theatre .quad-slot, .mode-focus .quad-slot {
	display: grid; place-items: center; align-content: center; gap: 4px; cursor: pointer;
	background: var(--bg-raise); border: 2px dashed var(--line-strong); color: var(--ink-dim);
	font: inherit; font-weight: 700; font-size: 12px;
	aspect-ratio: auto; min-height: 0;
}
.mode-theatre .quad-slot:hover, .mode-focus .quad-slot:hover { border-color: var(--sky); color: var(--sky); }
.mode-theatre .quad-slot .add-plus, .mode-focus .quad-slot .add-plus { font-size: 22px; }
.mode-theatre .quad-slot .add-sub, .mode-focus .quad-slot .add-sub { display: none; }

/* Deferred tiles (weak devices): the stream's face with a tap-to-load badge instead of a
   live player. One tap loads it -- the visitor always outranks the heuristic. */
.tile-defer { position: absolute; inset: 0; background-size: cover; background-position: center; display: grid; place-items: center; cursor: pointer; }
.tile-defer .defer-badge { text-align: center; font-size: 13px; font-weight: 800; color: #fff; background: rgba(10,12,18,.78); backdrop-filter: blur(4px); border: 1px solid rgba(255,255,255,.3); border-radius: 12px; padding: 10px 16px; line-height: 1.5; }
.tile-defer .defer-badge small { font-weight: 600; color: var(--ink-dim); font-size: 10.5px; }

/* Mobile keeps all four modes; the geometry just tightens. Adaptive stacks, quad stays
   two-up, theatre renders as focus (multi.js), focus keeps its strip a little shorter.
   multi.js re-applies the inline areas on resize. */
@media (max-width: 900px) {
	.mode-adaptive.multi-grid { grid-template-columns: 1fr !important; grid-template-rows: none !important; height: auto; }
	.mode-adaptive .tile { grid-row: auto !important; aspect-ratio: 16/9 !important; }
	.mode-quad.multi-grid { grid-template-columns: 1fr 1fr !important; height: auto; }
	.mode-focus.multi-grid { grid-template-rows: 1fr 84px !important; height: min(64vh, 560px); }
}

/* saved multiviews menu */
.saved-menu { position: relative; }
.saved-menu summary { list-style: none; cursor: pointer; }
.saved-menu summary::-webkit-details-marker { display: none; }
.saved-panel { position: absolute; right: 0; top: calc(100% + 6px); z-index: 60; min-width: 240px; background: var(--bg-raise); border: 1px solid var(--line-strong); border-radius: 12px; padding: 8px; display: flex; flex-direction: column; gap: 4px; box-shadow: 0 18px 50px -18px rgba(0,0,0,.8); }
.saved-row { display: flex; align-items: center; gap: 8px; }
.saved-row a { flex: 1; padding: 7px 10px; border-radius: 8px; font-size: 13px; font-weight: 600; }
.saved-row a:hover { background: rgba(255,255,255,.06); color: var(--sky); }
.saved-row button { width: 24px; height: 24px; border-radius: 6px; border: 0; background: none; color: var(--ink-faint); cursor: pointer; }
.saved-row button:hover { color: var(--brand); }
.saved-empty { padding: 8px 10px; color: var(--ink-faint); font-size: 12.5px; }

/* pickable rows: greyed once already in the multiview */
.pick.added { opacity: .4; pointer-events: none; }

.add-tile {
	display: grid; place-items: center; align-content: center; gap: 6px; cursor: pointer;
	background: var(--bg-raise); border: 2px dashed var(--line-strong); color: var(--ink-dim);
	font: inherit; font-weight: 700;
}
.add-tile:hover { border-color: var(--sky); color: var(--sky); }
.add-plus { font-size: 34px; font-weight: 800; line-height: 1; }
.add-sub { font-size: 11.5px; font-weight: 500; color: var(--ink-faint); }
.multi-empty-note { color: var(--ink-dim); font-size: 14px; max-width: 560px; margin: 14px 0 30px; }
.hint-note { font-size: 12px; }

/* ---------- the picker ---------- */
.picker { position: fixed; inset: 0; z-index: 90; background: rgba(5,7,11,.7); backdrop-filter: blur(4px); display: flex; justify-content: flex-end; }
.picker-panel { width: min(430px, 96vw); background: var(--bg-raise); border-left: 1px solid var(--line-strong); display: flex; flex-direction: column; padding: 16px; gap: 12px; }
.picker-head { display: flex; align-items: center; justify-content: space-between; }
.picker input { background: var(--bg); border: 1px solid var(--line); border-radius: 10px; color: var(--ink); padding: 10px 13px; font-size: 13.5px; outline: none; }
.picker input:focus { border-color: var(--sky); }
.picker-plats { display: flex; gap: 7px; flex-wrap: wrap; }
.picker-list { overflow-y: auto; display: flex; flex-direction: column; gap: 8px; }
.pick {
	position: relative; display: flex; gap: 10px; align-items: center; text-align: left;
	background: var(--bg-card); border: 1px solid var(--line); border-radius: 10px;
	padding: 8px; cursor: pointer; font: inherit; color: var(--ink); transition: border-color .13s;
}
.pick:hover { border-color: var(--sky); }
.pick-thumb { flex: 0 0 92px; aspect-ratio: 16/9; border-radius: 7px; background-size: cover; background-position: center; }
.pick-meta { min-width: 0; flex: 1; }
.pick-title { display: block; font-size: 12.5px; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pick-ch { display: block; font-size: 11.5px; color: var(--ink-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pick .badge-live { position: static; font-size: 9px; padding: 3px 6px; }
.pick .badge-up { flex: 0 0 auto; font-size: 10px; font-weight: 700; color: var(--sky); background: rgba(41,171,226,.12); border: 1px solid rgba(41,171,226,.3); border-radius: 7px; padding: 3px 7px; }

/* ---------- the tray ---------- */
#multi-tray {
	position: fixed; left: 0; right: 0; bottom: 0; z-index: 80;
	display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
	background: rgba(13,15,21,.94); backdrop-filter: blur(10px);
	border-top: 1px solid var(--line-strong); padding: 10px var(--pad);
}
.tray-label { font-size: 10px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-faint); }
.tray-chip {
	display: inline-flex; align-items: center; gap: 7px; background: var(--bg-card);
	border: 1px solid var(--line); border-radius: 999px; padding: 5px 6px 5px 12px;
	font-size: 12.5px; font-weight: 600;
}
.tray-chip button { width: 20px; height: 20px; border-radius: 50%; border: 0; background: rgba(255,255,255,.1); color: var(--ink-dim); font-size: 10px; cursor: pointer; }
.tray-chip button:hover { background: var(--brand); color: #fff; }
.tray-spacer { flex: 1; }
.tray-clear { border: 0; background: none; color: var(--ink-faint); font: inherit; font-size: 12.5px; cursor: pointer; }
.tray-clear:hover { color: var(--ink); }

/* ---------- per-card "+" ---------- */
.card-add {
	position: absolute; bottom: 10px; right: 10px; z-index: 3;
	width: 28px; height: 28px; border-radius: 8px; border: 1px solid rgba(255,255,255,.25);
	background: rgba(10,12,18,.78); backdrop-filter: blur(4px); color: var(--ink);
	font-size: 17px; font-weight: 700; line-height: 1; cursor: pointer;
	opacity: 0; transition: all .15s; display: grid; place-items: center;
}
.card:hover .card-add { opacity: 1; }
@media (hover: none) { .card-add { opacity: 1; } }
.card-add:hover { background: var(--brand); border-color: var(--brand); }

/* ---------- events ---------- */
.event-chip {
	margin-top: 8px; display: inline-flex; align-items: center; gap: 5px;
	font-size: 11.5px; font-weight: 700; color: var(--gold);
	background: rgba(255, 198, 11, .08); border: 1px solid rgba(255, 198, 11, .25);
	border-radius: 7px; padding: 3px 8px; max-width: 100%;
	overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer;
}
.event-chip:hover { background: rgba(255, 198, 11, .18); }
.event-header { grid-column: 1 / -1; padding: 14px 2px 2px; font-size: 15px; font-weight: 800; }
.event-header a { color: var(--ink); }
.event-header a:hover { color: var(--sky); }
.event-header .n { font-size: 11.5px; color: var(--gold); font-weight: 700; margin-left: 6px; }
/* Inferred kick-off, in the visitor's local clock. */
.event-header .ko { font-size: 11.5px; color: var(--sky); font-weight: 700; margin-left: 6px; }
.feat-ko { color: var(--sky); font-size: 13px; font-weight: 700; }
.event-header.more { color: var(--ink-faint); font-size: 13px; border-top: 1px solid var(--line); margin-top: 10px; padding-top: 16px; }
.view-toggles { margin-left: auto; display: flex; gap: 6px; }
.event-head { margin: 18px 0 20px; }
.event-head h1 { font-size: clamp(20px, 3vw, 28px); font-weight: 800; }
.event-head .sub { color: var(--ink-dim); margin: 6px 0 14px; }
.event-head .live-note { color: var(--brand); font-weight: 700; }
.event-head .pulse { display: inline-block; vertical-align: middle; }
.event-grid { margin-top: 6px; }

/* ---------- odds and ends ---------- */
.empty { text-align: center; color: var(--ink-faint); padding: 60px 0; }
.empty b { color: var(--ink-dim); }
footer.site { border-top: 1px solid var(--line); color: var(--ink-faint); font-size: 12.5px; padding: 18px 0 30px; display: flex; gap: 16px; flex-wrap: wrap; align-items: center; }
/* Item 144: the trading-disclosure line wraps onto its own full-width row, quieter than
   the links above it. */
footer.site .co-line { flex-basis: 100%; font-size: 11.5px; color: var(--ink-faint); }
footer.site a { color: var(--ink-dim); }
footer.site a:hover { color: var(--sky); }
footer.site .spacer { flex: 1; }

/* Skip link. Visually gone until focused, which is the point: sighted users never see it,
   keyboard users get it first. Without it the homepage offers no way past two video
   iframes and several hundred card tab stops. */
.skip-link {
	position: absolute; left: 8px; top: -60px; z-index: 100;
	background: var(--bg-card); color: var(--ink); border: 2px solid var(--brand);
	padding: 10px 16px; border-radius: 8px; font-weight: 700; text-decoration: none;
	transition: top .15s ease;
}
.skip-link:focus { top: 8px; outline: 2px solid var(--brand); outline-offset: 2px; }

/* Contact submissions table (token-gated admin view). Deliberately plain: it is an
   operational page, not part of the product's visual system. */
.contact-table { width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 14px; }
.contact-table th, .contact-table td {
	text-align: left; vertical-align: top; padding: 10px 12px;
	border-bottom: 1px solid var(--bg-card);
}
.contact-table th { color: var(--ink-dim); font-weight: 700; white-space: nowrap; }
.contact-table td { color: var(--ink); }
.contact-table td:last-child { white-space: pre-wrap; word-break: break-word; min-width: 280px; }

/* The standing positioning line. Deliberately quiet: it is here to say what the site is to
   a first-time visitor and a crawler, not to push the streams down the page -- the stage is
   still what a returning visitor came for, and it stays above the fold on a laptop. */
.pitch { margin: 18px 0 4px; }
.pitch h1 {
	font-size: clamp(22px, 3.4vw, 34px); font-weight: 800; letter-spacing: -.02em;
	margin: 0 0 6px; color: var(--ink);
}
.pitch p {
	margin: 0; max-width: 62ch; color: var(--ink-dim);
	font-size: clamp(14px, 1.6vw, 16px); line-height: 1.5;
}
@media (max-width: 640px) {
	.pitch { margin: 12px 0 2px; }
	/* On a phone the stage is the whole first screen, so the subcopy earns its space less
	   than the streams do. The h1 stays -- it is the page's heading, not decoration. */
	.pitch p { display: none; }
}

/* Visually hidden, still announced. The filter input had no label at all -- its name came
   from the placeholder, which vanishes the moment anyone types. */
.sr-only {
	position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
	overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* The filter sits on the day-head row, and wraps under it on a phone rather than
   squeezing the heading. */
.day-search { margin-left: auto; align-self: center; min-width: 220px; max-width: 340px; }
@media (max-width: 640px) { .day-search { margin: 8px 0 0; width: 100%; } }

/* Header typeahead (item 177). .search is already position:relative, so the list hangs
   off the box it belongs to rather than off the page. */
.omni-list {
	position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 60;
	margin: 0; padding: 4px; list-style: none;
	background: var(--panel, #10151f); border: 1px solid rgba(255,255,255,.12);
	border-radius: 10px; box-shadow: 0 18px 40px rgba(0,0,0,.45);
	max-height: 60vh; overflow-y: auto;
}
.omni-opt {
	display: flex; align-items: baseline; gap: 8px;
	padding: 8px 10px; border-radius: 7px; cursor: pointer;
}
/* .on is set by keyboard AND by hover, so arrow keys and the mouse cannot disagree
   about which row is about to be chosen. */
.omni-opt.on { background: rgba(41,171,226,.16); }
.omni-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.omni-sub { color: var(--ink-faint, #97a0b2); font-size: 12px; flex: none; }
/* Events gather every platform's coverage of one occasion -- the strongest thing the
   box can offer, so it is the one that gets a marker. */
.omni-opt.kind-event .omni-label::before { content: '\2726\00a0'; color: var(--sky, #29abe2); }

/* ---------- payload diet: skip rendering the cards nobody is looking at (item 30) ----
   QA's perf layer measured the homepage at DOMContentLoaded 12.9s desktop / 7.5s phone
   against a budget of 8s. Two of that item's three levers already landed: thumbnails are
   painted lazily from data-bg as a card nears the viewport, and grid thumbs are rewritten
   to mqdefault (320x180) instead of the stored sddefault. What was left is the DOM
   itself -- ~1,600 cards, every one laid out and painted at load even though a viewport
   holds about a dozen.

   content-visibility: auto tells the browser to skip layout and paint for cards that are
   off screen, and to do the work as they approach. The cards stay in the DOM, which is
   not a compromise but the requirement: the day/category/platform chip counts, the taste
   ordering, the stage picker and the filter all read every card's dataset, and those
   reads touch no layout, so they keep working on cards that have never been painted.

   contain-intrinsic-size gives the browser a size to reserve before it has rendered one,
   so the scrollbar does not jump as you scroll. `auto` means "remember the real size once
   measured and use it from then on" -- the 260px is only the first guess (a 250px column
   gives a 141px 16:9 thumb plus ~120px of body).

   SCOPED TO .grid > .card DELIBERATELY. #live-rail is a horizontal scroller whose step is
   computed from its first card's measured width; containment on those would make that
   read 0 and break the arrows. */
@supports (content-visibility: auto) {
	.grid > .card { content-visibility: auto; contain-intrinsic-size: auto 260px; }
	/* Printing and find-in-page both need the skipped content back. Chrome already
	   handles find-in-page for content-visibility: auto; print is on us. */
	@media print { .grid > .card { content-visibility: visible; } }
}

/* The card placeholder, which every card used to carry as an identical inline style.
   YouTube is the default because anything that is not twitch or kick used it already --
   the template's own ternary said so. v2.js overwrites this inline as the card nears the
   viewport, and inline wins, so the lazy paint is unaffected. */
.card .thumb { background-image: url('/assets/img/card-fallback-youtube.svg'); }
.card[data-plat="twitch"] .thumb { background-image: url('/assets/img/card-fallback-twitch.svg'); }
.card[data-plat="kick"] .thumb { background-image: url('/assets/img/card-fallback-kick.svg'); }

/* Hover-to-preview (item 36). Fills the thumbnail exactly, so the card does not resize
   and the grid never reflows when a preview appears or goes away -- the jank this
   feature would otherwise be notorious for. */
.card-preview { position: absolute; inset: 0; z-index: 1; background: #000; }
.card-preview iframe { width: 100%; height: 100%; border: 0; display: block; }
/* The badges stay on top of the player: LIVE and the platform mark are how you tell what
   you are looking at, and they are more useful over moving video, not less. */
.thumb .badge-live, .thumb .badge-time, .thumb .badge-plat { z-index: 2; }
/* The gradient scrim is for text over a still. Over a playing preview it just dims the
   video, so it goes while one is up. */
.thumb:has(.card-preview)::after { opacity: 0; }
/* Item 202: multiview's big rollover audio icon, grid-sized. A button laid over the
   whole preview -- the target is the tile, not an icon-sized square -- revealed on
   hover so the video stays clean. The pinned (audible) card keeps its icon faintly
   visible as the "this one has the sound" marker even when not hovered. */
.card-audio-layer { position: absolute; inset: 0; z-index: 3; display: flex;
	align-items: center; justify-content: center;
	background: transparent; border: 0; padding: 0; cursor: pointer; }
.card-audio-layer .ico { font-size: 2.2rem; line-height: 1; opacity: 0;
	text-shadow: 0 1px 8px rgba(0, 0, 0, .75); transition: opacity .15s;
	pointer-events: none; }
.card-preview:hover .card-audio-layer .ico,
.card-audio-layer:focus-visible .ico { opacity: .92; }
.card-preview.audible .card-audio-layer .ico { opacity: .55; }
.card-preview.audible:hover .card-audio-layer .ico { opacity: .95; }


/* ---------- the strip's rows are spaced by their CONTAINER (Richard, 2026-08-20) ----------
   "Sport filter 1-click row touches Date above it now - the gap has gone".

   The rows used to be spaced by `.chip-row + .chip-row`, which only works while every row
   is a sibling of the next. Item 420 wrapped Show and Date in .day-filter-rows to stop the
   Date row wrapping onto three lines on mobile -- correct in itself, but it made the Sport
   row's previous sibling a DIV rather than a .chip-row, so the adjacent-sibling rule stopped
   matching it and the gap above Sport vanished. The rule was still there and still looked
   right; it just no longer applied to anything.

   A gap owned by the parent cannot be switched off by re-nesting a child, so wrapping the
   next row will not silently cost anyone their spacing again. The direct-child margin is
   zeroed so rows that DO still match the sibling rule are not spaced twice.
   tests/test_strip_rows_are_spaced.py holds this. */
.strip .shell { display: flex; flex-direction: column; gap: 6px; }
.strip .shell > .chip-row + .chip-row { margin-top: 0; }
@media(hover: none) { .strip .shell { gap: 8px; } }
