/* ============================================================
   Helix Games — CRT Atmosphere
   A two-layer overlay that gives every page a faint cathode-ray-tube
   character without intruding on content:
     1. Scanlines  — fixed full-viewport repeating gradient at low
                     opacity. Reads as horizontal phosphor lines.
     2. Noise/grain — tiled SVG fractal noise at very low opacity for
                     a hint of analog texture / breakup.
     3. Vignette  — radial darkening at the corners to "frame" the
                     content the way a real CRT tube would.

   All three are pointer-events: none so they never intercept clicks.
   Honours prefers-reduced-motion (no scanlines, no noise) and
   prefers-contrast: more (atmosphere disabled entirely).

   Loaded after tokens.css; activated by adding `class="atmosphere-on"`
   to <body>. Pages opt in. Game canvases stay crisp because the
   overlay sits BELOW interactive content (z-index: 1) and ABOVE the
   page background only.
   ============================================================ */

body.atmosphere-on {
  position: relative;
}

/* Scanlines. The band colour + overall opacity come from CSS vars so
   themes (terminal, holo) can dial the CRT-ness up or down without
   needing to redefine the layer. */
body.atmosphere-on::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-image: repeating-linear-gradient(
    to bottom,
    transparent 0,
    transparent 2px,
    var(--helix-scanline-band) 2px,
    var(--helix-scanline-band) 3px
  );
  mix-blend-mode: multiply;
  opacity: var(--helix-scanline-opacity);
}

/* Grain (SVG fractal noise) */
body.atmosphere-on::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  /* Tile a small SVG noise patch — keeps the bundle small and the
     visual organic. baseFrequency=0.9 gives a fine "TV static" grain. */
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 0.35 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  opacity: var(--helix-grain-opacity);
  mix-blend-mode: overlay;
}

/* Vignette — applied to #app so it scrolls with content rather than
   floating above the entire viewport. */
body.atmosphere-on #app {
  position: relative;
}
body.atmosphere-on #app::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(
    ellipse at center,
    transparent 55%,
    var(--helix-vignette-end) 100%
  );
}

/* Lift real interactive UI above the overlay layer. `z-index` only
   takes effect on POSITIONED elements, so we set `position: relative`
   here — otherwise the overlay renders above text and the scanline
   pattern multiplies title strokes into a ghosted stripe.

   Importantly we do NOT touch `.app-header` or `.app-sidebar`. The
   framework gives them `position: fixed` (header sticks to the top,
   sidebar slides in from the side) with z-index 1010/1020 — much
   higher than our overlay z-index 1, so they naturally render above
   without any help from us. Forcing them to `position: relative` was
   the bug that made the header scroll with the page. */
body.atmosphere-on > *,
body.atmosphere-on #app,
body.atmosphere-on main {
  position: relative;
  z-index: 2;
}

/* Respect user preferences */
@media (prefers-reduced-motion: reduce) {
  body.atmosphere-on::before { opacity: 0; }
}
@media (prefers-contrast: more) {
  body.atmosphere-on::before,
  body.atmosphere-on::after,
  body.atmosphere-on #app::before { display: none; }
}
