/* jjscoolwebsite.com — the CRT layer
   A fake television sat over the whole site: horizontal scanlines, a soft
   vignette into the corners, a faint phosphor flicker and a slow roll bar.

   Nothing here turns itself on. The layer is `display: none` until <html>
   carries data-scanlines="on", because this file doesn't own the switch —
   the settings panel does, and layouts/base.njk writes the starting value
   straight into the markup so the first frame is already a television.

     <html data-scanlines="on" data-scanlines-intensity="high">

   data-scanlines-intensity takes low | medium | high. Anything else — a typo,
   a missing attribute, an empty string — falls through to the medium numbers,
   which are the ones declared on the "on" block itself; low and high only
   overwrite them. That fallback is what makes the settings slider safe: it
   can only ever hand over one of the three, and anything that isn't looks
   exactly like the default.

   Colours come from the site palette rather than black and white: the lines
   are --dim (#16121f) and the gaps between them carry a breath of --lit
   (#7d78ad), so the screen reads as this navy page behind glass instead of a
   grey filter dropped on top. Literal rgb() triples rather than the tokens,
   because these need an alpha channel per intensity and the tokens are hex. */

.crt-overlay {
  /* Off unless asked for. */
  display: none;
  position: fixed;
  inset: 0;
  /* Decoration only: every click, hover and drag passes straight through. */
  pointer-events: none;

  /* Above ordinary page content — the topbar sits at 5, the ticker at 2, the
     home page's hero letters reach 3 and the skip link 10 — and deliberately
     below the floating UI: the music widget and the quest HUD start at 30 and
     the quest's cutscenes, panels and toasts run 40–60. Those are things you
     read and press, so they sit on the glass rather than behind it, and none
     of them can be dimmed or tinted by a setting the visitor forgot about. */
  z-index: 25;
}

/* The switch. Everything below is scoped to it, so an absent attribute costs
   the page one `display: none` element and nothing else. */
[data-scanlines="on"] .crt-overlay {
  display: block;

  /* --- medium, and the fallback for any unrecognised intensity --- */

  /* One scanline period: --crt-line-w of dark, the rest gap. */
  --crt-period: 3px;
  --crt-line-w: 1px;
  --crt-line-a: 0.20;
  /* The lavender glow in the gap. Keeps the dark lines from reading as soot. */
  --crt-gap-a: 0.05;
  --crt-vignette-a: 0.34;
  /* The phosphor bloom in the top-left corner, in --yellow. */
  --crt-bloom-a: 0.035;
  /* Flicker floor: the layer's opacity dips to this and back, fast. */
  --crt-flicker-min: 0.86;
  --crt-flicker-dur: 5.5s;
  /* The roll bar — a slightly brighter band drifting down the screen. */
  --crt-roll-a: 0.035;
  --crt-roll-dur: 9s;

  background-image: repeating-linear-gradient(
    to bottom,
    rgba(22, 18, 31, var(--crt-line-a)) 0,
    rgba(22, 18, 31, var(--crt-line-a)) var(--crt-line-w),
    rgba(125, 120, 173, var(--crt-gap-a)) var(--crt-line-w),
    rgba(125, 120, 173, var(--crt-gap-a)) var(--crt-period)
  );

  animation: crt-flicker var(--crt-flicker-dur) steps(1, end) infinite;
}

/* --- low: the lines are there if you look for them --- */
[data-scanlines="on"][data-scanlines-intensity="low"] .crt-overlay {
  --crt-period: 4px;
  --crt-line-w: 1px;
  --crt-line-a: 0.10;
  --crt-gap-a: 0.03;
  --crt-vignette-a: 0.18;
  --crt-bloom-a: 0.02;
  /* No flicker and no roll bar at low — the lines alone are the effect. */
  --crt-flicker-min: 1;
  --crt-roll-a: 0;
}

/* --- high: a tube with the brightness up too far --- */
[data-scanlines="on"][data-scanlines-intensity="high"] .crt-overlay {
  --crt-period: 3px;
  --crt-line-w: 2px;
  --crt-line-a: 0.34;
  --crt-gap-a: 0.09;
  --crt-vignette-a: 0.52;
  --crt-bloom-a: 0.06;
  --crt-flicker-min: 0.72;
  --crt-flicker-dur: 3.4s;
  --crt-roll-a: 0.075;
  --crt-roll-dur: 6s;
}

/* The curve of the glass: dark in the corners, plus one soft --yellow bloom
   off the top-left the way a lit phosphor screen throws light back at you.
   `transparent 52%` keeps the middle of the page — where the text is — clean
   at every intensity; only the margins darken. */
[data-scanlines="on"] .crt-overlay::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    radial-gradient(
      circle at 22% 12%,
      rgba(251, 242, 84, var(--crt-bloom-a)) 0,
      rgba(251, 242, 84, 0) 45%
    ),
    radial-gradient(
      ellipse 78% 72% at 50% 50%,
      rgba(16, 14, 26, 0) 52%,
      rgba(16, 14, 26, var(--crt-vignette-a)) 100%
    );
}

/* The roll bar. A tall, very faint lavender band that walks from above the
   viewport to below it and starts again — the refresh beat an old set never
   quite locked onto. Height is a third of the screen so it stays a wash
   rather than a stripe; at low, --crt-roll-a is 0 and it is invisible. */
[data-scanlines="on"] .crt-overlay::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 34vh;
  background-image: linear-gradient(
    to bottom,
    rgba(125, 120, 173, 0) 0,
    rgba(125, 120, 173, var(--crt-roll-a)) 50%,
    rgba(125, 120, 173, 0) 100%
  );
  animation: crt-roll var(--crt-roll-dur) linear infinite;
}

/* steps(1, end) on the animation above means these are held frames, not a
   crossfade: the brightness snaps between them, which is what a failing tube
   does. The dips are short and unevenly spaced so it never reads as a pulse. */
@keyframes crt-flicker {
  0%,
  18%,
  22%,
  54%,
  58%,
  81%,
  88%,
  100% {
    opacity: 1;
  }

  19%,
  55%,
  82% {
    opacity: var(--crt-flicker-min);
  }

  21%,
  57% {
    opacity: calc((1 + var(--crt-flicker-min)) / 2);
  }
}

@keyframes crt-roll {
  from {
    transform: translateY(-34vh);
  }

  to {
    transform: translateY(100vh);
  }
}

/* Same bargain as the ticker and the quest layer: the look stays, the motion
   goes. The scanlines, the vignette and the bloom are all static, so a visitor
   who asked for less movement still gets the CRT — it simply holds still, and
   the roll bar (which is nothing but movement) is taken off the page. */
@media (prefers-reduced-motion: reduce) {
  [data-scanlines="on"] .crt-overlay {
    animation: none;
    opacity: 1;
  }

  [data-scanlines="on"] .crt-overlay::after {
    display: none;
  }
}
