/* ==========================================================================
   CRT / cathode-ray-tube emulation layer
   --------------------------------------------------------------------------
   A layered CSS + SVG-filter stack that sits over the live terminal DOM, so
   text stays sharp and links/chips stay clickable.

   Everything gates on `body.crt-on`. The active phosphor mask is chosen with
   `body.crt--aperture | crt--shadow | crt--slot`. Tune it live from the
   terminal: `crt ca 0.8`, `crt scan 0.3`, `crt bloom 8`, `crt mask shadow`…
   ========================================================================== */

:root {
  /* Phosphor triad pitch — width of one R+G+B cell. 3px reads on 1x and 2x. */
  --crt-triad: 3px;
  /* Grille strength (how hard the RGB mask bites into lit pixels). */
  --crt-mask: 0.16;

  /* Scanlines. */
  --crt-scan: 0.20;
  --crt-scan-pitch: 3px;

  /* Phosphor bloom (text glow). Tight halo + wide bleed. */
  --crt-bloom: 1px;
  --crt-bloom-wide: 6px;
  /* Ambient halation — the whole glass glowing faintly. */
  --crt-halation: 0.12;

  /* Chromatic aberration offset is set on the SVG filter (see index.html),
     tunable via `crt ca <px>`. This var is just documentation. */
  --crt-ca: 0.6px;

  /* Glass. */
  --crt-vignette: 0.42;
  --crt-vignette-blur: 160px;
  --crt-glare: 0.06;

  /* Brightness compensation — the mask + scanlines darken the picture, so we
     push the phosphors back up a touch. */
  --crt-gain: 1.12;
}

/* --------------------------------------------------------------------------
   Overlay stack — one fixed, click-through layer per effect, above everything
   (including the fullscreen game overlay at z 999).
   -------------------------------------------------------------------------- */
/* The filter-defs SVG must be in the DOM but must never take up space. */
.crt-svg {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.crt {
  position: fixed;
  inset: 0;
  z-index: 2147483000;
  pointer-events: none;
  display: none;
}

body.crt-on .crt {
  display: block;
}

.crt > * {
  position: absolute;
  inset: 0;
}

/* --- Phosphor mask ------------------------------------------------------- */
/* multiply => the grille only shows where the tube emits light (text + glow),
   and disappears into true black, exactly like a real shadow mask. */

/* Aperture grille (Trinitron): continuous vertical R/G/B stripes. */
body.crt--aperture .crt__mask {
  background-image: repeating-linear-gradient(
    90deg,
    rgba(255, 40, 40, var(--crt-mask)) 0,
    rgba(255, 40, 40, var(--crt-mask)) calc(var(--crt-triad) / 3),
    rgba(40, 255, 40, var(--crt-mask)) calc(var(--crt-triad) / 3),
    rgba(40, 255, 40, var(--crt-mask)) calc(var(--crt-triad) / 3 * 2),
    rgba(40, 40, 255, var(--crt-mask)) calc(var(--crt-triad) / 3 * 2),
    rgba(40, 40, 255, var(--crt-mask)) var(--crt-triad)
  );
  mix-blend-mode: multiply;
}

/* Shadow mask: vertical triads broken into short dots by a horizontal grid. */
body.crt--shadow .crt__mask {
  background-image:
    repeating-linear-gradient(
      0deg,
      rgba(0, 0, 0, calc(var(--crt-mask) * 1.4)) 0,
      rgba(0, 0, 0, calc(var(--crt-mask) * 1.4)) 1px,
      transparent 1px,
      transparent var(--crt-triad)
    ),
    repeating-linear-gradient(
      90deg,
      rgba(255, 40, 40, var(--crt-mask)) 0,
      rgba(255, 40, 40, var(--crt-mask)) calc(var(--crt-triad) / 3),
      rgba(40, 255, 40, var(--crt-mask)) calc(var(--crt-triad) / 3),
      rgba(40, 255, 40, var(--crt-mask)) calc(var(--crt-triad) / 3 * 2),
      rgba(40, 40, 255, var(--crt-mask)) calc(var(--crt-triad) / 3 * 2),
      rgba(40, 40, 255, var(--crt-mask)) var(--crt-triad)
    );
  mix-blend-mode: multiply;
}

/* Slot mask: elongated vertical slots (taller cells than the shadow mask). */
body.crt--slot .crt__mask {
  background-image:
    repeating-linear-gradient(
      0deg,
      rgba(0, 0, 0, calc(var(--crt-mask) * 1.4)) 0,
      rgba(0, 0, 0, calc(var(--crt-mask) * 1.4)) 1px,
      transparent 1px,
      transparent calc(var(--crt-triad) * 2)
    ),
    repeating-linear-gradient(
      90deg,
      rgba(255, 40, 40, var(--crt-mask)) 0,
      rgba(255, 40, 40, var(--crt-mask)) calc(var(--crt-triad) / 3),
      rgba(40, 255, 40, var(--crt-mask)) calc(var(--crt-triad) / 3),
      rgba(40, 255, 40, var(--crt-mask)) calc(var(--crt-triad) / 3 * 2),
      rgba(40, 40, 255, var(--crt-mask)) calc(var(--crt-triad) / 3 * 2),
      rgba(40, 40, 255, var(--crt-mask)) var(--crt-triad)
    );
  mix-blend-mode: multiply;
}

/* --- Scanlines ----------------------------------------------------------- */
.crt__scanlines {
  background-image: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, var(--crt-scan)) 0,
    rgba(0, 0, 0, var(--crt-scan)) 1px,
    transparent 1px,
    transparent var(--crt-scan-pitch)
  );
  mix-blend-mode: multiply;
}

/* --- Ambient halation (glass-wide glow) ---------------------------------- */
.crt__bloom {
  background: radial-gradient(
    75% 75% at 50% 45%,
    rgba(126, 231, 135, var(--crt-halation)) 0,
    transparent 70%
  );
  mix-blend-mode: screen;
  opacity: 0.6;
}

/* --- Vignette (tube edges fall off) -------------------------------------- */
/* A radial fade PLUS a big inset shadow, so the darkened edges read even on a
   near-black background where there's little lit content to attenuate. */
.crt__vignette {
  background: radial-gradient(
    120% 120% at 50% 50%,
    transparent 44%,
    rgba(0, 0, 0, 0.10) 74%,
    rgba(0, 0, 0, var(--crt-vignette)) 100%
  );
  box-shadow: inset 0 0 var(--crt-vignette-blur) rgba(0, 0, 0, 0.45);
}

/* --- Glass glare / reflection -------------------------------------------- */
.crt__glare {
  background: radial-gradient(
    60% 40% at 28% 12%,
    rgba(255, 255, 255, var(--crt-glare)) 0,
    transparent 60%
  );
  mix-blend-mode: screen;
}

/* --------------------------------------------------------------------------
   Content treatment — bloom (text-shadow), chromatic aberration (SVG filter),
   and brightness gain applied to the actual text.
   -------------------------------------------------------------------------- */

/* Phosphor glow follows the text colour: green stays green, links bleed blue. */
body.crt-on .terminal__output {
  text-shadow:
    0 0 var(--crt-bloom) currentColor,
    0 0 var(--crt-bloom-wide) currentColor;
}

/* CA + gain only on the output-lines block — deliberately NOT the input row,
   so the blinking cursor doesn't force a filter re-render every frame. */
body.crt-on #output-lines {
  filter: brightness(var(--crt-gain)) contrast(1.03) url(#crt-aberration);
}

/* The cursor is a solid block, not a glyph, so glow it with box-shadow. */
body.crt-on .terminal__cursor {
  box-shadow: 0 0 6px var(--text);
}

/* Game canvas gets a phosphor bloom too (CSS path, when the shader is off). */
body.crt-on .terminal__game canvas {
  filter: drop-shadow(0 0 6px rgba(126, 231, 135, 0.45));
}

/* When the WebGL shader is driving the game, it owns the whole CRT look — so
   suppress the CSS overlay + canvas glow over it to avoid doubling up. */
body.crt-game .crt {
  display: none;
}

body.crt-game .terminal__game canvas {
  filter: none;
}

/* --------------------------------------------------------------------------
   Curved glass (`crt curve on`). The tube's content is bowed by the #crt-warp
   SVG displacement filter; the vignette deepens to sell the rounded edges.
   -------------------------------------------------------------------------- */
#crt-tube {
  width: 100vw;
  height: 100vh;
}

body.crt--curve #crt-tube {
  filter: url(#crt-warp);
}

body.crt--curve .crt__vignette {
  background: radial-gradient(
    122% 120% at 50% 50%,
    transparent 38%,
    rgba(0, 0, 0, 0.12) 72%,
    rgba(0, 0, 0, calc(var(--crt-vignette) + 0.1)) 100%
  );
  box-shadow: inset 0 0 calc(var(--crt-vignette-blur) + 30px) rgba(0, 0, 0, 0.55);
}

/* --- Flicker (subtle luminance instability) ------------------------------ */
.crt__flicker {
  background: #000;
  opacity: 0;
}

/* --------------------------------------------------------------------------
   Motion — scanline drift + flicker, only for viewers who allow motion.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
  body.crt-on .crt__scanlines {
    animation: crt-roll 8s linear infinite;
  }

  @keyframes crt-roll {
    from {
      background-position-y: 0;
    }
    to {
      background-position-y: var(--crt-scan-pitch);
    }
  }

  body.crt-on.crt--flicker .crt__flicker {
    animation: crt-flicker 0.18s linear infinite;
  }

  @keyframes crt-flicker {
    0% {
      opacity: 0.02;
    }
    25% {
      opacity: 0.09;
    }
    50% {
      opacity: 0.03;
    }
    75% {
      opacity: 0.11;
    }
    100% {
      opacity: 0.05;
    }
  }
}
