/* CRT emulation.
   --------------------------------------------------------------------------
   Layered the way a real tube actually builds an image, outermost last:

     1. #crt-warp   geometry — the glass is not flat, and the corners pull in
     2. #crt-mask   the shadow mask / aperture grille, in OUTPUT pixels
     3. #crt-scan   scanlines, thinner where the picture is bright
     4. #crt-glass  reflection, vignette, and the dust on a 22-year-old screen

   Every layer is driven by a CSS variable so js/crt/crt.js can expose real
   knobs rather than a single on/off. Everything is pointer-events:none and
   sits above the app, so the emulation can never eat a click.

   If .crt-off is set, all of it collapses to nothing — the site has to stay
   usable for people who find this unbearable, which is most people. */

/* The three layers are siblings of #app, not children of a wrapper. See
   mount() in js/crt/crt.js: a wrapper would form its own stacking context and
   quietly turn every mix-blend-mode below into plain alpha compositing. */
#crt-mask, #crt-scan, #crt-glass {
  position: fixed;
  inset: 0;
  pointer-events: none;
  display: none;
}

html.crt-on :is(#crt-mask, #crt-scan, #crt-glass) { display: block; }

#crt-mask { z-index: 9000; }
#crt-scan { z-index: 9001; }
#crt-glass { z-index: 9002; }

/* --- 1. geometry ------------------------------------------------------- */

html.crt-on #app {
  transform-origin: 50% 50%;
  transform:
    perspective(1400px)
    scale(calc(1 - var(--crt-curve, 0.04) * 0.55));
  border-radius: calc(var(--crt-curve, 0.04) * 320px);
  overflow: hidden;
  filter:
    saturate(var(--crt-sat, 1.15))
    contrast(var(--crt-contrast, 1.06))
    brightness(var(--crt-bright, 1.02));

  /* Not incidental: a filter forces Chrome to drop subpixel antialiasing and
     render text with grey AA instead. That matters enormously here, because
     subpixel AA paints its own RGB fringes and those fringes land on top of an
     aperture grille that is already painting RGB stripes — two different
     phosphor grids at two different phases, which is what muddy CRT emulation
     actually is. The explicit properties below say the same thing to browsers
     that need asking. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: geometricPrecision;
}

/* Phosphor bloom. text-shadow is an inherited property, so one declaration on
   the container reaches every glyph in the app, and `currentColor` means each
   element blooms in its own colour without a single hard-coded value.
   This is the half of the effect that makes type look EMITTED rather than
   printed — and it is what gives the scanlines and the shadow mask something
   bright enough to cut across, which is the whole point of them. */
html.crt-on #app {
  text-shadow:
    0 0 calc(var(--crt-bloom, 0.4) * 4px) currentColor,
    calc(var(--crt-bleed, 0.2) * 0.6px) 0 rgba(255, 60, 60, 0.2),
    calc(var(--crt-bleed, 0.2) * -0.6px) 0 rgba(60, 220, 255, 0.2);
}

/* Headings and wordmarks carry their own --glow, which as a fixed rgba value
   ignored the bloom knob and fought it at the extremes. Redefining the token
   itself under .crt-on folds every existing `text-shadow: var(--glow)` into
   the same system in one line: same knob, and `currentColor` means a heading
   blooms in its own colour rather than a hard-coded green. Brighter elements
   bloom wider than body text, which is what a real tube does. */
html.crt-on {
  --glow: 0 0 calc(var(--crt-bloom, 0.4) * 7px) currentColor;
}

/* Form controls opt out: a glowing input caret is not nostalgia, it is a
   headache, and the bloom makes small text in a 140-character field mush. */
html.crt-on #app :is(input, textarea, select, code, pre) {
  text-shadow: none;
}

/* --- 2. shadow mask ---------------------------------------------------- */

#crt-mask {
  opacity: var(--crt-mask-strength, 0.09);
  mix-blend-mode: color-burn;
}

/* Aperture grille (Trinitron): vertical RGB stripes, no vertical structure. */
html[data-mask='grille'] #crt-mask {
  background: repeating-linear-gradient(
    90deg,
    rgba(255, 0, 0, 0.9) 0, rgba(255, 0, 0, 0.9) 1px,
    rgba(0, 255, 0, 0.9) 1px, rgba(0, 255, 0, 0.9) 2px,
    rgba(0, 0, 255, 0.9) 2px, rgba(0, 0, 255, 0.9) 3px
  );
}

/* Trinitrons have two damper wires across the screen. Everyone who owned one
   remembers the lines. Non-negotiable detail. */
html[data-mask='grille'] #crt-mask::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(transparent 27.5%, rgba(0, 0, 0, 0.55) 27.6%, rgba(0,0,0,0.55) 27.75%, transparent 27.85%),
    linear-gradient(transparent 73.5%, rgba(0, 0, 0, 0.55) 73.6%, rgba(0,0,0,0.55) 73.75%, transparent 73.85%);
  mix-blend-mode: normal;
  /* The Trinitron damper wires are a signature, not a feature — they fade out
     with the mask instead of staying at full strength over a gentle setting. */
  opacity: calc(var(--crt-mask-strength, 0.09) * 2.2);
}

/* Slot mask: the cheap PC monitor. Staggered phosphor rectangles. */
html[data-mask='slot'] #crt-mask {
  background:
    repeating-linear-gradient(
      90deg,
      rgba(255, 0, 0, 0.85) 0, rgba(255, 0, 0, 0.85) 1px,
      rgba(0, 255, 0, 0.85) 1px, rgba(0, 255, 0, 0.85) 2px,
      rgba(0, 0, 255, 0.85) 2px, rgba(0, 0, 255, 0.85) 3px
    ),
    repeating-linear-gradient(
      0deg,
      rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.5) 1px,
      transparent 1px, transparent 4px
    );
}

/* Dot triad: the console television. */
html[data-mask='dot'] #crt-mask {
  background-image: radial-gradient(circle at 50% 50%, rgba(255,255,255,0.9) 0.5px, transparent 1.1px);
  background-size: 3px 3px;
  mix-blend-mode: overlay;
  opacity: calc(var(--crt-mask-strength, 0.28) * 1.4);
}

html[data-mask='none'] #crt-mask { display: none; }

/* --- 3. scanlines ------------------------------------------------------ */

/* The line and the gap are both opaque, and the layer burns rather than
   paints. See scanInk() in js/crt/crt.js for why that is the whole trick:
   the gaps are white and therefore invisible, dark background is already at
   the floor so it stays clean instead of gaining a grey grid, and bright text
   survives the line crossing it. A flat black overlay at 34% alpha — the old
   version — dimmed the page uniformly and dimmed the text most of all. */
#crt-scan {
  mix-blend-mode: color-burn;
  background: repeating-linear-gradient(
    180deg,
    var(--crt-scan-ink, #d8d8d8) 0,
    var(--crt-scan-ink, #d8d8d8) calc(var(--crt-scan-size, 4px) * 0.5),
    #fff calc(var(--crt-scan-size, 4px) * 0.5),
    #fff var(--crt-scan-size, 4px)
  );
}



/* Interlace shimmer — the 60Hz flicker that made your eyes hurt by 4pm. */
html.crt-on[data-flicker='on'] #crt-scan {
  animation: crt-flicker 0.11s steps(2, end) infinite;
}

@keyframes crt-flicker {
  0%   { opacity: 1; }
  100% { opacity: 0.93; }
}

/* Vertical hold drift: the picture creeps upward, slowly, forever. */
html.crt-on[data-roll='on'] #crt-scan {
  background-position-y: 0;
  animation: crt-roll 9s linear infinite;
}

@keyframes crt-roll {
  to { background-position-y: -200px; }
}

/* --- 4. glass ---------------------------------------------------------- */

#crt-glass {
  background:
    /* corner darkening from the tube's own geometry */
    radial-gradient(
      140% 120% at 50% 50%,
      transparent 52%,
      rgba(0, 0, 0, calc(var(--crt-vignette, 0.5) * 0.7)) 88%,
      rgba(0, 0, 0, var(--crt-vignette, 0.5)) 100%
    ),
    /* the window reflected in the glass, upper left, always */
    linear-gradient(
      145deg,
      rgba(255, 255, 255, var(--crt-glare, 0.035)) 0%,
      transparent 32%
    );
}

/* The bright band sweeping down the screen when a phone is too close. */
html.crt-on[data-hum='on'] #crt-glass::after {
  content: '';
  position: absolute;
  inset: -20% 0;
  background: linear-gradient(
    180deg,
    transparent 0%,
    rgba(255, 255, 255, 0.045) 45%,
    rgba(255, 255, 255, 0.07) 50%,
    rgba(255, 255, 255, 0.045) 55%,
    transparent 100%
  );
  animation: crt-hum 7.5s linear infinite;
}

@keyframes crt-hum {
  from { transform: translateY(-60%); }
  to   { transform: translateY(60%); }
}

/* --- power cycle ------------------------------------------------------- */

/* Turning the emulation on degausses, because of course it does. */
html.crt-degauss #app { animation: degauss 0.85s ease-out 1; }

@keyframes degauss {
  0%   { filter: hue-rotate(0deg) saturate(3) brightness(1.6); transform: scale(1.015) skewX(1.4deg); }
  20%  { filter: hue-rotate(40deg) saturate(2.4) brightness(1.3); transform: scale(0.995) skewX(-1.1deg); }
  45%  { filter: hue-rotate(-30deg) saturate(1.8) brightness(1.12); transform: scale(1.006) skewX(0.6deg); }
  70%  { filter: hue-rotate(12deg) saturate(1.3); transform: scale(0.999) skewX(-0.25deg); }
  100% { filter: none; transform: none; }
}

/* Switching it off collapses the picture to a line, then a dot. */
html.crt-poweroff #app {
  animation: crt-off 0.45s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}

@keyframes crt-off {
  0%   { transform: scale(1, 1); filter: brightness(1); }
  55%  { transform: scale(1, 0.006); filter: brightness(4); }
  100% { transform: scale(0.002, 0.002); filter: brightness(8); opacity: 0; }
}

/* --- the knob panel ---------------------------------------------------- */

#crt-knobs {
  position: fixed;
  right: 8px;
  bottom: 40px;
  z-index: 9100;
  width: 218px;
  display: none;
  padding: 8px;
  background: var(--face);
  color: var(--face-ink);
  font: 11px var(--ui);
  border: 2px outset var(--face-lit);
  box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.5);
}

#crt-knobs.open { display: block; }
#crt-knobs h3 { margin: 0 0 6px; font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; }
#crt-knobs label { display: block; margin: 5px 0 1px; }
#crt-knobs input[type='range'] { width: 100%; }
#crt-knobs select, #crt-knobs button { width: 100%; font: 11px var(--ui); }
#crt-knobs .row { display: flex; gap: 4px; }
