/* roll.css — channel-change garnish:
 *   #black   — a 200ms hard cut to black before every channel change (real TVs
 *              blank the screen while they retune).
 *   #roll    — a sub-100ms "calibration" snap on the INSTANT (warm) switch: the
 *              picture briefly overscans/jumps then locks, with a quick tear flash
 *              and the OSD text twitching, like a set re-locking the image.
 * All transform-only → GPU-composited. */

#black {
  position: absolute;
  inset: 0;
  z-index: 10;                 /* above EVERYTHING (picture, snow, noise, OSD bars) so
                                  the whole screen blacks out together on a channel change */
  display: none;
  background: #000;
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.12s linear;
}
#black.fade { opacity: 0; }
/* organic fade INTO black (with the scan over it) rather than a hard snap */
#black.cutin { animation: black-in var(--sw-cutin, 70ms) linear; }
@keyframes black-in { from { opacity: 0; } to { opacity: 1; } }

/* --- calibration snap (instant switch) --- */
#roll {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: none;
  overflow: hidden;
  pointer-events: none;
}
#roll .tear {
  position: absolute;
  left: 0; right: 0;
  height: 14%;
  background: linear-gradient(to bottom,
    rgba(255,255,255,0) 0%,
    rgba(255,255,255,0.22) 45%,
    rgba(0,0,0,0.35) 55%,
    rgba(255,255,255,0) 100%);
  will-change: transform, opacity;
}
#roll.go .tear { animation: tear-flick var(--sw-calib, 70ms) steps(3) 1; }

/* sustained "tuning" state: #roll just stays visible (for the tear flashes). NO extra
   scanline layer here — that used to stack a second, darker rolling scanline over the
   constant #scanlines, so the scanlines looked stronger while tuning. The scanlines are now
   ONE constant layer (#scanlines) in every state; tuning is conveyed by the picture hop, the
   tear flashes, and the per-hop NOISE variation. */
#roll.scan { display: block; }

/* while tuning, the PICTURE (not the UI) jitters VERTICALLY like a CRT hunting for
   vertical lock — Y axis only, never diagonal */
/* scale(1.04) overscan so the vertical hunt never reveals the frame's black edges */
#players.tuning { animation: tune-jitter 0.16s steps(2) infinite; }
@keyframes tune-jitter {
  0%   { transform: translateY(0) scale(1.04); }
  25%  { transform: translateY(2px) scale(1.04); }
  50%  { transform: translateY(-3px) scale(1.04); }   /* vertical "hunting" jump */
  75%  { transform: translateY(1px) scale(1.04); }
  100% { transform: translateY(-1px) scale(1.04); }
}
@keyframes tear-flick {
  0%   { transform: translateY(8%);  opacity: 0.9; }
  60%  { transform: translateY(64%); opacity: 0.7; }
  100% { transform: translateY(118%); opacity: 0; }
}

/* the live picture briefly overscans/jumps then locks (wrapper transform, so it
   doesn't fight the retro-mode transform on the iframe child) */
.pslot.live.calib { animation: calib-lock var(--sw-calib, 70ms) steps(3) 1; }
@keyframes calib-lock {
  0%   { transform: translateY(-1.6%) scaleY(1.06); }
  40%  { transform: translateY(0.9%)  scaleY(0.985); }
  72%  { transform: translateY(-0.4%) scaleY(1.012); }
  100% { transform: translateY(0)     scaleY(1); }
}

/* one-shot vertical shudder of the PICTURE on a calibration snap (UI stays put) */
#players.calib { animation: v-jitter calc(var(--sw-calib, 70ms) / 2) steps(2) 2; }
@keyframes v-jitter {
  0%   { transform: translateY(0); }
  25%  { transform: translateY(2px); }
  50%  { transform: translateY(-2px); }
  75%  { transform: translateY(1px); }
  100% { transform: translateY(0); }
}

/* settle: a damped vertical wobble easing to rest when tuning ends (jitter→stable) */
#players.settle { animation: v-settle 200ms ease-out 1; }
@keyframes v-settle {
  0%   { transform: translateY(-3px); }
  30%  { transform: translateY(2px); }
  55%  { transform: translateY(-1px); }
  78%  { transform: translateY(0.5px); }
  100% { transform: translateY(0); }
}

/* TOUCH (tablets/phones): shaking the PICTURE means transforming the cross-origin video
   iframe every frame, which forces a per-frame re-composite that glitches on tablet Chrome.
   Skip all picture-shake there — the rolling-scan / digital-macroblock OVERLAYS (which don't
   touch the video layer) still play, so the tuning still reads as "acquiring". */
@media (pointer: coarse) {
  #players.tuning, #players.calib, #players.settle { animation: none !important; transform: none !important; }
  .pslot.live.calib { animation: none !important; }
}
