/* ==========================================================================
   TIMEVERSE — the Gaming hero (SPEC 5.4 / 6.4)
   The Into the Timeverse key art opens in a film gate (Cinemascope 2.39:1 on landscape screens) that retracts to
   full-bleed on scroll while the title rises. js/timeverse.js drives the letterbox, places the art and runs the
   portal shader on a transparent canvas over the crisp <picture>. Without JS the same layout is computed here
   (static, or driven by a CSS scroll timeline where supported). Under reduced motion / Motion: Off the hero is one
   composed still: the retracted, full-bleed frame. Standalone: every token has a fallback; every selector is scoped.

   Geometry (mirrored exactly in timeverse.js):
     window at rest  wide (> 1.55:1)       h0 = min(W / 2.39, (H - header) / 1.18)        Scope
                     square, 4:3 (≤ 1.55)  h0 = clamp(W / 2.39, .76 (H - header - title), W / 1.85)   up to Flat
                     portrait (≤ 4:5)      h0 = clamp(W / 2.39, .76 (H - header - title), W / 0.8)    up to 4:5
                     below the header; the free height is split evenly above and below it, except narrower than
                     1.55:1, where the title hangs under the window and the pair is centred. Retracted, the window
                     is everything below the header (all of the screen when it is short and the header hides).
     art width       Dw = max(min(Ww, 2.6 Wh), 2.1388 Wh)   cover, but wider than 2.6:1 the art stops growing
                                                            and a starfield extends it on the left
     art x           right-anchored when narrower than the window, else a focal crop (lettering / probe + portal,
                     panning between the two as the visible share of the art goes from 78% to 62%)
     art y           30% of any vertical excess is cropped at the top
     inflated 2.4% about the window centre, so pointer parallax never exposes an edge
     title           rests --tv-rest above the bottom; while letterboxed it sits just inside the window's bottom
                     edge (wide) or just under the window (narrow)
   ========================================================================== */

/* The title's own fallbacks. Until Anybody arrives (2–3 s on 4G; never where Google Fonts is blocked) a 22cqw
   "GAMING" in the site's Arial fallback is 30% wider than in Anybody (wght 200, wdth 100) and broke as "GAMIN / G"
   on phones. These faces are scaled to Anybody's width for the title (Arial 405.6 → 328.1 per 100px, Roboto 387.3)
   and keep Anybody's line metrics (ascent .795, descent .24), so the fallback line is the web font's line. */
@font-face {
  font-family: 'Anybody Title Fallback';
  src: local('Arial'), local('ArialMT'), local('Liberation Sans'), local('LiberationSans-Regular'), local('Arimo');
  size-adjust: 80.9%;
  ascent-override: 98.27%;
  descent-override: 29.66%;
  line-gap-override: 0%;
}
@font-face {                   /* Android has no Arial */
  font-family: 'Anybody Title Fallback R';
  src: local('Roboto'), local('Roboto-Regular');
  size-adjust: 84.72%;
  ascent-override: 93.84%;
  descent-override: 28.33%;
  line-gap-override: 0%;
}

@property --tv-lb {            /* letterbox amount: 1 = window at rest, 0 = full-bleed */
  syntax: '<number>';
  inherits: true;
  initial-value: 1;
}
@property --tv-hdr {           /* header height as an absolute length (read by timeverse.js) */
  syntax: '<length>';
  inherits: true;
  initial-value: 64px;
}
@property --tv-hdr-end {       /* where the retracted window starts: under a header that stays, at 0 under one that hides */
  syntax: '<length>';
  inherits: true;
  initial-value: 64px;
}

.tv-hero {
  --tv-ground: #02050D;
  --tv-teal: var(--accent, #38E1D2);
  --tv-text: var(--plasma, #F2F7FF);
  --tv-text-2: var(--text-2, rgba(233, 237, 250, .72));
  --tv-mono: var(--font-mono, 'Geist Mono', ui-monospace, 'Cascadia Mono', Consolas, monospace);
  --tv-display: var(--font-display, 'Anybody', 'Anybody Fallback', system-ui, sans-serif);
  --tv-edge: var(--gutter, max(clamp(16px, 4vw, 96px), env(safe-area-inset-left), env(safe-area-inset-right)));
  /* the type sits on the site's content grid (the header's and the sections' --wrap); resolved inside the stage,
     so 100cqw is the stage width */
  --tv-gutter: max(var(--tv-edge), (100cqw - var(--wrap-max, 100rem)) / 2);
  --tv-ease: var(--ease-out, cubic-bezier(.16, 1, .3, 1));
  --tv-travel: 75svh;          /* scroll distance over which the letterbox retracts */
  --tv-starfield:              /* tiled pseudo-random stars */
    radial-gradient(circle at 23% 37%, rgba(235, 242, 255, .9) 0 .6px, transparent 1.3px) 0 0 / 97px 89px,
    radial-gradient(circle at 71% 64%, rgba(200, 220, 255, .7) 0 .5px, transparent 1.1px) 0 0 / 131px 113px,
    radial-gradient(circle at 46% 12%, rgba(255, 249, 240, .85) 0 .75px, transparent 1.6px) 0 0 / 173px 151px,
    radial-gradient(circle at 88% 81%, rgba(190, 215, 255, .55) 0 .45px, transparent 1px) 0 0 / 59px 67px,
    radial-gradient(circle at 15% 90%, rgba(255, 255, 255, .95) 0 1px, transparent 2.4px) 0 0 / 263px 241px,
    radial-gradient(circle at 62% 28%, rgba(170, 205, 255, .8) 0 .8px, transparent 2px) 0 0 / 389px 331px;
  position: relative;
  height: 100vh;
  height: 100svh;
  background: var(--tv-ground);
  color: var(--tv-text);
}
@media (max-aspect-ratio: 31/20) { .tv-hero { --tv-travel: 60svh; } }   /* taller gates have less to open */
@media (max-aspect-ratio: 4/5) { .tv-hero { --tv-travel: 50svh; } }

/* The sticky retract needs motion and something to drive it: JS, or a CSS scroll timeline. */
html.js .tv-hero { height: calc(100vh + var(--tv-travel)); height: calc(100svh + var(--tv-travel)); }
@supports (animation-timeline: view()) {
  html:not(.js) .tv-hero { height: calc(100vh + var(--tv-travel)); height: calc(100svh + var(--tv-travel)); }
}

.tv-stage {
  --tv-hdr: var(--header-offset, calc(clamp(56px, 3.75rem, 80px) + env(safe-area-inset-top, 0px)));
  --tv-hdr-end: var(--tv-hdr);
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  overflow: clip;
  container: tv-stage / size;
  isolation: isolate;
}
@media (max-height: 520px) {
  .tv-stage { --tv-hdr: var(--header-offset, calc(48px + env(safe-area-inset-top, 0px))); }
}
@media (max-height: 520px) {
  /* the site header hides on scroll, so the retracted frame takes the top (the still stays under the header) */
  html.js:not(.motion-off) .tv-stage { --tv-hdr-end: 0px; }
}

/* The geometry, declared on every child of the stage so container units and per-composition values resolve
   there. JS writes the same numbers inline (and animates them); these drive the no-JS page. */
.tv-stage > * {
  --tv-title-size: clamp(2.25rem, min(22cqw, 17cqh), 13rem);
  --tv-title-h: calc(var(--tv-title-size) * .86);              /* one line at line-height .86 */
  --tv-gap: clamp(12px, 2.4cqh, 28px);
  --tv-margin: clamp(10px, 2.2cqh, 36px);
  --tv-rest: max(var(--tv-edge), 17cqh);                       /* title baseline box above the stage bottom */
  --tv-below: 0px;                                             /* content hung under the window at rest */
  --tv-fx: .012;                                               /* art share cropped at the left */
  --tv-h0: min(100cqw / 2.39, (100cqh - var(--tv-hdr)) / 1.18);                  /* the gate: Scope */
  --tv-free: max(0px, 100cqh - var(--tv-hdr) - var(--tv-h0));
  --tv-above0: max(0px, (var(--tv-free) - var(--tv-below)) / 2);
  --tv-bot0: calc(var(--tv-free) - var(--tv-above0));
  --tv-top: calc(var(--tv-hdr-end) + (var(--tv-hdr) - var(--tv-hdr-end) + var(--tv-above0)) * var(--tv-lb));
  --tv-bot: calc(var(--tv-bot0) * var(--tv-lb));
  --tv-wh: calc(100cqh - var(--tv-top) - var(--tv-bot));
  --tv-dw: max(min(100cqw, 2.6 * var(--tv-wh)), 2.1388 * var(--tv-wh));
  --tv-rise0: calc(var(--tv-rest) - var(--tv-margin) - var(--tv-bot0));   /* inside the window's bottom edge */
}
@container tv-stage (min-width: 30rem) {
  .tv-stage > * { --tv-title-size: clamp(2.25rem, min(10cqw, 17cqh), 13rem); }
}
@container tv-stage (min-width: 60rem) {   /* … and the title ends before the probe (≈ 46% across the frame) */
  .tv-stage > * { --tv-title-size: clamp(2.25rem, min(7cqw, 17cqh, (46cqw - var(--tv-gutter)) / 6.1), 13rem); }
}
/* Narrower than 1.55:1 (3:2, 4:3, square, portrait) the frame holds the probe and the portal large, so the title
   rests low, under the probe, and while letterboxed it hangs just under the window, the pair centred. A Scope strip
   would be a sliver here, so the gate grows with the height (up to Flat 1.85:1, or 4:5 in portrait) and the bars
   stay about a tenth of the screen each. */
@container tv-stage (max-aspect-ratio: 31/20) {
  .tv-stage > * {
    --tv-fx: calc(.45 - .438 * var(--tv-lb));   /* the whole art at rest, panning to the probe and portal */
    --tv-rest: max(var(--tv-edge), 7.5cqh);
    --tv-below: calc(var(--tv-title-h) + var(--tv-gap));
    --tv-rise0: calc(var(--tv-rest) + var(--tv-below) - var(--tv-bot0));
    --tv-h0: clamp(100cqw / 2.39, (100cqh - var(--tv-hdr) - var(--tv-below)) * .76, 100cqw / 1.85);
  }
}
@container tv-stage (max-aspect-ratio: 4/5) {
  .tv-stage > * {
    --tv-fx: .45;
    --tv-h0: clamp(100cqw / 2.39, (100cqh - var(--tv-hdr) - var(--tv-below)) * .76, 100cqw / .8);
  }
}
@container tv-stage (max-aspect-ratio: 31/20) and (min-width: 30rem) {
  .tv-stage > * { --tv-title-size: clamp(2.25rem, min(9cqw, 12cqh), 13rem); }
}
@container tv-stage (max-aspect-ratio: 4/5) and (min-width: 30rem) {
  .tv-stage > * { --tv-title-size: clamp(2.25rem, min(14cqw, 12cqh), 13rem); }
}

/* ---------- frame: starfield, art, canvas, veil ---------- */
.tv-frame { position: absolute; inset: 0; }

.tv-stars {                    /* CSS starfield: the extension zone when there is no WebGL */
  position: absolute;
  inset: 0;
  background:
    var(--tv-starfield),
    radial-gradient(ellipse 34% 30% at 22% 52%, rgba(40, 150, 170, .16), transparent 72%),
    radial-gradient(ellipse 60% 45% at 8% 60%, rgba(30, 70, 150, .14), transparent 75%),
    var(--tv-ground);
}

.tv-art {
  --tv-unletter: linear-gradient(90deg, transparent 49%, #000 52%), linear-gradient(transparent 37%, #000 40%);   /* below */
  position: absolute;
  inset: 0;
  display: block;
}
.tv-art img {
  position: absolute;
  display: block;
  max-width: none;
  width: calc(var(--tv-dw) * 1.024);
  height: auto;
  left: calc(50cqw + (clamp(100cqw - var(--tv-dw), -1 * var(--tv-fx) * var(--tv-dw), 0px) - 50cqw) * 1.024);
  top: calc(var(--tv-top) + var(--tv-wh) / 2 + (.3 * (var(--tv-wh) - var(--tv-dw) / 2.1388) - var(--tv-wh) / 2) * 1.024);
  transform-origin: 0 0;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 var(--tv-fade, 0%));
  mask-image: linear-gradient(90deg, transparent, #000 var(--tv-fade, 0%));
}
@container tv-stage (min-aspect-ratio: 21/10) {
  html:not(.js) .tv-art img { --tv-fade: 5%; }   /* the art fades into the starfield it no longer covers */
}
.js .tv-art img { will-change: transform; }

/* When the frame cuts the art's own lettering (portrait frames; narrow ones once the caption has taken over), what
   is left of it is masked out of the picture, so no fragment of a letter hangs at the frame's edge before the shader
   dissolves it (or without the shader). The masked corner (left of 49%, above 37% of the art) holds only the
   lettering: the probe and the ring sit clear of it. */
.tv-hero.is-captioned .tv-art img {
  -webkit-mask-image: var(--tv-unletter);
  mask-image: var(--tv-unletter);
}
@container tv-stage (max-aspect-ratio: 4/5) {
  .tv-art img {
    -webkit-mask-image: var(--tv-unletter);
    mask-image: var(--tv-unletter);
  }
}

.tv-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}
.tv-canvas[hidden] { display: none; }

.tv-veil {                     /* lens vignette + scrim under the title; shared by the picture and the canvas */
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 70% 55% at 0% 92%, rgba(2, 5, 13, .78), rgba(2, 5, 13, .35) 50%, transparent 78%),
    linear-gradient(0deg, rgba(2, 5, 13, .5), transparent 30%),
    radial-gradient(ellipse 120% 95% at 58% 50%, transparent 55%, rgba(2, 5, 13, .5));
}
@container tv-stage (max-aspect-ratio: 4/5) {
  .tv-veil {                   /* + a short scrim under the caption at the top */
    background:
      linear-gradient(180deg, rgba(2, 5, 13, .6), transparent calc(var(--tv-hdr) + 7rem)),
      linear-gradient(0deg, rgba(2, 5, 13, .9), rgba(2, 5, 13, .5) 18%, transparent 34%),
      radial-gradient(ellipse 130% 80% at 50% 45%, transparent 50%, rgba(2, 5, 13, .5));
  }
}

/* ---------- letterbox: bars and gate ---------- */
.tv-bar {                      /* the frame floats in deep space: dim stars that hold still while the bars slide */
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 100cqh;
  overflow: hidden;
  background: var(--tv-ground);
  pointer-events: none;
  transform: translateY(var(--tv-bar-y));
}
.tv-bar::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--tv-starfield);
  opacity: .45;
  transform: translateY(calc(-1 * var(--tv-bar-y)));
}
.tv-bar--top { --tv-bar-y: calc(var(--tv-top) - 100cqh); }
.tv-bar--bottom { --tv-bar-y: calc(100cqh - var(--tv-bot)); }
.js .tv-bar, .js .tv-bar::before { will-change: transform; }

/* gate: hairline corner marks riding the window edges */
.tv-gate {
  position: absolute;
  left: calc(var(--tv-edge) * .5);
  right: calc(var(--tv-edge) * .5);
  height: 0;
  pointer-events: none;
  opacity: var(--tv-lb);
}
.tv-gate--top { top: 0; transform: translateY(calc(var(--tv-top) + 10px)); }
.tv-gate--bottom { bottom: 0; transform: translateY(calc(-1 * var(--tv-bot) - 10px)); }
.tv-gate::before,
.tv-gate::after {
  content: "";
  position: absolute;
  width: clamp(10px, 1.6cqmin, 22px);
  height: clamp(10px, 1.6cqmin, 22px);
  border: 0 solid rgba(56, 225, 210, .55);
}
.tv-gate--top::before { left: 0; top: 0; border-width: 1px 0 0 1px; }
.tv-gate--top::after { right: 0; top: 0; border-width: 1px 1px 0 0; }
.tv-gate--bottom::before { left: 0; bottom: 0; border-width: 0 0 1px 1px; }
.tv-gate--bottom::after { right: 0; bottom: 0; border-width: 0 1px 1px 0; }

/* ---------- type ---------- */
.tv-mono {
  font-family: var(--tv-mono);
  font-size: clamp(var(--t-mono, .72rem), .35rem + .6cqmin, 1.25rem);   /* grows on large screens */
  font-weight: 400;
  letter-spacing: .2em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  line-height: 1.3;
  margin: 0;
}

/* credit: in the top bar, riding the window's top edge while the art's own lettering is in frame. Its slot passes
   to the caption when the frame cuts the lettering (portrait, and narrow frames as they open). */
.tv-credit {
  position: absolute;
  left: var(--tv-gutter);
  right: var(--tv-gutter);
  top: 0;
  max-width: none;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: .25rem 1rem;
  color: var(--tv-text-2);
  transform: translateY(max(var(--tv-top) - 100% - var(--tv-gap), var(--tv-hdr) + 8px));
  opacity: clamp(0, (var(--tv-lb) - .85) * 8, 1);   /* gone while the frame has barely moved, never over the art */
  text-shadow: 0 1px 12px rgba(2, 5, 13, .9);
}
.tv-credit span:last-child { color: var(--tv-teal); }
@container tv-stage (max-aspect-ratio: 4/5) {
  .tv-credit { opacity: 0; }   /* the caption holds the slot (timeverse.js agrees: the lettering is never in frame) */
}

.tv-titles {
  position: absolute;
  left: var(--tv-gutter);
  right: var(--tv-gutter);
  bottom: calc(var(--tv-rest) + env(safe-area-inset-bottom, 0px));
  transform: translateY(calc(var(--tv-rise0) * var(--tv-lb)));
}
.js .tv-titles { will-change: transform; }

.tv-title {
  margin: 0;
  /* narrow stages set the title at wdth 100, the width the title fallbacks are matched to */
  font-family: 'Anybody', 'Anybody Title Fallback', 'Anybody Title Fallback R', var(--tv-display);
  font-weight: 200;
  font-stretch: 100%;
  font-size: var(--tv-title-size);
  line-height: .86;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--tv-text);
  text-shadow: 0 0 .5em rgba(2, 5, 13, .55), 0 0 1.4em rgba(56, 225, 210, .12);
  overflow-wrap: normal;       /* one short word: it never breaks inside, even while it tracks in */
  hyphens: manual;
}
@container tv-stage (min-width: 30rem) { .tv-title { font-stretch: 125%; font-family: var(--tv-display); } }
@container tv-stage (min-width: 60rem) { .tv-title { font-stretch: 150%; } }
@container tv-stage (max-aspect-ratio: 31/20) and (min-width: 30rem) { .tv-title { font-stretch: 125%; } }

/* "Into the Timeverse": takes the place of the art's own lettering when the frame cuts it. It rides the window's
   top edge in the bar, then stays just inside the open frame. */
.tv-caption {
  position: absolute;
  left: var(--tv-gutter);
  right: var(--tv-gutter);
  top: 0;
  max-width: none;
  transform: translateY(max(var(--tv-top) - 100% - var(--tv-gap), var(--tv-hdr-end) + var(--tv-gap)));
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .35em .9em;
  color: var(--tv-text-2);
  opacity: 0;
  transition: opacity 640ms var(--tv-ease);
  text-shadow: 0 1px 12px rgba(2, 5, 13, .9), 0 0 3px rgba(2, 5, 13, .7);
}
.tv-caption cite {
  font-family: var(--tv-display);
  font-style: normal;
  font-weight: 300;
  font-stretch: 112%;
  font-size: clamp(1rem, .85rem + .6cqmin, 1.6rem);
  letter-spacing: .02em;
  text-transform: none;
  color: var(--tv-text);
}
.tv-hero.is-captioned .tv-caption { opacity: 1; }
@container tv-stage (max-aspect-ratio: 4/5) {
  .tv-caption { opacity: 1; }   /* portrait frames never hold the lettering */
}
@container tv-stage (4/5 < aspect-ratio <= 31/20) {   /* no JS, square: once the pan has taken the lettering out */
  html:not(.js) .tv-caption { opacity: clamp(0, (.15 - var(--tv-lb)) * 10, 1); transition: none; }
}

/* live aspect readout and scroll cue: in the bottom bar, riding the window's bottom edge; an instrument of the
   retract, so it fades as the frame finishes opening and is absent from the still */
.tv-readout {
  position: absolute;
  right: var(--tv-gutter);
  bottom: 0;
  max-width: none;
  display: flex;
  align-items: center;
  gap: 1.1em;
  color: var(--tv-text-2);
  transform: translateY(calc(-1 * max(var(--tv-bot) - 100% - var(--tv-gap), max(var(--tv-edge) * .5, 12px))));
  text-shadow: 0 1px 12px rgba(2, 5, 13, .9);
}
@container tv-stage (max-aspect-ratio: 4/5) {
  .tv-readout { transform: translateY(calc(-1 * max(var(--tv-edge) * .5, 12px))); }   /* the title owns the edge */
}
html:not(.js) .tv-readout { display: none; }   /* only JS measures the real window */
.tv-readout b { font-weight: 500; color: var(--tv-text); }
.tv-cue { display: inline-flex; align-items: center; gap: .7em; }
.tv-cue-trace {                /* a short trace with a packet running along it */
  position: relative;
  width: clamp(28px, 4cqw, 64px);
  height: 1px;
  overflow: hidden;
  background: linear-gradient(90deg, rgba(56, 225, 210, .5), rgba(56, 225, 210, .1));
}
.tv-cue-trace::after {         /* the packet: the dot sits at the right end and the whole layer slides (compositor only) */
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle, var(--tv-teal) 0 1.5px, transparent 2px) 100% 50% / 6px 6px no-repeat;
  transform: translateX(-100%);
  animation: tv-cue 2.4s var(--tv-ease) infinite;
}
@keyframes tv-cue {
  from { transform: translateX(-100%); }
  to { transform: translateX(6px); }
}
/* it rests once the cue has faded or the hero is off-screen (timeverse.js); reduced motion / Motion: Off hide the
   whole readout, so nothing runs there */
.tv-hero.is-cue-idle .tv-cue-trace::after { animation-play-state: paused; }

/* ---------- entrance (JS + motion only; the h1 is never hidden for long) ----------
   First light: the art exposes up from near-black like a projector lamp warming (the picture paints at once, so it
   stays the LCP), the title tracks in, then timeverse.js ignites the probe's lamp. */
html.js:not(.motion-off) .tv-art img { animation: tv-expose 1.8s cubic-bezier(.3, 0, .2, 1) .05s backwards; }
html.js:not(.motion-off) .tv-title { animation: tv-title-in 1.6s var(--tv-ease) .15s backwards; }
html.js:not(.motion-off) .tv-credit,
html.js:not(.motion-off) .tv-readout > span:last-child { animation: tv-fade-in 1.2s var(--tv-ease) .9s backwards; }
@keyframes tv-expose {
  from { filter: brightness(.12) saturate(.5); }
}
@keyframes tv-title-in {
  from { opacity: .35; letter-spacing: .3em; filter: blur(10px); }
  to { opacity: 1; letter-spacing: .03em; filter: blur(0); }
}
@keyframes tv-fade-in { from { opacity: 0; } }

/* ---------- no-JS: a CSS scroll timeline retracts the letterbox where supported ----------
   The section's own view timeline: its "contain" range is exactly the stage's sticky travel. The inset is zeroed:
   "auto" would subtract the page's scroll-padding (the header) and start the retract before the first scroll. */
@supports (animation-timeline: view()) {
  html:not(.js) .tv-hero { view-timeline: --tv-scroll block; view-timeline-inset: 0px; }
  html:not(.js) .tv-stage {
    animation: tv-retract linear both;
    animation-timeline: --tv-scroll;
    animation-range: contain 0% contain 100%;
  }
}
@keyframes tv-retract {
  from { --tv-lb: 1; }
  85%, to { --tv-lb: 0; }
}

@media (forced-colors: active) {
  .tv-veil, .tv-gate, .tv-canvas { display: none; }
  .tv-title { text-shadow: none; }
}
@media (prefers-contrast: more) {
  .tv-veil { background: linear-gradient(0deg, rgba(2, 5, 13, .92), rgba(2, 5, 13, .6) 35%, transparent 60%); }
  .tv-title { text-shadow: none; }
}
@media print {
  .tv-hero { height: auto; }
  .tv-stage { position: relative; height: 70vh; }
  .tv-canvas, .tv-bar, .tv-gate, .tv-readout { display: none; }
}

/* ==========================================================================
   .cinemascope — reusable letterbox (class API). js/timeverse.js sets --letterbox (1 = bars closed to 2.39:1,
   0 = open) from the element's scroll position, and --cs-bar (the bar height at 2.39:1) from its size.
   ========================================================================== */
@property --letterbox {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;
}
.cinemascope { position: relative; isolation: isolate; }
.cinemascope::before,
.cinemascope::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  z-index: 2;
  height: var(--cs-bar, 12.5%);
  background: var(--cs-bar-color, #02050D);
  pointer-events: none;
  transform: scaleY(var(--letterbox));
}
.cinemascope::before { top: 0; transform-origin: 50% 0; }
.cinemascope::after { bottom: 0; transform-origin: 50% 100%; }
@supports (animation-timeline: view()) {
  html:not(.js) .cinemascope {
    animation: cs-open linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 45%;
  }
}
@keyframes cs-open { from { --letterbox: 1; } to { --letterbox: 0; } }
