/* ===========================================================================
   The commercial break, and the sponsors' furniture.
   ---------------------------------------------------------------------------
   Kept out of app.css deliberately. Everything here belongs to one feature and
   is loaded on every page only because the banner slots are — splitting it out
   means the break player's several hundred lines can be read, changed and
   deleted as one thing.

   Two rules run through all of it:

   1. **A commercial is drawn in its own brand's colours, not the site's.**
      Nothing here inherits --panel, --ink or --accent inside a scene. A break
      that looked like the rest of the site would read as part of the site; it
      has to read as somebody else's television.

   2. **It is meant to look cheap.** Flat colour, hard type, no gradients, no
      shadows inside a scene, transitions of a single frame. A beautifully
      rendered advert for a second-hand bucket shop is a joke that has stopped
      working.
   =========================================================================== */

/* ---- the break player ---------------------------------------------------- */

/* ---------------------------------------------------------------------------
   The mount IS the break. There is no wrapper element.

   There used to be an `.adbreak` div positioned to fill the mount, with
   everything else inside it, and four separate attempts were made to give it a
   size: `inset: 0`, the four longhands, `width/height: 100%`, and finally
   explicit pixels measured off the mount. The live readout came back identical
   every time —

       mount .adpreview__stage 1262×710    correct at every attempt
       break .adbreak            0×0       including when told it was 1262px

   — and an element carrying an explicit pixel width cannot measure zero unless
   it is `display: none` or detached, which it demonstrably was not.

   So the wrapper is gone rather than fixed. The mount is the container, the
   scene is its direct child, and the element doing the containing is the one
   that measured correctly at every single attempt. `.adbreak` is kept below
   only as the thing the stylesheet probe in ads.js tests against.
   --------------------------------------------------------------------------- */

[data-break-mount] {
  position: relative;
  /* The junction sizes its type in `cqw` — a share of *this box* — rather than
     `vw`, a share of the window. On the standalone preview the two are nearly
     the same because the mount is most of the width; inside the television the
     panel is about half the desktop, so `vw` sized every headline and list for
     a screen twice the width of the box it was in and the top and bottom were
     clipped. Making the mount a query container is what lets `cqw` mean "of the
     panel". `cqw` falls back to the viewport where there is no container, so
     nothing outside a junction is affected. */
  container-type: inline-size;
}
[data-break-mount].has-break {
  /* **No `min-height`.** There was one, of 240px, from when a mount could be
     handed no size at all and would otherwise collapse. Every child of a
     running junction is absolutely positioned, so the mount has no in-flow
     content and that floor became its *actual* height the moment anything
     stopped giving it a real one — and the 3D renderer, which measures this box
     to size its drawing buffer, built a canvas 240 pixels tall inside a panel
     more than twice that. A commercial in the top half of the frame and black
     underneath, which is exactly how it was reported.
     A floor that is only ever reached when something upstream is wrong is not a
     safety net. It is a wrong answer with the failure hidden. */
  overflow: hidden;
  background: #000;
  font-family: "Helvetica Neue", Arial, var(--sans, sans-serif);
}

/* Not used for layout any more. `cssProbe` in ads.js creates a bare element
   with this class and reads its computed position back to find out whether this
   stylesheet reached the server at all, so the rule has to stay and has to keep
   saying `absolute`. */
.adbreak { position: absolute; inset: 0; }

.adbreak__scene { position: absolute; inset: 0; width: 100%; height: 100%; }

/* ---- the built set ------------------------------------------------------- */

/* The WebGL canvas a staged scene is drawn into. It sits under the type and
   under the lower third, and it is never removed between scenes — only hidden
   — because throwing away a graphics context per cut is how a browser is
   persuaded to stop giving you contexts at all. */
.adstage { position: absolute; inset: 0; width: 100%; height: 100%; }
.adstage__canvas { display: block; width: 100%; height: 100%; }

/* A vignette and a very slight scan of noise over the picture.

   Both are doing the same job: the geometry is untextured flat colour, and
   without something on top of it the image reads as a diagram rather than as a
   camera looking at something. This is the cheapest possible film grade and it
   is worth more than any amount of extra polygons. */
.adstage::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse at 50% 45%, rgba(0,0,0,0) 42%, rgba(0,0,0,.42) 100%);
}

/* ---- type over a built set ---------------------------------------------- */

/* Not the card's headline. Over a picture, type has to behave like a broadcast
   caption — cornered, backed off the image so it survives a bright sky, and
   much smaller — or it is a card with a photograph behind it. */
.adover {
  position: absolute;
  left: 0;
  bottom: 0;
  max-width: min(74%, 640px);
  padding: clamp(10px, 2cqw, 22px) clamp(12px, 2.6cqw, 30px) clamp(30px, 6cqw, 62px);
  color: #fff;
  text-shadow: 0 2px 14px rgba(0, 0, 0, .85), 0 0 3px rgba(0, 0, 0, .6);
  pointer-events: none;
  animation: ad-over-in .28s ease-out both;
}
@keyframes ad-over-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
.adover__kicker {
  font-size: clamp(8px, 1.15cqw, 12px);
  font-weight: 800;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--c, #ff7a18);
  margin-bottom: .45em;
}
.adover__big {
  font-size: clamp(17px, 3.4cqw, 42px);
  font-weight: 900;
  line-height: 1.02;
  text-transform: uppercase;
  overflow-wrap: break-word;
}
.adover__sub {
  margin-top: .3em;
  font-size: clamp(11px, 1.6cqw, 18px);
  font-weight: 600;
  opacity: .92;
}

/* ---- a scene ------------------------------------------------------------- */

.adscene {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(12px, 3cqw, 40px);
  padding: clamp(16px, 4cqw, 54px);
  text-align: left;
  /* One frame of fade, and no movement. A cut between two adverts should feel
     like a cut. */
  animation: ad-cut .09s linear;
}

@keyframes ad-cut { from { opacity: .25; } to { opacity: 1; } }

.adscene__art {
  flex: 0 0 auto;
  width: clamp(64px, 16cqw, 190px);
  height: clamp(64px, 16cqw, 190px);
  opacity: .92;
}
.adscene__art svg { display: block; width: 100%; height: 100%; }

.adscene__type { flex: 1 1 auto; min-width: 0; }

.adscene__kicker {
  font-size: clamp(9px, 1.3cqw, 13px);
  font-weight: 700;
  letter-spacing: .3em;
  text-transform: uppercase;
  opacity: .72;
  margin-bottom: .5em;
}

.adscene__big {
  font-size: clamp(22px, 6cqw, 68px);
  font-weight: 900;
  line-height: .96;
  letter-spacing: -.01em;
  text-transform: uppercase;
  /* Long headlines are common — several of these brands write in sentences —
     and a headline that overflows the picture is worse than a small one. */
  overflow-wrap: break-word;
}

.adscene__sub {
  margin-top: .55em;
  font-size: clamp(12px, 2cqw, 22px);
  font-weight: 500;
  line-height: 1.25;
  opacity: .86;
}

/* -- layouts --------------------------------------------------------------- */

.adscene--full,
.adscene--title,
.adscene--product,
.adscene--bumper,
.adscene--ident,
.adscene--trail { flex-direction: column; text-align: center; }

.adscene--full .adscene__big { font-size: clamp(26px, 8cqw, 92px); }

.adscene--product .adscene__art { width: clamp(96px, 26cqw, 300px); height: clamp(96px, 26cqw, 300px); }

.adscene--split { flex-direction: column; align-items: stretch; justify-content: center; }
.adscene--split .adscene__big { font-size: clamp(20px, 5cqw, 56px); }

/* Nothing at all. A held black is a real edit and several of the seeded spots
   are built on one, so it must genuinely be empty — no logo, no bug lift, no
   helpful "advertisement" caption. */
.adscene--black { background: #000 !important; }

/* One frame of the brand at full strength. Used a scene at a time. */
.adscene.is-flash { animation: ad-flash .18s steps(2) 1; }
@keyframes ad-flash { 0% { filter: brightness(2.2); } 100% { filter: none; } }

/* -- the endcard ----------------------------------------------------------- */

.adscene.is-endcard { flex-direction: column; text-align: center; }
.adscene__logo {
  display: block;
  width: min(88%, 900px);
  height: auto;
  margin: 0 auto clamp(8px, 2cqw, 20px);
}
.adscene.is-endcard .adscene__big { font-size: clamp(18px, 4cqw, 44px); }
.adscene.is-endcard .adscene__sub { font-weight: 600; opacity: .9; }

/* -- the network's own furniture ------------------------------------------- */

/* ---- the network ident --------------------------------------------------

   On screen more often than anything else in a junction, so it is the one
   piece of furniture worth animating properly.

   ---------------------------------------------------------------------------
   It is choreographed across its WHOLE length, not just the front
   ---------------------------------------------------------------------------

   The first version had a one-and-a-half second build inside a card that is on
   screen for **seven point three seconds**. So it moved briefly and then held a
   static logo for six seconds — and what a viewer remembers of that is the six
   seconds. It read as having no animation at all, which is exactly what it was
   reported as.

   The duration is now handed in from the player as `--dur`, because only the
   server knows it: the ident slot sizes itself to whatever sonic logo is on
   disk. Everything below is timed against that, in three movements:

       0        →  1.1s        the arrival. Violent, oversized, off-frame.
       1.1s     →  dur-1.0s    the hold, which never actually stops moving:
                               a slow push, a parallax on the plate, and a
                               light sweeping the mark every few seconds.
       dur-1.0s →  dur         the exit. A wipe takes it off rather than a cut.

   A channel ident is a piece of film, not a logo with a transition on the
   front. This is that difference.
   -------------------------------------------------------------------------- */

.adident {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: #05070c;
  color: #e9eef7;
  overflow: hidden;
  perspective: 1400px;
  /* Fallback if the player did not pass one; every real card sets it. */
  --dur: 5200ms;
}

/* ---- the background ------------------------------------------------------
   A full-frame diagonal wedge in the network colour, not a thin blade. It
   crosses the whole picture, holds behind the mark as a field, and leaves at
   the end — so the *background* is moving for the entire ident and the frame is
   never a flat dark rectangle with something small in the middle of it.
   ------------------------------------------------------------------------- */

.adident__wipe {
  position: absolute;
  top: -30%;
  bottom: -30%;
  left: -140%;
  width: 190%;
  background:
    linear-gradient(100deg,
      transparent 0%,
      var(--c, #ff7a18) 12%,
      color-mix(in srgb, var(--c, #ff7a18) 62%, #ffffff) 46%,
      var(--c, #ff7a18) 78%,
      color-mix(in srgb, var(--c, #ff7a18) 55%, #000000) 100%);
  transform: skewX(-14deg);
  pointer-events: none;
  z-index: 0;
  animation:
    ad-wipe-in  calc(var(--dur) * .22) cubic-bezier(.32, 0, .1, 1) both,
    ad-wipe-out calc(var(--dur) * .13) cubic-bezier(.7, 0, .2, 1) both;
  animation-delay: 0ms, calc(var(--dur) * .87);
}
@keyframes ad-wipe-in  { from { left: -190%; } to { left: -34%; } }
@keyframes ad-wipe-out { from { left: -34%; }  to { left: 150%; } }

/* A second, thinner blade crossing the other way a beat later. Two things
   moving in opposite directions is what makes a wipe read as designed rather
   than as a slide. */
.adident::before {
  content: "";
  position: absolute;
  top: -30%;
  bottom: -30%;
  width: 26%;
  left: 150%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.32), transparent);
  transform: skewX(-14deg);
  mix-blend-mode: screen;
  pointer-events: none;
  z-index: 2;
  animation: ad-wipe-cross calc(var(--dur) * .15) cubic-bezier(.3, 0, .2, 1) calc(var(--dur) * .30) both;
}
@keyframes ad-wipe-cross { from { left: 130%; } to { left: -60%; } }

/* ---- the mark ------------------------------------------------------------
   Two nested animations: the *entrance*, which differs per treatment, and a
   slow continuous push that runs for the whole card so the hold is never still.
   ------------------------------------------------------------------------- */

.adident__mark {
  position: relative;
  z-index: 1;
  transform-style: preserve-3d;
  animation: ad-mark-hold var(--dur) linear both;
}
@keyframes ad-mark-hold {
  0%   { transform: scale(1.00) translateY(0); }
  100% { transform: scale(1.07) translateY(-10px); }
}

/* The entrance sits on the plate inside it, so the two transforms compose
   instead of fighting over the same property. */
.adident__plate {
  padding: clamp(16px, 3.4cqw, 40px) clamp(24px, 5cqw, 60px);
  background: var(--c, #ff7a18);
  border-radius: clamp(6px, 1cqw, 14px);
  box-shadow: 0 30px 90px rgba(0, 0, 0, .72), 0 0 0 1px rgba(255, 255, 255, .12) inset;
  transform-style: preserve-3d;
}

/* -- drop: falls the height of the screen and lands hard ------------------- */
.adident--drop .adident__plate {
  animation: ad-in-drop calc(var(--dur) * .46) cubic-bezier(.2, 1.6, .3, 1) calc(var(--dur) * .04) both;
}
@keyframes ad-in-drop {
  0%   { opacity: 0; transform: translateY(-130vh) scale(2.4) rotate(-6deg); }
  58%  { opacity: 1; transform: translateY(4%)     scale(.93) rotate(1.5deg); }
  78%  {              transform: translateY(-1.5%) scale(1.03) rotate(-.6deg); }
  100% { opacity: 1; transform: none; }
}

/* -- flip: arrives enormous and rotates flat ------------------------------- */
.adident--flip .adident__plate {
  animation: ad-in-flip calc(var(--dur) * .50) cubic-bezier(.2, 1.05, .28, 1) calc(var(--dur) * .03) both;
}
@keyframes ad-in-flip {
  0%   { opacity: 0; transform: scale(3.4) rotateY(96deg) rotateZ(-12deg); }
  45%  { opacity: 1; }
  74%  {              transform: scale(1.08) rotateY(-14deg) rotateZ(2deg); }
  100% { opacity: 1; transform: none; }
}

/* -- slide: rockets in from off-frame against the wipe --------------------- */
.adident--slide .adident__plate {
  animation: ad-in-slide calc(var(--dur) * .42) cubic-bezier(.08, .92, .18, 1) calc(var(--dur) * .04) both;
}
@keyframes ad-in-slide {
  0%   { opacity: 0; transform: translateX(-150cqw) scale(1.5) skewX(18deg); }
  66%  { opacity: 1; transform: translateX(3.5%)   scale(1.02) skewX(-4deg); }
  100% { opacity: 1; transform: none; }
}

/* -- split: two panels part on a mark already in place --------------------- */
.adident--split .adident__plate {
  animation: ad-in-settle calc(var(--dur) * .40) cubic-bezier(.2, .9, .2, 1) calc(var(--dur) * .10) both;
}
@keyframes ad-in-settle {
  0%   { opacity: 0; transform: scale(1.24); }
  40%  { opacity: 1; }
  100% { opacity: 1; transform: none; }
}
.adident--split::after {
  content: "";
  position: absolute;
  left: -30%;
  right: -30%;
  height: 64%;
  top: -14%;
  background: var(--c, #ff7a18);
  z-index: 3;
  pointer-events: none;
  transform-origin: 100% 100%;
  animation: ad-split-top calc(var(--dur) * .36) cubic-bezier(.55, 0, .15, 1) calc(var(--dur) * .03) both;
}
.adident--split .adident__sweep {
  /* The lower panel, borrowed — the sweep is not wanted on this treatment. */
  position: absolute;
  left: -30%;
  right: -30%;
  bottom: -14%;
  top: auto;
  height: 64%;
  background: var(--c, #ff7a18);
  mix-blend-mode: normal;
  z-index: 3;
  opacity: 1;
  transform-origin: 0 0;
  animation: ad-split-bottom calc(var(--dur) * .36) cubic-bezier(.55, 0, .15, 1) calc(var(--dur) * .03) both;
}
@keyframes ad-split-top {
  from { transform: translateY(50%) rotate(-5deg); }
  to   { transform: translateY(-115%) rotate(-5deg); }
}
@keyframes ad-split-bottom {
  from { transform: translateY(-50%) rotate(-5deg); }
  to   { transform: translateY(115%) rotate(-5deg); }
}

/* -- zoom: far larger than the screen, snapping back ----------------------- */
.adident--zoom .adident__plate {
  animation: ad-in-zoom calc(var(--dur) * .44) cubic-bezier(.14, .92, .16, 1) calc(var(--dur) * .02) both;
}
@keyframes ad-in-zoom {
  0%   { opacity: 0; transform: scale(14); filter: blur(22px); }
  40%  { opacity: 1; }
  84%  { transform: scale(.955); filter: none; }
  100% { opacity: 1; transform: none; filter: none; }
}

/* The whole card takes the hit as it lands. */
.adident { animation: ad-ident-impact calc(var(--dur) * .11) ease-out calc(var(--dur) * .46) both; }
@keyframes ad-ident-impact {
  0%   { transform: none; }
  16%  { transform: translateY(9px) scale(1.018); }
  42%  { transform: translateY(-4px) scale(.995); }
  100% { transform: none; }
}

.adident__logo {
  display: block;
  width: clamp(170px, 38cqw, 520px);
  height: auto;
}
.adident__mono {
  font-size: clamp(40px, 10cqw, 110px);
  font-weight: 900;
  letter-spacing: .04em;
  color: var(--c, #ff7a18);
}

/* The sweep. Light crossing the mark — and it repeats, so a seven-second hold
   gets three passes rather than one and never settles into a photograph. */
.adident__sweep {
  position: absolute;
  inset: -14% -46%;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(105deg,
    transparent 38%,
    rgba(255, 255, 255, .55) 48%,
    rgba(255, 255, 255, .9) 51%,
    rgba(255, 255, 255, .55) 54%,
    transparent 64%);
  mix-blend-mode: screen;
  opacity: 0;
  animation: ad-ident-sweep calc(var(--dur) * .52) ease-out calc(var(--dur) * .50) infinite;
}
@keyframes ad-ident-sweep {
  0%   { opacity: 0; transform: translateX(-64%); }
  10%  { opacity: 1; }
  34%  { opacity: 1; transform: translateX(64%); }
  36%  { opacity: 0; transform: translateX(64%); }
  100% { opacity: 0; transform: translateX(64%); }
}

/* The rule draws out from the centre, and pulls back in at the end. */
.adident__rule {
  display: block;
  height: 4px;
  width: clamp(110px, 26cqw, 300px);
  margin: clamp(16px, 3cqw, 28px) 0 clamp(10px, 1.8cqw, 17px);
  background: #fff;
  transform-origin: 50% 50%;
  z-index: 1;
  animation:
    ad-rule-in  calc(var(--dur) * .17) cubic-bezier(.2, .85, .2, 1) both,
    ad-rule-out calc(var(--dur) * .10) cubic-bezier(.7, 0, .3, 1) both;
  animation-delay: calc(var(--dur) * .50), calc(var(--dur) * .88);
}
@keyframes ad-rule-in  { from { transform: scaleX(0); opacity: 0; } to { transform: scaleX(1); opacity: 1; } }
@keyframes ad-rule-out { from { transform: scaleX(1); }             to { transform: scaleX(0); } }

/* And the name spaces in behind it, then away. */
.adident__name {
  font-size: clamp(10px, 1.7cqw, 16px);
  font-weight: 800;
  letter-spacing: .44em;
  text-indent: .44em;
  z-index: 1;
  animation:
    ad-name-in  calc(var(--dur) * .19) ease-out both,
    ad-name-out calc(var(--dur) * .10) ease-in both;
  animation-delay: calc(var(--dur) * .58), calc(var(--dur) * .90);
}
@keyframes ad-name-in  { from { opacity: 0; letter-spacing: 1.1em; } to { opacity: .9; letter-spacing: .44em; } }
@keyframes ad-name-out { from { opacity: .9; } to { opacity: 0; letter-spacing: .8em; } }

.adident__line {
  margin-top: clamp(12px, 2.2cqw, 20px);
  font-size: clamp(17px, 3.6cqw, 44px);
  font-weight: 900;
  letter-spacing: .1em;
  color: #fff;
  z-index: 1;
  animation: ad-line-in calc(var(--dur) * .15) cubic-bezier(.2, 1.3, .3, 1) calc(var(--dur) * .66) both;
}
@keyframes ad-line-in {
  from { opacity: 0; transform: translateY(14px) scale(.94); }
  to   { opacity: 1; transform: none; }
}

.adscene--card {
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  background: #070b12;
  color: #e9eef7;
  padding: clamp(14px, 3cqw, 38px);
  position: absolute;   /* inherited from .adscene; explicit so ::before anchors */
  overflow: hidden;
}
.adscene--card .adscene__kicker { color: var(--c, #ff7a18); opacity: 1; }

/* The station's own graphics — venues, results, the table, what's coming up —
   are no longer a flat black card. Two soft glows in the sport/network accent
   drift behind the type on a long loop, so it reads as a lit studio wall rather
   than an animation. `color-mix` degrades to nothing where unsupported, leaving
   the card its old flat dark, and reduced-motion holds it still. */
.adscene--card::before,
.adscene--trail::before {
  content: "";
  position: absolute;
  inset: -35%;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(45% 45% at 22% 28%, color-mix(in srgb, var(--c, #ff7a18) 26%, transparent), transparent 62%),
    radial-gradient(42% 42% at 82% 74%, color-mix(in srgb, var(--c, #ff7a18) 16%, transparent), transparent 60%),
    radial-gradient(60% 60% at 50% 50%, rgba(120,150,200,.07), transparent 72%);
  animation: ad-card-aurora 22s ease-in-out infinite alternate;
}
.adscene--card > *,
.adscene--trail > * { position: relative; z-index: 1; }
@keyframes ad-card-aurora {
  from { transform: translate3d(-3%, -2%, 0) scale(1.05) rotate(-1deg); }
  to   { transform: translate3d(3%, 4%, 0) scale(1.16) rotate(1.4deg); }
}
@media (prefers-reduced-motion: reduce) {
  .adscene--card::before,
  .adscene--trail::before { animation: none; }
}

/* The sponsorship bump. Seven seconds, the business's own two colours, and its
   sonic logo if the station has one. Deliberately closer in look to a
   commercial than to the station's other graphics — it is somebody's money on
   screen, not a results page. */
.adscene--sponsor {
  flex-direction: column;
  text-align: center;
  gap: clamp(6px, 1.4cqw, 14px);
}
.adscene--sponsor .adscene__kicker { letter-spacing: .22em; opacity: .78; }
.adscene--sponsor .adscene__logo { width: min(76%, 620px); margin: 0; }
.adscene--sponsor .adscene__sub { font-weight: 700; }

.adscene--trail {
  background: #05070c;
  color: #e9eef7;
  border-bottom: 5px solid var(--c, #ff7a18);
}
.adscene--trail .adscene__kicker { color: var(--c, #ff7a18); opacity: 1; }
.adscene__trail-logo { width: clamp(44px, 9cqw, 92px); height: auto; margin-bottom: 8px; }

/* -- the lists the station fills with ------------------------------------- */

.adlist {
  list-style: none;
  margin: clamp(8px, 2cqw, 18px) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(4px, 1cqw, 9px);
  overflow: hidden;
}
.adlist__row {
  display: flex;
  align-items: center;
  gap: clamp(8px, 1.6cqw, 16px);
  padding: clamp(4px, 1cqw, 9px) clamp(6px, 1.4cqw, 14px);
  background: rgba(255, 255, 255, .04);
  border-left: 4px solid var(--c, #666);
  font-size: clamp(11px, 1.6cqw, 17px);
}
.adlist__time,
.adlist__pos {
  flex: 0 0 auto;
  font-family: var(--mono, monospace);
  font-weight: 700;
  opacity: .75;
  min-width: 2.6em;
}
.adlist__body { display: flex; flex-direction: column; min-width: 0; }
.adlist__name {
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.adlist__sub { font-size: .78em; opacity: .62; }
.adlist--table .adlist__name { flex: 1 1 auto; }
.adlist__pts { margin-left: auto; font-family: var(--mono, monospace); font-weight: 800; }
.adlist__row--empty { justify-content: center; opacity: .7; font-style: italic; border-left-color: transparent; }

/* ---- the player showcase ------------------------------------------------- */

/* One competitor and their public record, built in the team's own two colours.
   The crest is the hero; the stats count into place and the form lands pip by
   pip. All units are `cqw` — a share of the panel — so it reads the same in the
   television and the control booth's preview. */
.adplayer {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; justify-content: center;
  gap: clamp(10px, 2.4cqw, 26px);
  padding: clamp(18px, 5cqw, 60px) clamp(20px, 6cqw, 72px);
  overflow: hidden;
  color: #eef2f8;
  background:
    radial-gradient(120% 120% at 12% 0%, color-mix(in srgb, var(--c) 42%, #05070c), #05070c 70%),
    #05070c;
  animation: ad-player-in .5s cubic-bezier(.2,.8,.25,1) both;
}
/* A soft drift of the team colour behind everything, the same lit-wall idea as
   the station cards. */
.adplayer::before {
  content: ""; position: absolute; inset: -30%;
  background:
    radial-gradient(40% 40% at 24% 30%, color-mix(in srgb, var(--c) 55%, transparent), transparent 60%),
    radial-gradient(38% 38% at 82% 74%, color-mix(in srgb, var(--accent) 30%, transparent), transparent 60%);
  animation: ad-card-aurora 20s ease-in-out infinite alternate;
  z-index: 0;
}
.adplayer > * { position: relative; z-index: 1; }

.adplayer__kicker {
  font-size: clamp(9px, 1.4cqw, 14px); font-weight: 800; letter-spacing: .26em;
  text-transform: uppercase; color: color-mix(in srgb, var(--c) 40%, #ffffff);
}

.adplayer__main { display: flex; align-items: center; gap: clamp(16px, 4.5cqw, 54px); min-width: 0; }

/* The crest and number. */
.adplayer__badge {
  flex: 0 0 auto; display: flex; flex-direction: column; align-items: center;
  gap: clamp(6px, 1.4cqw, 14px);
  animation: ad-player-badge 6s ease-in-out infinite;
}
.adplayer__crest {
  width: clamp(78px, 20cqw, 168px); height: clamp(78px, 20cqw, 168px);
  filter: drop-shadow(0 8px 22px rgba(0,0,0,.5));
}
.adplayer__crest svg { width: 100%; height: 100%; display: block; }
.adplayer__num {
  font-family: var(--mono, monospace); font-weight: 800;
  font-size: clamp(11px, 1.8cqw, 18px); letter-spacing: .04em;
  padding: clamp(2px,.5cqw,4px) clamp(8px,1.6cqw,14px); border-radius: 999px;
  background: rgba(0,0,0,.35); color: #fff;
  border: 1px solid color-mix(in srgb, var(--c) 60%, transparent);
}

.adplayer__info { flex: 1 1 auto; min-width: 0; }
.adplayer__name {
  font-size: clamp(24px, 7cqw, 74px); font-weight: 900; line-height: .98;
  letter-spacing: -.01em; text-transform: uppercase; overflow-wrap: break-word;
  animation: ad-player-line .5s ease .06s both;
}
.adplayer__team {
  margin-top: .5em; font-size: clamp(12px, 2.1cqw, 22px); font-weight: 600;
  color: #c6ceda; animation: ad-player-line .5s ease .12s both;
}

.adplayer__stats {
  display: flex; flex-wrap: wrap; gap: clamp(8px, 1.8cqw, 18px);
  margin-top: clamp(12px, 2.6cqw, 26px);
}
.adplayer__stat {
  flex: 1 1 auto; min-width: clamp(60px, 12cqw, 96px);
  display: flex; flex-direction: column; gap: 2px;
  padding: clamp(8px,1.6cqw,15px) clamp(10px,2cqw,18px);
  background: rgba(255,255,255,.06); border-radius: 10px;
  border-top: 3px solid color-mix(in srgb, var(--c) 70%, transparent);
  animation: ad-player-pop .42s cubic-bezier(.2,1.2,.35,1) both;
  animation-delay: calc(.18s + var(--i) * .08s);
}
.adplayer__stat-v {
  font-size: clamp(18px, 3.4cqw, 34px); font-weight: 900; line-height: 1;
  font-family: var(--mono, monospace);
}
.adplayer__stat-l {
  font-size: clamp(8px, 1.15cqw, 11px); font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: #aab3c2;
}

.adplayer__form {
  display: flex; align-items: center; gap: clamp(8px, 1.6cqw, 14px);
  margin-top: clamp(12px, 2.4cqw, 22px);
}
.adplayer__form-label {
  font-size: clamp(8px, 1.15cqw, 11px); font-weight: 800; letter-spacing: .2em;
  color: #8b93a2;
}
.adplayer__pips { display: flex; gap: clamp(5px, 1cqw, 9px); }
.adplayer__pip {
  width: clamp(20px, 3.4cqw, 32px); height: clamp(20px, 3.4cqw, 32px);
  display: grid; place-items: center; border-radius: 8px;
  font-family: var(--mono, monospace); font-weight: 800;
  font-size: clamp(10px, 1.7cqw, 15px); color: #05070c;
  animation: ad-player-pop .38s cubic-bezier(.2,1.2,.35,1) both;
  animation-delay: calc(.34s + var(--i) * .07s);
}
.adplayer__pip.is-win    { background: #ffcf4a; }
.adplayer__pip.is-podium { background: #cfe0f2; }
.adplayer__pip.is-back   { background: rgba(255,255,255,.16); color: #cbd3df; }

@keyframes ad-player-in   { from { opacity: 0; transform: translateY(14px) scale(.98); } to { opacity: 1; transform: none; } }
@keyframes ad-player-line { from { opacity: 0; transform: translateX(-16px); } to { opacity: 1; transform: none; } }
@keyframes ad-player-pop  { from { opacity: 0; transform: translateY(10px) scale(.9); } to { opacity: 1; transform: none; } }
@keyframes ad-player-badge { 0%,100% { transform: translateY(0) rotate(-1deg); } 50% { transform: translateY(-5px) rotate(1deg); } }

@media (prefers-reduced-motion: reduce) {
  .adplayer, .adplayer__name, .adplayer__team, .adplayer__stat, .adplayer__pip,
  .adplayer__badge, .adplayer::before { animation: none; }
}

/* ---- the lower third ----------------------------------------------------- */

/* Every spoken line, whether or not it was spoken. This is what makes a
   never-yet-voiced commercial watchable on its first airing. */
.adbreak__lower {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: clamp(8px, 1.6cqw, 16px) clamp(12px, 3cqw, 34px);
  background: linear-gradient(transparent, rgba(0, 0, 0, .82) 42%);
  color: #fff;
  font-size: clamp(12px, 1.9cqw, 20px);
  font-weight: 600;
  line-height: 1.28;
  text-shadow: 0 2px 10px rgba(0, 0, 0, .9);
  opacity: 0;
  transition: opacity .16s;
  pointer-events: none;
}
.adbreak__lower.is-on { opacity: 1; }

/* ---- the small print ----------------------------------------------------- */

.adbreak__crawl {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 5px clamp(10px, 2cqw, 26px) 6px;
  background: rgba(0, 0, 0, .55);
  color: rgba(255, 255, 255, .72);
  font-size: clamp(7px, .95cqw, 10px);
  line-height: 1.35;
  letter-spacing: .01em;
  opacity: 0;
  pointer-events: none;
}
.adbreak__crawl.is-on { opacity: 1; }

/* When both are on, the disclaimer sits under the line rather than through
   it — the endcard is the one place a spot may have a spoken line and small
   print at the same time. */
.adbreak__lower.is-on ~ .adbreak__crawl.is-on { display: none; }

/* ---- the corner bug ------------------------------------------------------ */

/* The one thing on screen for the whole junction. A viewer who joins during
   an advert can name what they are waiting for and see how long, which is the
   entire reason the break is watchable rather than something to leave. */
.adbreak__bug {
  position: absolute;
  top: clamp(8px, 1.6cqw, 18px);
  right: clamp(8px, 1.6cqw, 18px);
  min-width: clamp(118px, 17cqw, 190px);
  padding: clamp(6px, 1cqw, 11px) clamp(8px, 1.2cqw, 14px);
  background: rgba(4, 6, 11, .82);
  border: 1px solid rgba(255, 255, 255, .14);
  border-radius: 8px;
  backdrop-filter: blur(6px);
  color: #e9eef7;
  text-align: right;
  pointer-events: none;
  z-index: 4;
}
.adbreak__bug-head {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 7px;
  margin-bottom: 4px;
}
.adbreak__bug-kicker {
  font-size: clamp(7px, .85cqw, 9px);
  font-weight: 800;
  letter-spacing: .34em;
  opacity: .6;
}
.adbreak__bug-logo { width: clamp(16px, 2.4cqw, 26px); height: auto; }
.adbreak__bug-logo:not([src]) { display: none; }
.adbreak__bug-name {
  font-size: clamp(10px, 1.35cqw, 15px);
  font-weight: 800;
  line-height: 1.1;
}
.adbreak__bug-where {
  font-size: clamp(8px, 1cqw, 11px);
  opacity: .58;
  margin-top: 1px;
}
.adbreak__bug-clock {
  margin-top: 5px;
  font-family: var(--mono, monospace);
  font-size: clamp(14px, 2.4cqw, 26px);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: .02em;
}
.adbreak__bug.is-imminent { border-color: rgba(255, 59, 71, .6); }
.adbreak__bug.is-imminent .adbreak__bug-clock { color: #ff5a5f; }

/* ===========================================================================
   The control booth's preview
   =========================================================================== */

/* A 16:9 box for the break player to take over, with the play button that
   starts it. The button is not decoration and must not be removed: browsers
   block audio until the page has been interacted with, so a preview that
   started on load would be a silent preview — and sound is the one thing this
   screen exists to check. */
.adpreview {
  border: 1px solid var(--line, #223049);
  border-radius: var(--radius, 14px);
  overflow: hidden;
  background: #000;
}
.adpreview__stage {
  position: relative;
  aspect-ratio: 16 / 9;
  width: 100%;
}
/* Older engines with no aspect-ratio still get a usable box rather than a
   collapsed one. */
@supports not (aspect-ratio: 16 / 9) {
  .adpreview__stage { height: 0; padding-bottom: 56.25%; }
}

.adpreview__play {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  border: 0;
  background: radial-gradient(circle at 50% 45%, #16202f, #05070c 70%);
  color: var(--ink, #e9eef7);
  font-family: inherit;
  cursor: pointer;
  transition: background .15s;
}
.adpreview__play:hover { background: radial-gradient(circle at 50% 45%, #1d2a3d, #05070c 70%); }
.adpreview__play-icon {
  font-size: 34px;
  line-height: 1;
  color: var(--accent, #ff7a18);
}
.adpreview__play-label { font-size: 15px; font-weight: 800; letter-spacing: .02em; }
.adpreview__play-sub { font-size: 12px; color: var(--ink-3, #6d7c96); }

/* The diagnostic strip. Turns "it's black" into a sentence. */
.diagstrip {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 10px;
  padding: 8px 12px;
  border: 1px solid var(--line, #223049);
  border-radius: var(--radius-sm, 9px);
  background: var(--panel, #121826);
  font-size: 12px;
}
.diagstrip__dot {
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--ink-3, #6d7c96);
}
.diagstrip__dot.is-good { background: var(--good, #35d07f); }
.diagstrip__dot.is-warn { background: var(--accent-2, #ffb703); }
.diagstrip__dot.is-bad  { background: var(--bad, #ff5a5f); }
.diagstrip__note { color: var(--ink, #e9eef7); }
.diagstrip__facts {
  margin-left: auto;
  font-family: var(--mono, monospace);
  font-size: 11px;
  color: var(--ink-3, #6d7c96);
}
.diagstrip--chain {
  display: block;
  font-family: var(--mono, monospace);
  font-size: 11px;
  color: var(--accent-2, #ffb703);
  border-color: var(--accent-2, #ffb703);
  overflow-x: auto;
  white-space: nowrap;
}

/* The bug says PREVIEW rather than counting down to a fixture there isn't. */
.adpreview__stage .adbreak__bug-kicker::after { content: ""; }

/* ===========================================================================
   Sponsors on the site itself
   =========================================================================== */

/* ---- a banner slot ------------------------------------------------------- */

/* Styled as a real display ad — a hard rectangle in somebody else's colours,
   sitting in the page rather than being part of it. The `--brand` and `--ink`
   custom properties are set inline by the partial from the bible. */
.adslot {
  display: block;
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-sm, 9px);
  background: var(--brand, #333);
  color: var(--brandink, #fff);
  text-decoration: none;
  border: 1px solid rgba(0, 0, 0, .3);
  font-family: "Helvetica Neue", Arial, var(--sans, sans-serif);
}
.adslot:hover { filter: brightness(1.06); }

.adslot__label {
  position: absolute;
  top: 0;
  left: 0;
  padding: 2px 6px;
  background: rgba(0, 0, 0, .34);
  color: rgba(255, 255, 255, .8);
  font-size: 8px;
  font-weight: 700;
  letter-spacing: .2em;
  text-transform: uppercase;
  border-bottom-right-radius: 6px;
}

.adslot__in {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 18px 18px 16px;
}
.adslot__logo { flex: 0 0 auto; width: 116px; height: auto; }
.adslot__body { min-width: 0; }
/* These are spans, so they need telling they are blocks — an inline element
   ignores margin-top, and the name, the tagline and the small print were
   running together as one paragraph with the spacing silently dropped. */
.adslot__body, .adslot__name, .adslot__tag, .adslot__print { display: block; }
.adslot__name {
  font-size: 15px;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: .01em;
  line-height: 1.05;
}
.adslot__tag { margin-top: 4px; font-size: 12.5px; font-weight: 600; opacity: .88; line-height: 1.25; }
.adslot__print { margin-top: 8px; font-size: 8.5px; line-height: 1.3; opacity: .6; }

/* The tall one, for a sidebar. */
.adslot--tower .adslot__in { flex-direction: column; text-align: center; padding: 22px 16px 18px; }
.adslot--tower .adslot__logo { width: 150px; }

/* The thin one, for the foot of a page. */
.adslot--strip .adslot__in { padding: 10px 14px; gap: 12px; }
.adslot--strip .adslot__logo { width: 84px; }
.adslot--strip .adslot__print { display: none; }
.adslot--strip .adslot__name { font-size: 13px; }

/* ---- the sponsors index -------------------------------------------------- */

.brandgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 14px;
}
.brandcard {
  display: block;
  border-radius: var(--radius-sm, 9px);
  overflow: hidden;
  text-decoration: none;
  background: var(--panel, #121826);
  border: 1px solid var(--line, #223049);
  transition: transform .12s, border-color .12s;
}
.brandcard:hover { transform: translateY(-2px); border-color: var(--brand, #666); }
.brandcard__art { display: block; width: 100%; height: auto; background: var(--brand, #333); }
.brandcard__body { padding: 11px 13px 13px; }
.brandcard__name { font-size: 13.5px; font-weight: 800; color: var(--ink, #e9eef7); }
.brandcard__sector {
  margin-top: 2px;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: .16em;
  color: var(--ink-3, #6d7c96);
}
.brandcard__tag { margin-top: 7px; font-size: 12px; color: var(--ink-2, #a9b6cc); line-height: 1.35; }

/* ---- one sponsor's page -------------------------------------------------- */

.brandhead {
  border-radius: var(--radius, 14px);
  overflow: hidden;
  border: 1px solid var(--line, #223049);
  margin-bottom: 18px;
}
.brandhead__art { display: block; width: 100%; height: auto; background: var(--brand, #333); }
.brandhead__body { padding: 16px 18px; background: var(--panel, #121826); }
.brandhead__print {
  margin-top: 10px;
  font-size: 10px;
  line-height: 1.45;
  color: var(--ink-3, #6d7c96);
  max-width: 62ch;
}

/* A spot in the bank, listed on a brand's page and in the control booth. */
.spotcard {
  border: 1px solid var(--line, #223049);
  border-radius: var(--radius-sm, 9px);
  background: var(--panel, #121826);
  padding: 12px 14px;
}
.spotcard + .spotcard { margin-top: 9px; }
.spotcard__head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.spotcard__title { font-weight: 800; font-size: 13.5px; color: var(--ink, #e9eef7); }
.spotcard__meta { font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-3, #6d7c96); }
.spotcard__log { margin: 6px 0 0; font-size: 12.5px; color: var(--ink-2, #a9b6cc); line-height: 1.4; }

/* The script, as a shooting board. Monospace because the durations only line
   up in one. */
.spotscript {
  margin-top: 10px;
  border-top: 1px solid var(--line-soft, #1a2437);
  padding-top: 9px;
  font-family: var(--mono, monospace);
  font-size: 11px;
  line-height: 1.55;
  color: var(--ink-2, #a9b6cc);
}
.spotscript__row { display: flex; gap: 10px; padding: 2px 0; }
.spotscript__at { flex: 0 0 4.4em; color: var(--ink-3, #6d7c96); }
.spotscript__what { min-width: 0; }
.spotscript__vo { color: var(--ink, #e9eef7); }
.spotscript__vo::before { content: "♪ "; color: var(--accent, #ff7a18); }
.spotscript__silent { opacity: .45; font-style: italic; }
.spotscript__role {
  font-size: 9.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--accent, #ff7a18);
  opacity: .8;
}

/* ---- a shirt sponsor, next to a crest ------------------------------------ */

.kitsponsor {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 8px 2px 4px;
  border-radius: 999px;
  background: var(--brand, #333);
  color: var(--brandink, #fff);
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
}
.kitsponsor__dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; opacity: .7; }

/* ===========================================================================
   Reduced motion
   ---------------------------------------------------------------------------
   The cut and the flash are the only animations here and both are under a
   fifth of a second, but a single-frame brightness spike is exactly the kind
   of thing this setting exists for. The break still plays; it simply changes
   scene without announcing it.
   =========================================================================== */

@media (prefers-reduced-motion: reduce) {
  /* **The ident is deliberately not in here.**
     It was, and it honoured the setting properly: the mark built in a third of
     a second, the wipe parked, the drift and sweep stopped. That is the correct
     reading of the media query and it is not what this network wants — the
     ident is the channel announcing itself and it is the one four seconds an
     operator actually art-directs, so it plays in full for everybody. A
     considered decision rather than an oversight, which is why it is written
     down: if it is ever reversed, the rules to restore are the eight that used
     to sit here, all gated on the ident.

     What stays is the *flash*, which is a different thing in kind. A
     single-frame brightness spike between cuts is a photosensitivity risk
     rather than a stylistic flourish, and nobody chooses to see one. */
  .adscene { animation: none !important; }
  .adscene.is-flash { animation: none !important; }
  .adbreak__lower { transition: none !important; }
  .adover { animation: none !important; }
  .brandcard:hover { transform: none; }
}

/* ===========================================================================
   The script editor
   ---------------------------------------------------------------------------
   Dense on purpose. A commercial is up to fourteen scenes and each has a set,
   a light, a shot, a cast, props, a line and three pieces of type — laid out
   generously that is several screens of scrolling to change one word, and the
   thing you are editing is a twenty-second film you need to hold in your head
   all at once.
   =========================================================================== */

.edscene {
  border: 1px solid var(--line, #223049);
  border-radius: var(--radius-sm, 9px);
  background: var(--panel, #121826);
  margin-bottom: 10px;
  overflow: hidden;
}
.edscene__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;
  background: var(--panel-2, #172033);
  border-bottom: 1px solid var(--line, #223049);
}
.edscene__n { font-size: 12px; font-weight: 800; color: var(--ink, #e9eef7); }
.edscene__tag {
  font-family: var(--mono, monospace);
  font-size: 10.5px;
  color: var(--accent, #ff7a18);
}
.edscene__body { padding: 10px; }

.edrow { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-end; margin-bottom: 8px; }
.edrow--tight { margin-bottom: 5px; }

.edfield { display: flex; flex-direction: column; gap: 3px; min-width: 120px; }
.edfield--wide { flex: 1 1 260px; }
.edfield--num { min-width: 78px; max-width: 96px; }
.edfield--check { min-width: 62px; }
.edfield__label {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-3, #6d7c96);
}
.edfield select,
.edfield input[type="text"],
.edfield input[type="number"] {
  width: 100%;
  padding: 5px 7px;
  font-size: 12.5px;
  background: var(--bg-2, #0d1119);
  color: var(--ink, #e9eef7);
  border: 1px solid var(--line, #223049);
  border-radius: 6px;
}
.edfield input[type="checkbox"] { width: 18px; height: 18px; }

.edsub {
  border-top: 1px solid var(--line-soft, #1a2437);
  padding-top: 8px;
  margin-top: 8px;
}
.edsub__title {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-3, #6d7c96);
  margin-bottom: 6px;
}

