/* RetroVision Cable: the room photo scene + the composited CRT screen */

:root {
  --osd-green: #35e05a;
  --led-green: #46ff7c;
  --led-amber: #ffb54a;
  --accent: #ffb54a;
  /* the TV glass quad, as fractions of the room photo (1938 x 811):
     measured x 596..1334, y 97..488 */
  --glass-l: 30.75%;
  --glass-t: 11.96%;
  --glass-w: 38.08%;
  --glass-h: 48.21%;
  --photo-ratio: calc(1938 / 811);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  min-height: 100vh;
  background: #05040a;
  font-family: 'Archivo', system-ui, sans-serif;
  overflow: hidden;
  overscroll-behavior: none;
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
  -webkit-text-size-adjust: 100%;
}
body { user-select: none; -webkit-user-select: none; }

/* a soft, blurred copy of the room behind everything, so no screen shape
   ever shows bare black around the scene */
#roomfill {
  position: fixed;
  inset: -8%;
  z-index: 0;
  background: #05040a url(../img/room.webp) center 38% / cover no-repeat;
  filter: blur(26px) brightness(0.5) saturate(1.15);
  pointer-events: none;
}

/* ============ SCENE ============ */

#scene {
  position: relative;
  width: 100%;
  height: 100vh;
  height: 100dvh;
  padding: 0 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  overflow: hidden;
  z-index: 1;
  touch-action: none;
}

/* The stage keeps the photo's aspect so the screen quad stays glued on.
   The photo is ultrawide, so on normal screens it covers the viewport and
   crops the room's outer edges; the TV sits dead center and never crops. */
#stage {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  aspect-ratio: 1938 / 811;
  width: max(100vw, calc(100dvh * var(--photo-ratio)));
  container-type: inline-size;
  transition: transform 0.7s cubic-bezier(.4,0,.2,1);
}

/* portrait: the set nearly spans the width at the top, the room fades into
   the blurred backdrop above and below, and the remote lives underneath */
@media (max-aspect-ratio: 1 / 1) {
  #stage {
    top: calc(env(safe-area-inset-top, 0px) + 1.5vh);
    transform: translateX(-50%);
    width: 212vw;
    transition: top 0.65s cubic-bezier(.22,1,.36,1), transform 0.65s cubic-bezier(.22,1,.36,1);
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 7%, #000 84%, transparent 97%);
    mask-image: linear-gradient(to bottom, transparent 0%, #000 7%, #000 84%, transparent 97%);
  }
  /* remote tucked away: the set glides to the middle of the space above the
     peeking remote (46% of the stage height is the middle of the TV and stand) */
  body.remote-down #stage {
    top: calc((100dvh - 5.6 * min(4.3vw, 17px)) / 2 - 40.8vw);
  }
}

#roombg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}

/* ============ THE SCREEN, ON THE TUBE ============ */

#screen {
  position: absolute;
  left: var(--glass-l);
  top: var(--glass-t);
  width: var(--glass-w);
  height: var(--glass-h);
  background: #0a0d10;
  border-radius: 3.2% / 5%;
  overflow: hidden;
  box-shadow:
    inset 0 0 40px rgba(0,0,0,0.85),
    inset 0 0 6px rgba(0,0,0,0.9);
  cursor: pointer;
}

/* when the set is off, let the photo's pale dead tube glow through a bit */
body:not(.tv-on) #screen { background: #0d1216; }

/* everything the tube draws lives in here, with js/crt.js scanlines laid over it */
#tube {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  overflow: hidden;
}

body.tv-on #screen {
  box-shadow:
    inset 0 0 40px rgba(0,0,0,0.8),
    inset 0 0 6px rgba(0,0,0,0.9),
    0 0 60px 6px rgba(120, 170, 255, 0.13),
    0 0 140px 30px rgba(120, 170, 255, 0.07);
  animation: screenspill 4.2s infinite;
}
@keyframes screenspill {
  0%, 100% { filter: brightness(1); }
  8% { filter: brightness(1.03); }
  9% { filter: brightness(0.98); }
  10% { filter: brightness(1.01); }
  52% { filter: brightness(1.02); }
  83% { filter: brightness(0.99); }
}

/* Oversized and shifted UP so YouTube's title bar and chrome sit above the
   visible tube, and the watermark corner below it. CRTs overscanned anyway. */
#ytwrap {
  position: absolute;
  inset: 0;
  overflow: hidden;
  transition: all 0.35s ease;
}
/* size and scale are set by fitVideo() in player.js */
#ytwrap iframe,
#ytwrap #yt {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  transform-origin: 0 0;
  border: 0;
  pointer-events: none;
  background: #000;
}

/* Scaled down, the embed's center button is a few pixels wide at dead center of
   the player. A frosted spot sized to that button (--dot-w, set by fitVideo)
   covers it only while the embed is drawing it: from each start, stall or
   resume until a few seconds of steady play (#screen.dot-on, player.js).
   Never left on permanently: viewers could see a standing spot and hated it. */
#ytdot {
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--dot-w, 2.4%);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  -webkit-mask-image: radial-gradient(circle, #000 52%, transparent 72%);
  mask-image: radial-gradient(circle, #000 52%, transparent 72%);
  /* At 1x device scale, Chromium skips a backdrop-filter element that paints
     nothing of its own when its backdrop is the cross-origin video frame: the
     button stayed crisp under a bare spot of any size, and vanished once the
     spot painted this invisible 1% tint (checked in a real headed browser;
     1.25x and 2x render the bare spot fine, and headless never shows it). */
  background: rgba(0, 0, 0, 0.01);
  pointer-events: none;
  z-index: 1;
  opacity: 0;
}
#screen.dot-on #ytdot { opacity: 1; }

/* when a phone insists on a real tap, let it reach the player itself */
#screen.tap-through #ytwrap iframe { opacity: 1 !important; pointer-events: auto; }
#screen.tap-through #staticfx { opacity: 0 !important; }

/* powered off: the player's own idle art (a red play button) must never show */
body:not(.tv-on) #ytwrap { visibility: hidden; }

/* guide mode shrinks live tv into the top-right quadrant */
.guide-active #ytwrap {
  inset: 0 0 55% 59%;
  z-index: 3;
}

#weathercanvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 2;
}

/* ============ CRT OVERLAYS ============ */

#staticfx {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 6;
  opacity: 0;
  pointer-events: none;
}

/* While a video is held back for its YouTube overlay, the guide and weather
   screens stay readable: only the little picture-in-picture is hidden. */
#guidewrap:not([hidden]) ~ #staticfx.held,
#weathercanvas:not([hidden]) ~ #staticfx.held { opacity: 0 !important; }
#screen.yt-held #ytwrap iframe { opacity: 0; }

/* The tube look is layers js/crt.js fills per style (SMOOTH / CLASSIC /
   VIVID / OFF, picked in the setup menu): #glow is a backdrop-filter halation
   pass, #grille a dot-mask tile multiplied over the picture, #scanlines the
   line tile blended soft-light. Blend modes, color filters and backdrop-filter
   are the only operations the browser runs on the cross-origin video frame;
   SVG filter math on it was measured and silently does nothing. */
body.tv-on #tube { background: #05070a; }

#glow { position: absolute; inset: 0; z-index: 7; pointer-events: none; display: none; }
#grille { position: absolute; inset: 0; z-index: 8; pointer-events: none; display: none; }
#scanlines {
  position: absolute;
  inset: 0;
  z-index: 8;
  pointer-events: none;
}
body.crt-off #vignette { display: none; }

/* the tube's curve: corners fall away and the edge sinks into the bezel */
#vignette {
  position: absolute;
  inset: 0;
  z-index: 9;
  pointer-events: none;
  border-radius: inherit;
  background: radial-gradient(118% 128% at 50% 50%, transparent 64%, rgba(0,0,0,0.42) 100%);
  box-shadow:
    inset 0 0 1.2cqw 0.25cqw rgba(0,0,0,0.7),
    inset 0 0 0.25cqw 0.06cqw rgba(0,0,0,0.85);
}

#rollbar {
  position: absolute;
  left: 0; right: 0;
  height: 14%;
  z-index: 6;
  pointer-events: none;
  background: linear-gradient(180deg, transparent, rgba(255,255,255,0.035), transparent);
  animation: rollbar 9s linear infinite;
  opacity: 0;
}
body.tv-on #rollbar { opacity: 1; }
@keyframes rollbar { from { top: 110%; } to { top: -20%; } }

/* ============ OSD ============ */

#osd {
  position: absolute;
  inset: 0;
  z-index: 10;
  pointer-events: none;
  font-family: 'Jersey 15', 'Archivo', sans-serif;
}

#osd-ch {
  position: absolute;
  top: 5%;
  right: 6%;
  font-size: clamp(14px, 3.4cqw, 52px);
  color: var(--osd-green);
  text-shadow: 0 0 12px rgba(53,224,90,0.8), 2px 2px 0 rgba(0,60,0,0.7);
  letter-spacing: 0.06em;
  opacity: 0;
  transition: opacity 0.25s;
}
#osd-ch.show { opacity: 1; }

#osd-vol {
  position: absolute;
  left: 6%;
  bottom: 8%;
  right: 6%;
}
#osd-vol-label {
  color: var(--osd-green);
  font-size: clamp(8px, 1.7cqw, 24px);
  text-shadow: 0 0 10px rgba(53,224,90,0.8);
  letter-spacing: 0.1em;
}
#osd-vol-bars {
  margin-top: 6px;
  display: flex;
  gap: 4px;
  height: clamp(8px, 1.6vh, 16px);
}
#osd-vol-bars span {
  flex: 1;
  background: rgba(53,224,90,0.25);
  box-shadow: 0 0 6px rgba(53,224,90,0.3);
}
#osd-vol-bars span.lit {
  background: var(--osd-green);
  box-shadow: 0 0 10px rgba(53,224,90,0.9);
}

#osd-banner {
  position: absolute;
  left: 5%;
  top: 5%;
  max-width: 64%;
  color: var(--osd-green);
  font-size: clamp(8px, 1.5cqw, 22px);
  line-height: 1.25;
  text-shadow: 0 0 10px rgba(53,224,90,0.75), 1px 1px 0 rgba(0,60,0,0.7);
  letter-spacing: 0.05em;
  white-space: pre-line;
}

#osd-mute {
  position: absolute;
  right: 6%;
  bottom: 8%;
  color: var(--osd-green);
  font-size: clamp(9px, 1.9cqw, 26px);
  text-shadow: 0 0 10px rgba(53,224,90,0.8);
  letter-spacing: 0.14em;
  animation: mutepulse 1.4s infinite;
}
@keyframes mutepulse { 0%,100% { opacity: 1; } 50% { opacity: 0.35; } }

#osd-prompt {
  position: absolute;
  left: 50%;
  bottom: 12%;
  transform: translateX(-50%);
  padding: 0.35em 0.8em;
  border-radius: 3px;
  background: rgba(0, 0, 0, 0.55);
  color: var(--osd-green);
  font-size: clamp(11px, 2.2cqw, 28px);
  letter-spacing: 0.12em;
  white-space: nowrap;
  text-shadow: 0 0 10px rgba(53,224,90,0.8);
  animation: mutepulse 1.6s infinite;
}

#chbug {
  position: absolute;
  right: 5.5%;
  bottom: 6.5%;
  z-index: 9;
  pointer-events: none;
  font-family: 'Archivo', sans-serif;
  font-weight: 900;
  font-size: clamp(6px, 1cqw, 14px);
  letter-spacing: 0.12em;
  color: rgba(255,255,255,0.4);
  text-shadow: 0 1px 3px rgba(0,0,0,0.8);
  opacity: 0;
  transition: opacity 1s;
}
.guide-active #osd { opacity: 0; }

/* ============ POWER STATES ============ */

#poweroff-fx {
  position: absolute;
  inset: 0;
  z-index: 12;
  background: #000;
  pointer-events: none;
}
#poweroff-fx::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 50%;
  height: 2px;
  transform: translateY(-50%);
  background: #fff;
  box-shadow: 0 0 24px 6px rgba(255,255,255,0.9);
  animation: tubecollapse 0.45s ease-out forwards;
}
@keyframes tubecollapse {
  0% { left: 0; right: 0; opacity: 1; }
  70% { left: 49.5%; right: 49.5%; opacity: 1; }
  100% { left: 50%; right: 50%; opacity: 0; }
}

#standby {
  position: absolute;
  inset: 0;
  z-index: 11;
  background: radial-gradient(85% 85% at 50% 45%, #161e26 0%, #06090c 80%);
  display: flex;
  align-items: center;
  justify-content: center;
}
body.tv-on #standby { display: none; }

#standby-inner { text-align: center; }
#standby-logo {
  font-family: 'Jersey 15', sans-serif;
  font-size: clamp(14px, 3.2cqw, 52px);
  color: #cfd6e4;
  letter-spacing: 0.08em;
  text-shadow: 0 0 18px rgba(150,180,255,0.35);
}
#standby-logo span { color: var(--accent); }
#standby-hint {
  margin-top: 8%;
  font-family: 'Jersey 15', sans-serif;
  font-size: clamp(8px, 1.7cqw, 24px);
  color: var(--osd-green);
  letter-spacing: 0.2em;
  text-shadow: 0 0 12px rgba(53,224,90,0.7);
  animation: mutepulse 1.8s infinite;
}

/* ============ VCR READOUT ============ */

/* sits in the JVC's display window, to the right of the printed PLAY */
#vcrled {
  position: absolute;
  left: 43.2%;
  top: 76.2%;
  width: 7.9%;
  height: 3.2%;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6%;
  pointer-events: none;
  font-family: 'Jersey 15', sans-serif;
  font-size: clamp(6px, 0.95cqw, 16px);
  letter-spacing: 0.05em;
  white-space: nowrap;
}
#vcr-ch { color: var(--led-green); text-shadow: 0 0 8px rgba(70,255,124,0.8); }
#vcr-year { color: var(--led-amber); text-shadow: 0 0 8px rgba(255,181,74,0.75); }
body:not(.tv-on) #vcr-ch { color: #1e2a1e; text-shadow: none; }
body:not(.tv-on) #vcr-year { color: #2a2114; text-shadow: none; }

/* ============ ZOOM ============ */

/* --zs/--zx/--zy come from computeZoom() in main.js: the tube is scaled to fill
   the free area around its own center, then slid to the middle of that area */
#stage {
  transform-origin: calc(var(--glass-l) + var(--glass-w) / 2) calc(var(--glass-t) + var(--glass-h) / 2);
}
body.zoomed #stage {
  transform: translate(-50%, -50%) translate(var(--zx, 0px), var(--zy, 0px)) scale(var(--zs, 1.75));
}
@media (max-aspect-ratio: 1 / 1) {
  body.zoomed #stage {
    transform: translateX(-50%) translate(var(--zx, 0px), var(--zy, 0px)) scale(var(--zs, 1.2));
  }
}
