/* ============================================================================
   JOHN BROWN — PHOSPHOR EFFECTS LAYER
   Opt-in CRT / terminal finishes: glow, scanlines, flicker, glitch, decay.
   Layer on top of colors_and_type.css. Everything is keyed off existing tokens
   and is a DECORATIVE finish — never required for legibility.

   ACCESSIBILITY CONTRACT (do not break):
   · Effects never sit under body text in a way that reduces contrast.
   · Nothing animates an element's own opacity to 0 (backgrounded tabs pause
     animations and would leave content invisible) — we animate text-shadow,
     filter, transform, and OVERLAY pseudo-elements instead.
   · Every motion effect is disabled under prefers-reduced-motion; the static
     glow/scanline finish remains.
   · Glitch is for display/decorative text only, never controls or running prose.

   Tune per-element with: --fx-glow (color), --fx-glow-size, --fx-scan-opacity.
   ============================================================================ */

:root {
  --fx-glow: var(--amber);
  --fx-glow-size: 10px;
  --fx-scan-opacity: 0.35;
  --fx-flicker-min: 0.82; /* brightness floor for flicker (never 0) */
}

/* ---------------------------------------------------------------------------
   1. PHOSPHOR GLOW — lit text/elements bloom like a charged phosphor dot.
   --------------------------------------------------------------------------- */
.fx-phosphor {
  color: var(--fx-glow);
  text-shadow:
    0 0 calc(var(--fx-glow-size) * 0.35) color-mix(in srgb, var(--fx-glow) 70%, transparent),
    0 0 var(--fx-glow-size) color-mix(in srgb, var(--fx-glow) 45%, transparent);
}
.fx-phosphor-cyan {
  --fx-glow: var(--cyan);
}
.fx-phosphor-acid {
  --fx-glow: var(--acid);
}
.fx-phosphor-red {
  --fx-glow: var(--red);
}

/* a soft box bloom for lit panels/keylines */
.fx-glow-box {
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--fx-glow) 40%, transparent),
    0 0 18px color-mix(in srgb, var(--fx-glow) 22%, transparent),
    inset 0 0 24px color-mix(in srgb, var(--fx-glow) 6%, transparent);
}

/* breathing phosphor — gentle pulse of the glow (text-shadow only, stays lit) */
.fx-pulse {
  animation: fx-pulse 2.8s var(--ease) infinite;
}
@keyframes fx-pulse {
  0%,
  100% {
    text-shadow: 0 0 calc(var(--fx-glow-size) * 0.3)
      color-mix(in srgb, var(--fx-glow) 55%, transparent);
  }
  50% {
    text-shadow:
      0 0 calc(var(--fx-glow-size) * 0.9) color-mix(in srgb, var(--fx-glow) 80%, transparent),
      0 0 calc(var(--fx-glow-size) * 2) color-mix(in srgb, var(--fx-glow) 35%, transparent);
  }
}

/* ---------------------------------------------------------------------------
   2. CRT SCREEN — scanlines + aperture grille + subtle flicker + vignette.
      Apply to a positioned container; overlays sit ABOVE content but are
      pointer-events:none and low-alpha so text stays readable.
   --------------------------------------------------------------------------- */
.fx-crt {
  position: relative;
}
.fx-crt > * {
  position: relative;
  z-index: 0;
}
.fx-crt::before,
.fx-crt::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* horizontal scanlines + faint RGB aperture grille */
.fx-crt::before {
  z-index: 2;
  background:
    repeating-linear-gradient(
      to bottom,
      rgba(0, 0, 0, calc(var(--fx-scan-opacity) * 0.9)) 0 1px,
      transparent 1px 3px
    ),
    repeating-linear-gradient(
      to right,
      rgba(255, 0, 0, 0.03) 0 1px,
      rgba(0, 255, 0, 0.02) 1px 2px,
      rgba(0, 0, 255, 0.03) 2px 3px
    );
  mix-blend-mode: multiply;
}
/* vignette + a barely-there screen flicker (overlay opacity, content untouched) */
.fx-crt::after {
  z-index: 3;
  background: radial-gradient(
    120% 130% at 50% 50%,
    transparent 55%,
    color-mix(in srgb, var(--vignette) 70%, transparent) 100%
  );
  animation: fx-screen-flicker 5.5s steps(1) infinite;
}
@keyframes fx-screen-flicker {
  0%,
  100% {
    opacity: 1;
  }
  48% {
    opacity: 1;
  }
  49% {
    opacity: 0.94;
  }
  50% {
    opacity: 1;
  }
  77% {
    opacity: 1;
  }
  78% {
    opacity: 0.97;
  }
  79% {
    opacity: 1;
  }
}

/* rolling scanline sweep — a single bright bar drifting down the screen */
.fx-scan-sweep {
  position: relative;
  overflow: hidden;
}
.fx-scan-sweep::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 28%;
  pointer-events: none;
  z-index: 4;
  background: linear-gradient(
    to bottom,
    transparent,
    color-mix(in srgb, var(--fx-glow) 7%, transparent),
    transparent
  );
  animation: fx-sweep 7s linear infinite;
}
@keyframes fx-sweep {
  0% {
    transform: translateY(-120%);
  }
  100% {
    transform: translateY(460%);
  }
}

/* ---------------------------------------------------------------------------
   3. FLICKER — unstable tube brightness. Uses filter brightness (never opacity 0).
   --------------------------------------------------------------------------- */
.fx-flicker {
  animation: fx-flicker 4s steps(1) infinite;
}
@keyframes fx-flicker {
  0%,
  100% {
    filter: brightness(1);
  }
  8% {
    filter: brightness(var(--fx-flicker-min));
  }
  9% {
    filter: brightness(1.06);
  }
  10% {
    filter: brightness(1);
  }
  42% {
    filter: brightness(1);
  }
  43% {
    filter: brightness(0.9);
  }
  44% {
    filter: brightness(1.04);
  }
  45% {
    filter: brightness(1);
  }
  88% {
    filter: brightness(1);
  }
  89% {
    filter: brightness(var(--fx-flicker-min));
  }
  90% {
    filter: brightness(1);
  }
}

/* ---------------------------------------------------------------------------
   4. GLITCH — RGB-split chromatic tear. DISPLAY/DECORATIVE text only.
      Markup: <span class="fx-glitch" data-text="LABEL">LABEL</span>
      The clones are aria-hidden via ::before/::after (not real DOM), so screen
      readers still read the single real text node.
   --------------------------------------------------------------------------- */
.fx-glitch {
  position: relative;
  display: inline-block;
  color: var(--fg-1);
}
.fx-glitch::before,
.fx-glitch::after {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  pointer-events: none;
  clip-path: inset(0 0 0 0);
  background: var(--bg-void);
}
.fx-glitch::before {
  left: 1px;
  color: var(--red);
  mix-blend-mode: screen;
  animation: fx-glitch-a 3.4s steps(2) infinite;
}
.fx-glitch::after {
  left: -1px;
  color: var(--cyan);
  mix-blend-mode: screen;
  animation: fx-glitch-b 2.7s steps(2) infinite;
}
@keyframes fx-glitch-a {
  0%,
  100%,
  72%,
  96% {
    clip-path: inset(100% 0 0 0);
    transform: translate(0, 0);
  }
  74% {
    clip-path: inset(8% 0 78% 0);
    transform: translate(2px, -1px);
  }
  86% {
    clip-path: inset(62% 0 12% 0);
    transform: translate(-2px, 1px);
  }
}
@keyframes fx-glitch-b {
  0%,
  100%,
  40%,
  88% {
    clip-path: inset(100% 0 0 0);
    transform: translate(0, 0);
  }
  46% {
    clip-path: inset(40% 0 30% 0);
    transform: translate(-2px, 0);
  }
  64% {
    clip-path: inset(74% 0 8% 0);
    transform: translate(2px, 1px);
  }
}
/* hover-only glitch for interactive accents (still display text) */
.fx-glitch-hover::before,
.fx-glitch-hover::after {
  animation-play-state: paused;
  opacity: 0;
}
.fx-glitch-hover:hover::before {
  opacity: 1;
  animation: fx-glitch-a 0.5s steps(2) 1;
}
.fx-glitch-hover:hover::after {
  opacity: 1;
  animation: fx-glitch-b 0.5s steps(2) 1;
}

/* ---------------------------------------------------------------------------
   5. TERMINAL CURSOR + TYPED REVEAL
   --------------------------------------------------------------------------- */
.fx-cursor::after {
  content: "▮";
  display: inline-block;
  margin-left: 2px;
  color: var(--fx-glow);
  text-shadow: 0 0 8px color-mix(in srgb, var(--fx-glow) 60%, transparent);
  animation: fx-blink 1.05s steps(1) infinite;
}
@keyframes fx-blink {
  0%,
  49% {
    opacity: 1;
  }
  50%,
  100% {
    opacity: 0;
  }
}

/* type-on reveal (width-based, so the text node is always present/readable) */
.fx-type {
  display: inline-block;
  overflow: hidden;
  white-space: nowrap;
  border-right: 2px solid var(--fx-glow);
  animation:
    fx-type 1.6s steps(28, end) 1,
    fx-blink 1.05s steps(1) infinite;
}
@keyframes fx-type {
  from {
    width: 0;
  }
  to {
    width: 100%;
  }
}

/* ---------------------------------------------------------------------------
   6. POWER-ON — brief CRT degauss/boot when a screen mounts.
      Animates transform + filter (visible throughout); add class on mount.
   --------------------------------------------------------------------------- */
.fx-power-on {
  animation: fx-power-on 0.55s var(--ease-out) 1;
  transform-origin: center;
}
@keyframes fx-power-on {
  0% {
    transform: scaleY(0.004) scaleX(1.2);
    filter: brightness(3) saturate(0);
  }
  8% {
    transform: scaleY(0.02) scaleX(1.1);
    filter: brightness(2.4) saturate(0.3);
  }
  40% {
    transform: scaleY(1) scaleX(1);
    filter: brightness(1.3);
  }
  100% {
    transform: none;
    filter: none;
  }
}

/* a one-shot horizontal tear/degauss line that drops once on mount */
.fx-degauss {
  position: relative;
}
.fx-degauss::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 2px;
  top: 0;
  pointer-events: none;
  z-index: 6;
  background: color-mix(in srgb, var(--fx-glow) 60%, white 20%);
  box-shadow: 0 0 14px var(--fx-glow);
  animation: fx-degauss 0.7s var(--ease-out) 1 forwards;
}
@keyframes fx-degauss {
  0% {
    top: 0;
    opacity: 0.9;
  }
  100% {
    top: 100%;
    opacity: 0;
  }
}

/* ---------------------------------------------------------------------------
   7. PHOSPHOR HAZE — ambient bloom the tube casts into the room. A soft,
      slowly-drifting radial glow that sits OVER the screen at very low alpha
      (screen blend), so blacks lift slightly and lit areas feel like they
      breathe light. Pointer-events:none; never touches contrast of text.
   --------------------------------------------------------------------------- */
.fx-haze {
  position: relative;
}
.fx-haze::before {
  content: "";
  position: absolute;
  inset: -10%;
  pointer-events: none;
  z-index: 1;
  background:
    radial-gradient(
      40% 50% at 30% 20%,
      color-mix(in srgb, var(--fx-glow) 12%, transparent),
      transparent 70%
    ),
    radial-gradient(
      45% 55% at 75% 75%,
      color-mix(in srgb, var(--cyan) 7%, transparent),
      transparent 72%
    ),
    radial-gradient(
      60% 70% at 50% 50%,
      color-mix(in srgb, var(--fx-glow) 5%, transparent),
      transparent 75%
    );
  mix-blend-mode: screen;
  filter: blur(18px);
  animation: fx-haze-drift 14s ease-in-out infinite alternate;
}
@keyframes fx-haze-drift {
  0% {
    transform: translate3d(-1.5%, -1%, 0) scale(1.02);
    opacity: 0.85;
  }
  100% {
    transform: translate3d(1.5%, 1.5%, 0) scale(1.06);
    opacity: 1;
  }
}
/* localized haze halo behind a single lit element (e.g. a hero word/metric) */
.fx-haze-spot {
  position: relative;
  isolation: isolate;
}
.fx-haze-spot::before {
  content: "";
  position: absolute;
  inset: -40% -25%;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(
    50% 50% at 50% 50%,
    color-mix(in srgb, var(--fx-glow) 28%, transparent),
    transparent 70%
  );
  filter: blur(22px);
  opacity: 0.8;
  animation: fx-pulse-haze 3.6s var(--ease) infinite;
}
@keyframes fx-pulse-haze {
  0%,
  100% {
    opacity: 0.6;
    transform: scale(1);
  }
  50% {
    opacity: 0.95;
    transform: scale(1.08);
  }
}

/* ---------------------------------------------------------------------------
   8. BURN-IN — static UI that has sat too long has etched a faint permanent
      afterimage into the phosphor. A non-animated ghost clone, blurred and
      barely visible, offset a hair. Decorative; the real text node is intact.
      Markup: <span class="fx-burn" data-text="OPERATIONS">OPERATIONS</span>
   --------------------------------------------------------------------------- */
.fx-burn {
  position: relative;
}
.fx-burn::after {
  content: attr(data-text);
  position: absolute;
  left: 0;
  top: 0;
  pointer-events: none;
  color: var(--fx-glow);
  opacity: 0.14;
  filter: blur(0.6px);
  transform: translate(0.5px, 0.5px);
  z-index: -1;
  white-space: inherit;
}
/* a rectangular panel burn-in: faint retained edge-glow where chrome once sat */
.fx-burn-edge {
  box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--fx-glow) 10%, transparent),
    inset 0 0 22px color-mix(in srgb, var(--fx-glow) 5%, transparent);
}

/* ---------------------------------------------------------------------------
   REDUCED MOTION — kill all animation, keep the static phosphor finish.
   --------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .fx-pulse,
  .fx-crt::after,
  .fx-scan-sweep::after,
  .fx-flicker,
  .fx-glitch::before,
  .fx-glitch::after,
  .fx-cursor::after,
  .fx-type,
  .fx-power-on,
  .fx-degauss::before,
  .fx-haze::before,
  .fx-haze-spot::before {
    animation: none !important;
  }
  .fx-glitch::before,
  .fx-glitch::after {
    display: none;
  } /* no static RGB ghost */
  .fx-type {
    width: auto;
    border-right: none;
  }
  .fx-cursor::after {
    opacity: 1;
  }
}
