/* ------------------------------------------------------------------
   The simulated stream.

   One stylesheet renders every surface: the small source monitors in the
   operator multiviewer and the full-screen programme output on a viewer's
   laptop. Everything inside a stage is sized in container-query units, so
   the same markup is correct at 320px and at 4K with no JS measuring.

   GENLOCK: every infinite animation below takes its position from --phase,
   a negative delay derived from the shared server clock (see render.js).
   For that to loop without a visible jump, EVERY infinite duration here must
   divide PHASE_BASE = 120000ms. Permitted values include 0.2s, 0.5s, 1s,
   1.2s, 1.25s, 1.5s, 2s, 2.5s, 3s, 4s, 5s, 6s, 8s, 10s, 12s, 15s, 20s, 24s,
   30s, 40s, 60s, 120s. If you add an animation, pick from that list.

   Design constraint: this is watched through Teams/Meet screen share at
   ~10fps and heavy compression. Big shapes, high contrast, slow motion.
   ------------------------------------------------------------------ */

.stage {
  container-type: inline-size;
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: #000;
  color: #fff;
  font-family: "Barlow Condensed", "Helvetica Neue", Arial, sans-serif;
  user-select: none;
  --phase: 0ms;
}

.stage * { box-sizing: border-box; }

/* Every looping animation in the feed hangs off this. */
.stage .feed [class*="feed-"],
.stage .feed .ticker span,
.stage .feed .meters i,
.stage .feed-news * { animation-delay: calc(0ms - var(--phase)); }

/* --- channel palettes ------------------------------------------- */
.stage[data-ch="blue"] { --c-deep: #041231; --c-mid: #0b3d91; --c-hot: #38bdf8; --c-accent: #7dd3fc; }
.stage[data-ch="red"]  { --c-deep: #2b0407; --c-mid: #9f1239; --c-hot: #fb7185; --c-accent: #fdba74; }

.feed { position: absolute; inset: 0; }

/* Mode decides which programme the feed is carrying. */
.stage[data-mode="mirrored"] .feed-channel,
.stage[data-mode="video"] .feed-channel { display: none; }
.stage:not([data-mode="mirrored"]) .feed-news { display: none; }
.stage:not([data-mode="video"]) .feed-video { display: none; }

/* ==================================================================
   DISTINCT MODE — two visibly different channels (source selection)
   ================================================================== */
.feed-channel { position: absolute; inset: 0; }

.feed-bg {
  position: absolute; inset: 0;
  background:
    radial-gradient(120% 90% at 30% 20%, var(--c-mid) 0%, var(--c-deep) 60%, #01040c 100%);
}

/* Slow drifting light source. Large and soft: survives compression. */
.feed-orb {
  position: absolute;
  width: 70cqw; height: 70cqw;
  left: 10cqw; top: -10cqw;
  border-radius: 50%;
  background: radial-gradient(circle, var(--c-hot) 0%, transparent 65%);
  opacity: .5;
  filter: blur(4cqw);
  animation: orb-drift 20s ease-in-out infinite alternate;
}
@keyframes orb-drift {
  from { transform: translate3d(-12cqw, 4cqw, 0) scale(1); }
  to   { transform: translate3d(28cqw, -2cqw, 0) scale(1.25); }
}

.feed-bands {
  position: absolute; inset: -20%;
  background: repeating-linear-gradient(
    115deg,
    rgba(255, 255, 255, .085) 0 6cqw,
    rgba(255, 255, 255, 0) 6cqw 18cqw
  );
  animation: bands-slide 12s linear infinite;
}
@keyframes bands-slide {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(19.9cqw, -9.3cqw, 0); }
}

.ident {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 1cqw;
  text-align: center;
}
.ident-name {
  font-size: 11cqw; font-weight: 800;
  letter-spacing: .06em; line-height: .95;
  text-shadow: 0 .6cqw 2.4cqw rgba(0, 0, 0, .6);
}
.ident-sub {
  font-size: 2.6cqw; letter-spacing: .42em;
  text-transform: uppercase; color: var(--c-accent);
  padding-left: .42em;
}

.bug {
  position: absolute; top: 3cqw; right: 3cqw;
  display: flex; align-items: center; gap: 1.2cqw;
  padding: .9cqw 1.6cqw;
  background: rgba(0, 0, 0, .42);
  border: .2cqw solid rgba(255, 255, 255, .28);
  border-radius: .8cqw;
}
.bug b { font-size: 2.6cqw; font-weight: 800; letter-spacing: .04em; }
.bug span {
  font-size: 2cqw; letter-spacing: .18em; color: var(--c-accent);
  border-left: .2cqw solid rgba(255, 255, 255, .28);
  padding-left: 1.2cqw;
}

/* ==================================================================
   MIRRORED MODE — one programme, carried identically by both chains.

   A two-hander studio: wall, desk, and two anchors who idle, blink and take
   turns speaking. Nothing in here may reference the channel palette. Main and
   backup have to be pixel-identical, so this scene has its own fixed colours
   and takes no cue whatsoever from which chain is rendering it.

   Layout is measured from the BOTTOM of the stage, because the strap (7-13.4cqw)
   and ticker (0-7cqw) are fixed furniture the scene has to sit on top of.
   ================================================================== */
.feed-news {
  position: absolute; inset: 0;
  --n-bg: #071026;
  --n-deep: #030814;
  --n-hot: #f43f5e;
  --n-cool: #38bdf8;
  --n-pale: #cbd5e1;
}

.news-bg {
  position: absolute; inset: 0;
  background:
    radial-gradient(90% 120% at 50% 30%, #12224a 0%, var(--n-bg) 55%, var(--n-deep) 100%);
}

.news-sweep {
  position: absolute; inset: -20%;
  background: linear-gradient(105deg,
    transparent 30%, rgba(56, 189, 248, .12) 48%, transparent 64%);
  animation: news-sweep 20s linear infinite;
}
@keyframes news-sweep {
  from { transform: translate3d(-40cqw, 0, 0); }
  to   { transform: translate3d(60cqw, 0, 0); }
}

.studio { position: absolute; inset: 0; }

/* --- the wall behind the presenters ------------------------------ */
.studio-wall {
  position: absolute;
  left: 3cqw; right: 3cqw;
  bottom: 20cqw; height: 30cqw;
  border-radius: .6cqw;
  background: linear-gradient(180deg, #0d1c3c 0%, #071634 60%, #050f24 100%);
  box-shadow: inset 0 0 6cqw rgba(56, 189, 248, .16);
  overflow: hidden;
}
.wall-grid {
  position: absolute; inset: 0;
  background:
    repeating-linear-gradient(90deg, rgba(56,189,248,.09) 0 .12cqw, transparent .12cqw 4cqw),
    repeating-linear-gradient(0deg,  rgba(56,189,248,.07) 0 .12cqw, transparent .12cqw 4cqw);
}

/* Wireframe globe. Meridians are ellipses scaled through zero on X, which
   reads as rotation and costs nothing to composite. */
.news-globe {
  position: absolute;
  left: 50%; top: 50%;
  width: 32cqw; height: 32cqw;
  transform: translate(-50%, -50%);
  overflow: visible;
  /* Mostly occluded by the presenters, and that is the point — a wall graphic
     you can see all of is a wall graphic nobody is standing in front of. */
  opacity: .32;
}
.news-globe .globe-rim,
.news-globe .globe-lat,
.news-globe .globe-mer {
  fill: none;
  stroke: var(--n-cool);
  stroke-width: 1.4;
  vector-effect: non-scaling-stroke;
}
.news-globe .globe-rim { stroke-width: 2.4; stroke-opacity: .85; }
.news-globe .globe-lat { stroke-opacity: .4; }
.news-globe .globe-mer {
  stroke-opacity: .55;
  transform-box: fill-box;
  transform-origin: center;
  animation: globe-spin 60s linear infinite;
  animation-delay: calc((0ms - var(--phase)) + (var(--m) * -15s)) !important;
}
@keyframes globe-spin {
  0%   { transform: scaleX(1); }
  25%  { transform: scaleX(0); }
  50%  { transform: scaleX(-1); }
  75%  { transform: scaleX(0); }
  100% { transform: scaleX(1); }
}

/* Two soft key lights from above, so the figures sit in the room. */
.studio-lights {
  position: absolute; inset: 0;
  background:
    radial-gradient(38% 46% at 24% 22%, rgba(125, 211, 252, .16), transparent 70%),
    radial-gradient(38% 46% at 46% 18%, rgba(125, 211, 252, .12), transparent 70%);
}

/* --- over-the-shoulder story panel -------------------------------- */
.studio-ots {
  position: absolute;
  right: 3cqw; bottom: 25cqw;
  width: 26cqw; height: 17cqw;
  border-radius: .5cqw;
  background: linear-gradient(165deg, rgba(8, 22, 52, .96), rgba(4, 11, 28, .96));
  border: .22cqw solid rgba(56, 189, 248, .5);
  box-shadow: 0 1cqw 3cqw rgba(0, 0, 0, .5);
  overflow: hidden;
}
.news-card {
  position: absolute;
  left: 1.6cqw; right: 1.6cqw; bottom: 1.6cqw;
  opacity: 0;
  animation: card-cycle 24s ease-in-out infinite;
}
.news-card:nth-child(2) { animation-delay: calc((0ms - var(--phase)) + 8s) !important; }
.news-card:nth-child(3) { animation-delay: calc((0ms - var(--phase)) + 16s) !important; }
@keyframes card-cycle {
  0%        { opacity: 0; transform: translate3d(0, 1.2cqw, 0); }
  2%, 31%   { opacity: 1; transform: translate3d(0, 0, 0); }
  34%, 100% { opacity: 0; transform: translate3d(0, -1.2cqw, 0); }
}
.news-card .kicker {
  display: inline-block;
  font-size: 1.4cqw; font-weight: 800; letter-spacing: .2em;
  padding: .35cqw .9cqw;
  margin-bottom: 1cqw;
  background: var(--n-cool);
  color: #04121f;
  border-radius: .25cqw;
}
.news-card h2 {
  margin: 0;
  font-size: 2.5cqw; font-weight: 800; line-height: 1.1;
}

/* --- the anchors --------------------------------------------------
   Seat A is drawn slightly larger and nearer the camera than seat B, which
   is what stops a two-shot looking like a pair of cut-outs. */
/* Framing: the desk crosses the chest, leaving roughly 70% of the figure in
   shot. Cut any higher and they read as heads peering over a wall. */
.anchor {
  position: absolute;
  bottom: 13.4cqw; height: 32cqw;
  overflow: visible;
  --skin: #c98d68;
  --skin-shade: #a9714f;
  --hair: #241a16;
  --jacket: #1e3a5f;
  --jacket-dark: #16293f;
  --shirt: #e8eef7;
  /* Two independent loops: the body breathes on 12s, and on 24s the anchor
     who is not speaking sits back very slightly. */
  animation:
    anchor-sway 12s ease-in-out infinite alternate,
    anchor-turn 24s linear infinite;
}
.anchor-a { left: 18cqw; height: 33cqw; }
.anchor-b {
  left: 44cqw;
  --skin: #e0b394;
  --skin-shade: #c28f6d;
  --hair: #6b3b22;
  --jacket: #7f1d3a;
  --jacket-dark: #5c1229;
  animation-delay:
    calc((0ms - var(--phase)) - 4s),
    calc((0ms - var(--phase)) + 12s) !important;
}
@keyframes anchor-sway {
  from { transform: translate3d(0, .1cqw, 0) rotate(-.25deg); }
  to   { transform: translate3d(0, -.1cqw, 0) rotate(.25deg); }
}

.anchor .skin { fill: var(--skin); }
.anchor .neck-shade { fill: var(--skin-shade); }
.anchor .hair { fill: var(--hair); }
.anchor .jacket { fill: var(--jacket); }
.anchor .lapel { fill: var(--jacket-dark); }
.anchor .shirt { fill: var(--shirt); }
.anchor .tie { fill: var(--n-hot); }
.anchor .eye { fill: #1c2433; }
.anchor .brow { fill: none; stroke: var(--hair); stroke-width: 2; stroke-linecap: round; }
.anchor .nose { fill: none; stroke: var(--skin-shade); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.anchor .mouth { fill: #7b3b42; }

/* Head idles independently of the body, or the figure looks like a statue
   on a turntable. */
.anchor-head {
  transform-box: fill-box;
  transform-origin: 50% 100%;
  animation: head-idle 8s ease-in-out infinite alternate;
}
.anchor-b .anchor-head { animation-delay: calc((0ms - var(--phase)) - 3s) !important; }
@keyframes head-idle {
  from { transform: rotate(-1.4deg) translateY(.4%); }
  to   { transform: rotate(1.4deg) translateY(-.4%); }
}

/* Blink: 6s apart, closed for about 90ms. */
.eye-gate {
  transform-box: fill-box;
  transform-origin: center;
  animation: blink 6s linear infinite;
}
.anchor-b .eye-gate { animation-delay: calc((0ms - var(--phase)) - 2.4s) !important; }
@keyframes blink {
  0%, 96.4%   { transform: scaleY(1); }
  97%, 97.6%  { transform: scaleY(.08); }
  98.2%, 100% { transform: scaleY(1); }
}

/* Speech is two nested transforms: an inner loop that flaps the mouth, and an
   outer gate that decides whose turn it is. Composing them through nesting is
   what makes a 12-second talking turn expressible without 60 keyframes. */
.mouth-gate {
  transform-box: fill-box;
  transform-origin: center;
  animation: speak-gate 24s linear infinite;
}
.anchor-b .mouth-gate { animation-delay: calc((0ms - var(--phase)) + 12s) !important; }
@keyframes speak-gate {
  0%, 49.4%   { transform: scaleY(1); }
  50%, 99.4%  { transform: scaleY(.14); }
  100%        { transform: scaleY(1); }
}
.mouth {
  transform-box: fill-box;
  transform-origin: center;
  animation: talk .4s ease-in-out infinite alternate;
}
@keyframes talk {
  from { transform: scaleY(.45); }
  to   { transform: scaleY(1.2); }
}

@keyframes anchor-turn {
  0%, 49.4% { filter: brightness(1); }
  50%, 100% { filter: brightness(.9); }
}

/* --- the desk ----------------------------------------------------- */
.studio-desk {
  position: absolute;
  left: 0; right: 0; bottom: 13.4cqw; height: 9.6cqw;
}
.desk-surface {
  position: absolute; left: 0; right: 0; top: 0; height: 2.2cqw;
  background: linear-gradient(180deg, #43587c 0%, #223350 100%);
  box-shadow: 0 -.18cqw 0 rgba(125, 211, 252, .55);
}
.desk-front {
  position: absolute; left: 0; right: 0; top: 2.2cqw; bottom: 0;
  display: flex; align-items: center;
  padding: 0 0 1.6cqw 5cqw;
  background: linear-gradient(180deg, #16233c 0%, #0a1224 100%);
  border-top: .12cqw solid rgba(56, 189, 248, .35);
}
.desk-front::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: .9cqw; height: .28cqw;
  background: linear-gradient(90deg, transparent, rgba(56,189,248,.55) 22%, rgba(56,189,248,.55) 78%, transparent);
}
.desk-mark {
  display: flex; align-items: baseline; gap: .45cqw;
  font-size: 2.6cqw; font-weight: 800;
  color: rgba(226, 232, 240, .9);
}
.desk-mark b {
  background: var(--n-hot);
  color: #fff;
  padding: .15cqw .8cqw .3cqw;
  border-radius: .3cqw;
}

/* --- header, strap ------------------------------------------------ */
.news-top {
  position: absolute; top: 3.4cqw; left: 4cqw;
  display: flex; align-items: center; gap: 2cqw;
}
.news-mark {
  display: flex; align-items: baseline; gap: .5cqw;
  font-size: 3.2cqw; font-weight: 800; letter-spacing: .02em;
  color: #fff;
}
.news-mark b {
  background: var(--n-hot);
  padding: .2cqw 1cqw .4cqw;
  border-radius: .4cqw;
}
.news-live {
  display: flex; align-items: center; gap: .8cqw;
  font-size: 2cqw; font-weight: 700; letter-spacing: .22em;
  color: var(--n-pale);
}
.news-live i {
  width: 1.2cqw; height: 1.2cqw; border-radius: 50%;
  background: var(--n-hot);
  animation: live-blink 2s ease-in-out infinite;
}
@keyframes live-blink { 0%, 100% { opacity: 1; } 50% { opacity: .2; } }
.news-clock {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 2.2cqw; letter-spacing: .06em;
  color: var(--n-pale);
  font-variant-numeric: tabular-nums;
}

.news-strap {
  position: absolute; left: 0; right: 0; bottom: 7cqw;
  display: flex; align-items: stretch;
  height: 6.4cqw;
  background: linear-gradient(90deg, rgba(4, 10, 24, .96), rgba(4, 10, 24, .76));
  border-top: .2cqw solid rgba(56, 189, 248, .45);
}
.strap-tag {
  display: flex; align-items: center;
  padding: 0 2cqw;
  font-size: 2.4cqw; font-weight: 800; letter-spacing: .2em;
  background: var(--n-hot);
  animation: strap-flash 4s steps(1, end) infinite;
}
@keyframes strap-flash {
  0%, 74%   { filter: brightness(1); }
  75%, 100% { filter: brightness(.7); }
}
.strap-lines { position: relative; flex: 1; overflow: hidden; }
.strap-lines span {
  position: absolute; inset: 0;
  display: flex; align-items: center;
  padding-left: 2cqw;
  font-size: 3cqw; letter-spacing: .04em;
  opacity: 0;
  animation: strap-cycle 24s ease-in-out infinite;
}
.strap-lines span:nth-child(2) { animation-delay: calc((0ms - var(--phase)) + 12s) !important; }
@keyframes strap-cycle {
  0%        { opacity: 0; transform: translate3d(0, 1.4cqw, 0); }
  3%, 46%   { opacity: 1; transform: translate3d(0, 0, 0); }
  50%, 100% { opacity: 0; transform: translate3d(0, -1.4cqw, 0); }
}

/* ==================================================================
   VIDEO MODE — a real programme file, carried identically by both chains.

   The clip brings its own graphics, so programme out drops the ticker, the
   meters and the timecode. The source monitors keep timecode and meters:
   they are the operator's tell that a chain has frozen.
   ================================================================== */
.feed-video { position: absolute; inset: 0; background: #000; }
.feed-video video {
  display: block;
  width: 100%; height: 100%;
  object-fit: cover;
}
.video-missing {
  position: absolute; inset: 0;
  display: none;
  flex-direction: column; align-items: center; justify-content: center; gap: 1.2cqw;
  background: #05070d;
}
.stage[data-video="missing"] .video-missing { display: flex; }
.video-missing b { font-size: 4cqw; font-weight: 800; letter-spacing: .2em; }
.video-missing span {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 2cqw; color: #94a3b8;
}
.stage[data-mode="video"] .ticker,
.stage[data-mode="video"][data-ctx="program"] .meters,
.stage[data-mode="video"][data-ctx="program"] .tc { display: none; }

/* ==================================================================
   SHARED FURNITURE — present in both modes
   ================================================================== */
.tc {
  position: absolute; top: 3cqw; left: 3cqw;
  font-family: "SF Mono", ui-monospace, Menlo, monospace;
  font-size: 2.6cqw; letter-spacing: .08em;
  padding: .7cqw 1.3cqw;
  background: rgba(0, 0, 0, .5);
  border-radius: .6cqw;
  font-variant-numeric: tabular-nums;
}
/* The news scene has its own header, so the timecode moves out of its way. */
.stage[data-mode="mirrored"] .tc {
  top: auto; bottom: 14.4cqw; left: auto; right: 3cqw;
  font-size: 2.2cqw;
}

.meters {
  position: absolute; right: 3cqw; bottom: 11cqw;
  display: flex; align-items: flex-end; gap: .7cqw;
  height: 12cqw;
}
/* In the studio scene the meters sit beside the timecode, in the clear band
   across the desk, rather than on top of the story panel. */
.stage[data-mode="mirrored"] .meters {
  bottom: 14.6cqw; right: 22cqw; height: 5cqw; gap: .5cqw;
}
.stage[data-mode="mirrored"] .meters i { width: .8cqw; }
.meters i {
  display: block;
  width: 1.1cqw; height: 100%;
  background: linear-gradient(to top, #22c55e 0%, #22c55e 55%, #eab308 78%, #ef4444 100%);
  border-radius: .2cqw;
  transform-origin: bottom;
  animation: meter-bounce 1.2s ease-in-out infinite alternate;
}
.meters i:nth-child(1) { animation-duration: 1.2s;  animation-delay: calc((0ms - var(--phase)) - .2s) !important; }
.meters i:nth-child(2) { animation-duration: 1.5s;  animation-delay: calc((0ms - var(--phase)) - .6s) !important; }
.meters i:nth-child(3) { animation-duration: .8s;   animation-delay: calc((0ms - var(--phase)) - .1s) !important; }
.meters i:nth-child(4) { animation-duration: 1s;    animation-delay: calc((0ms - var(--phase)) - .9s) !important; }
.meters i:nth-child(5) { animation-duration: 2s;    animation-delay: calc((0ms - var(--phase)) - .4s) !important; }
.meters i:nth-child(6) { animation-duration: 1.25s; animation-delay: calc((0ms - var(--phase)) - .7s) !important; }
@keyframes meter-bounce {
  from { transform: scaleY(.18); }
  to   { transform: scaleY(.95); }
}

/* Seamless marquee: render.js emits the caption twice and we translate by
   exactly one copy's width, so the loop point is invisible. */
.ticker {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 7cqw;
  display: flex; align-items: center;
  background: linear-gradient(90deg, rgba(0, 0, 0, .72), rgba(0, 0, 0, .45));
  border-top: .25cqw solid var(--c-hot);
  overflow: hidden;
  white-space: nowrap;
}
.stage[data-mode="mirrored"] .ticker {
  border-top-color: #38bdf8;
  background: linear-gradient(90deg, rgba(4, 10, 24, .96), rgba(4, 10, 24, .8));
}
.ticker span {
  display: inline-block;
  font-size: 2.6cqw; letter-spacing: .16em;
  animation: ticker-run 40s linear infinite;
}
@keyframes ticker-run {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

/* Source ID burned into the transmission path. A demo affordance only: in
   mirrored mode the absence of this is the thing being demonstrated. */
/* Top right: the only corner the news scene leaves empty (its header is top
   left, the timecode bottom right, the strap and ticker along the bottom). */
.reveal-burn {
  position: absolute; top: 3.4cqw; right: 4cqw;
  display: none;
}
.stage[data-reveal="true"] .reveal-burn { display: block; }
.reveal-burn span {
  display: block;
  padding: .8cqw 1.6cqw;
  font-size: 2.6cqw; font-weight: 800; letter-spacing: .2em;
  border-radius: .5cqw;
  background: rgba(0, 0, 0, .72);
  border: .25cqw solid var(--c-hot);
  color: var(--c-hot);
}

/* ------------------------------------------------------------------
   FAULT: stage one — degraded. The picture is still there but wrong.
   ------------------------------------------------------------------ */
.tear { position: absolute; inset: 0; opacity: 0; pointer-events: none; }

.stage[data-health="glitch"] .feed {
  animation: glitch-shake .2s steps(2, end) infinite;
  filter: saturate(1.7) contrast(1.35) hue-rotate(-8deg);
}
@keyframes glitch-shake {
  0%   { transform: translate3d(0, 0, 0) scale(1.05); }
  50%  { transform: translate3d(-1.4cqw, .5cqw, 0) scale(1.05); }
  100% { transform: translate3d(.9cqw, -.3cqw, 0) scale(1.05); }
}

.stage[data-health="glitch"] .tear {
  opacity: 1;
  background:
    repeating-linear-gradient(0deg,
      rgba(255, 255, 255, .16) 0 .5cqw,
      rgba(0, 0, 0, .32) .5cqw 1.1cqw,
      transparent 1.1cqw 4cqw);
  mix-blend-mode: screen;
  animation: tear-roll 1.5s linear infinite;
}
@keyframes tear-roll {
  from { transform: translate3d(0, -18cqw, 0); }
  to   { transform: translate3d(0, 18cqw, 0); }
}
.stage[data-health="glitch"] .tear::after {
  content: "";
  position: absolute; left: 0; right: 0; height: 9cqw;
  background: rgba(255, 60, 90, .3);
  box-shadow: 0 0 6cqw rgba(0, 255, 255, .45);
  animation: slab-drop 2s steps(6, end) infinite;
}
@keyframes slab-drop {
  from { top: -10cqw; }
  to   { top: 100cqw; }
}

/* Audio dies the moment the picture does. A paused animation would still win
   the cascade over this transform, so the animation is cancelled outright. */
.stage[data-health="glitch"] .meters i,
.stage[data-health="down"] .meters i { animation: none; transform: scaleY(.04); }

/* ------------------------------------------------------------------
   FAULT: stage two — signal gone.
   ------------------------------------------------------------------ */
.nosignal { position: absolute; inset: 0; display: none; background: #000; }
.stage[data-health="down"] .feed { display: none; }
.stage[data-health="down"] .nosignal { display: block; }

.noise {
  position: absolute; inset: 0;
  opacity: .5;
  background:
    repeating-linear-gradient(0deg,
      rgba(255, 255, 255, .18) 0 .35cqw, transparent .35cqw 1.1cqw),
    repeating-linear-gradient(90deg,
      rgba(255, 255, 255, .1) 0 .5cqw, transparent .5cqw 1.4cqw);
  animation: noise-jitter .2s steps(3, end) infinite;
}
@keyframes noise-jitter {
  0%   { transform: translate3d(0, 0, 0); }
  33%  { transform: translate3d(.8cqw, .6cqw, 0); }
  66%  { transform: translate3d(-.6cqw, -.4cqw, 0); }
  100% { transform: translate3d(.3cqw, -.7cqw, 0); }
}

.ns-text {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 1.4cqw;
  font-size: 7cqw; font-weight: 800; letter-spacing: .2em;
  padding-left: .2em;
  color: #ff4d4f;
  text-shadow: 0 0 4cqw rgba(255, 0, 0, .55);
  animation: ns-blink 1.5s steps(1, end) infinite;
}
.ns-text small {
  font-size: 2.4cqw; font-weight: 500; letter-spacing: .3em;
  color: rgba(255, 255, 255, .6);
}
@keyframes ns-blink {
  0%, 62%   { opacity: 1; }
  63%, 100% { opacity: .25; }
}

/* ------------------------------------------------------------------
   OVERLAYS — graphics laid over whatever is on air.
   ------------------------------------------------------------------ */
.ovl-lower3 {
  position: absolute; left: 4cqw; bottom: 10cqw;
  display: none; flex-direction: column;
  padding: 1.4cqw 3cqw 1.4cqw 2cqw;
  background: linear-gradient(90deg, rgba(0, 0, 0, .88), rgba(0, 0, 0, .5));
  border-left: .8cqw solid var(--c-hot);
  animation: l3-in .45s cubic-bezier(.2, .8, .2, 1) both;
}
.stage[data-lower3="true"] .ovl-lower3 { display: flex; }
.ovl-lower3 b { font-size: 4cqw; font-weight: 800; letter-spacing: .03em; }
.ovl-lower3 span { font-size: 2.2cqw; letter-spacing: .22em; color: var(--c-accent); }
@keyframes l3-in {
  from { transform: translateX(-6cqw); opacity: 0; }
  to   { transform: translateX(0); opacity: 1; }
}

.ovl-ad {
  position: absolute; inset: 0;
  display: none;
  align-items: center; justify-content: center;
  flex-direction: column; gap: 2cqw;
  background: rgba(3, 7, 18, .92);
}
.stage[data-ad="true"] .ovl-ad { display: flex; }
.ovl-ad b { font-size: 6.5cqw; font-weight: 800; letter-spacing: .18em; padding-left: .18em; }
.ovl-ad span { font-size: 2.4cqw; letter-spacing: .3em; color: var(--c-accent); }
.ovl-ad .ad-bar {
  width: 40cqw; height: .9cqw; border-radius: 1cqw;
  background: rgba(255, 255, 255, .18);
  overflow: hidden;
}
.ovl-ad .ad-bar i {
  display: block; height: 100%; width: 100%;
  background: var(--c-hot);
  transform-origin: left;
  animation: ad-count 30s linear infinite;
}
@keyframes ad-count {
  from { transform: scaleX(1); }
  to   { transform: scaleX(0); }
}

/* ------------------------------------------------------------------
   SLATES — what replaces the programme entirely.
   ------------------------------------------------------------------ */
.slate { position: absolute; inset: 0; display: none; }
.stage[data-slate="tech"]  .slate-tech  { display: flex; }
.stage[data-slate="bars"]  .slate-bars  { display: block; }
.stage[data-slate="black"] .slate-black { display: block; }

.slate-tech {
  flex-direction: column;
  align-items: center; justify-content: center;
  gap: 2.4cqw;
  background: radial-gradient(120% 100% at 50% 30%, #13203c 0%, #060a16 70%);
  text-align: center;
  padding: 0 8cqw;
}
.slate-tech .warn {
  width: 13cqw; height: 13cqw;
  animation: warn-pulse 2s ease-in-out infinite;
}
@keyframes warn-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: .62; transform: scale(.94); }
}
.slate-tech h1 {
  margin: 0;
  font-size: 6.4cqw; font-weight: 800; line-height: 1.08; letter-spacing: .04em;
}
.slate-tech p {
  margin: 0;
  font-size: 2.8cqw; letter-spacing: .26em;
  color: #94a3b8; text-transform: uppercase;
}
.slate-tech .hazard {
  position: absolute; left: 0; right: 0; height: 2.4cqw;
  background: repeating-linear-gradient(-45deg, #f59e0b 0 3cqw, #111827 3cqw 6cqw);
  animation: hazard-slide 3s linear infinite;
}
.slate-tech .hazard.top { top: 0; }
.slate-tech .hazard.bottom { bottom: 0; }
@keyframes hazard-slide {
  from { background-position: 0 0; }
  to   { background-position: 8.49cqw 0; }
}

/* 75% EBU/SMPTE colour bars with a castellation strip. */
.slate-bars { background: #000; }
.slate-bars .bars-main {
  position: absolute; top: 0; left: 0; right: 0; height: 75%;
  background: linear-gradient(90deg,
    #bfbfbf 0 14.285%, #bfbf00 14.285% 28.57%, #00bfbf 28.57% 42.855%,
    #00bf00 42.855% 57.14%, #bf00bf 57.14% 71.425%, #bf0000 71.425% 85.71%,
    #0000bf 85.71% 100%);
}
.slate-bars .bars-castle {
  position: absolute; top: 75%; left: 0; right: 0; height: 25%;
  background: linear-gradient(90deg,
    #0000bf 0 14.285%, #131313 14.285% 28.57%, #bf00bf 28.57% 42.855%,
    #131313 42.855% 57.14%, #00bfbf 57.14% 71.425%, #131313 71.425% 85.71%,
    #bfbfbf 85.71% 100%);
}
.slate-bars .bars-label {
  position: absolute; left: 0; right: 0; top: 50%;
  transform: translateY(-50%);
  text-align: center;
  font-size: 3.4cqw; font-weight: 800; letter-spacing: .3em;
  padding: 1.4cqw 0 1.4cqw .3em;
  background: rgba(0, 0, 0, .78);
}

.slate-black { background: #000; }

/* Context badges — only shown on the operator's monitors. */
.stage-badge {
  position: absolute; left: 3cqw; bottom: 9.5cqw;
  display: none;
  font-size: 2.2cqw; font-weight: 700; letter-spacing: .2em;
  padding: .7cqw 1.4cqw;
  border-radius: .5cqw;
  background: rgba(0, 0, 0, .7);
  border: .2cqw solid rgba(255, 255, 255, .3);
}
.stage[data-ctx="monitor"][data-slate]:not([data-slate=""]) .stage-badge { display: block; }

@media (prefers-reduced-motion: reduce) {
  .stage *, .stage *::after { animation-duration: 6s !important; }
}
