/* ============================================================
   CRAZY — glitch.css   (v2 — Architecture Council binding fixes)
   The escalation effects: RGB split, jitter, corruption,
   datamosh, tear, lurch. Intensity is driven by --instability.

   Council fixes baked in:
   • NO `attr(data-text)` mirroring — RGB-split is layered text-
     shadow on the REAL glyph, so untrusted text never enters a
     second render path (textContent-only law, D20).
   • NO `mix-blend-mode` full-screen passes (moved to crt.css).
   • NO `infinite` escalation animations — every animated glitch
     runs ONLY under `body.fx-on` (the intensity governor toggles
     it: play/stop, D16). One-shot effects are added/removed by JS.
   • `lurch` is transform-only (no animated `filter`, the worst
     measured cost per Meier).
   ALL of it is stripped by prefers-reduced-motion / .safe-mode.
   ============================================================ */

/* ---- RGB split ("chromatic aberration") on the REAL glyph.
   No pseudo-element text, no data-text. Just layered shadows whose
   offset scales with instability. Static by default; the jitter
   animation is governor-gated below. ---- */
.glitch {
  position: relative;
  display: inline-block;
  text-shadow:
    calc((-1px - 2px * var(--instability))) 0 0 color-mix(in srgb, var(--accent-cyan) 85%, transparent),
    calc((1px + 2px * var(--instability))) 0 0 color-mix(in srgb, var(--accent-magenta) 85%, transparent),
    0 0 calc(3px * var(--glow)) var(--glow-color);
}

/* Governor ON → the split shivers. Cheap transform, small spans only. */
body.fx-on .glitch {
  animation: glitch-shiver calc(0.9s - 0.5s * var(--instability)) steps(3) infinite;
}
@keyframes glitch-shiver {
  0%, 100% { transform: translate(0, 0); }
  33% { transform: translate(calc(0.6px * (0.5 + var(--instability) * 2)), 0); }
  66% { transform: translate(calc(-0.5px * (0.5 + var(--instability) * 2)), 0.3px); }
}

/* ---- Jitter: a line that won't sit still. Governor-gated. ---- */
body.fx-on .jitter {
  animation: jitter-move calc(0.5s + 0.3s * (1 - var(--instability))) steps(3) infinite;
}
@keyframes jitter-move {
  0% { transform: translate(0, 0); }
  33% { transform: translate(calc(0.6px * var(--instability) * 3), 0); }
  66% { transform: translate(calc(-0.5px * var(--instability) * 3), calc(0.4px * var(--instability) * 2)); }
  100% { transform: translate(0, 0); }
}

/* ---- Corruption: the engine swaps a glyph to noise for a beat,
   tagging the span .corrupt. One-shot; visual only. ---- */
.corrupt {
  color: var(--accent-red);
  letter-spacing: 0.08em;
}
body.fx-on .corrupt {
  animation: corrupt-flash 0.18s steps(2) 2;
}
@keyframes corrupt-flash {
  0% { opacity: 1; }
  50% { opacity: 0.45; }
  100% { opacity: 1; }
}

/* ---- Datamosh: occasional shifted slices. One-shot class the
   engine adds then removes. ---- */
body.fx-on .crt__screen.mosh .crt__content {
  animation: mosh 0.42s steps(1) 1;
}
@keyframes mosh {
  0% { clip-path: inset(0 0 0 0); transform: translate(0, 0); }
  20% { clip-path: inset(12% 0 40% 0); transform: translate(6px, 0); }
  21% { clip-path: inset(0 0 0 0); transform: translate(0, 0); }
  55% { clip-path: inset(60% 0 8% 0); transform: translate(-7px, 0); }
  56% { clip-path: inset(0 0 0 0); transform: translate(0, 0); }
  100% { clip-path: inset(0 0 0 0); transform: translate(0, 0); }
}

/* ---- Screen tear: a horizontal slip. One-shot. ---- */
body.fx-on .crt__screen.tear {
  animation: tear 0.3s steps(1) 1;
}
@keyframes tear {
  0% { transform: translateX(0); }
  50% { transform: translateX(calc(8px + 12px * var(--instability))); }
  100% { transform: translateX(0); }
}

/* ---- Lurch: a brief desync on a sharp escalation beat.
   TRANSFORM ONLY — no animated filter (Council fix #3). One-shot,
   bounded, never a rapid repeat. ---- */
body.fx-on .crt.lurch {
  animation: lurch 0.5s ease-out 1;
}
@keyframes lurch {
  0% { transform: scale(1) translate(0, 0); }
  18% { transform: scale(1.014) translate(3px, -2px); }
  40% { transform: scale(0.992) translate(-4px, 1px); }
  70% { transform: scale(1.004) translate(2px, 0); }
  100% { transform: scale(1) translate(0, 0); }
}

/* ---- Single inverted beat (bounded one-shot; never strobes).
   The governor guarantees this can't fire faster than the 3Hz cap. ---- */
body.fx-on .crt.invert-beat .crt__content {
  animation: invert-beat 0.2s steps(1) 1;
}
@keyframes invert-beat {
  0% { filter: none; }
  50% { filter: invert(1) hue-rotate(180deg); }
  100% { filter: none; }
}

/* ============================================================
   v2 DC3 — the extended screen-effects set. Transform/opacity only
   (gentle by default) EXCEPT color-flash + blackout-blink, which are
   luminance and are intense-opt-in + global-flash-budget gated in
   effects.js. All one-shot, governor-toggled (body.fx-on), and all
   are listed in the reduced-motion / safe-mode kill blocks below.
   ============================================================ */
body.fx-on .crt.shake { animation: shake 0.36s steps(2) 1; }
@keyframes shake {
  0%, 100% { transform: translate(0, 0); }
  25% { transform: translate(calc(2px + 3px * var(--instability)), -1px); }
  50% { transform: translate(calc(-2px - 3px * var(--instability)), 1px); }
  75% { transform: translate(1px, 1px); }
}

body.fx-on .crt.rgb-pulse .glitch { animation: rgb-pulse 0.3s steps(2) 1; }
@keyframes rgb-pulse {
  0%, 100% { text-shadow: -1px 0 0 var(--accent-cyan), 1px 0 0 var(--accent-magenta); }
  50% { text-shadow:
    calc(-3px - 3px * var(--instability)) 0 0 var(--accent-cyan),
    calc(3px + 3px * var(--instability)) 0 0 var(--accent-magenta); }
}

body.fx-on .crt__screen.jitter-beat .crt__content { animation: jitter-beat 0.32s steps(3) 1; }
@keyframes jitter-beat {
  0%, 100% { transform: translate(0, 0); }
  33% { transform: translate(calc(1px * var(--instability) * 3), 0); }
  66% { transform: translate(calc(-1px * var(--instability) * 3), 0.5px); }
}

/* scan-roll reuses the EXISTING scanband (no new full-screen element) */
body.fx-on .crt__scanband.scan-roll { animation: scan-roll 0.48s linear 1; }
@keyframes scan-roll {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(24px); }
}

/* the interview "reading" cue — a held, quiet opacity flicker (~1.5 Hz) */
body.fx-on .crt.reading .crt__content { animation: reading-flicker 0.32s steps(2) 1; }
@keyframes reading-flicker {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.86; }
}

/* color-flash (LUMINANCE, intense-only): a tint toward accent, NEVER to white */
body.fx-on .crt.color-flash .crt__content { animation: color-flash 0.24s steps(1) 1; }
@keyframes color-flash {
  0%, 100% { filter: none; }
  50% { filter: hue-rotate(40deg) saturate(1.6); }
}

/* blackout-blink (LUMINANCE, intense-only): a brief dip toward black */
body.fx-on .crt.blackout-blink .crt__content { animation: blackout-blink 0.2s steps(1) 1; }
@keyframes blackout-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.1; }
}

/* ============================================================
   v3.1 — register-driven additions. Transform/opacity (gentle) EXCEPT
   hue-shift + color-fade, which are luminance (intense-opt-in + shared
   <3Hz budget, gated in effects.js). All one-shot, governor-toggled,
   amplitude scaled by --instability (so the 0.72 ceiling clamps them),
   and all listed in BOTH kill blocks below.
   ============================================================ */

/* shudder — a tight high-frequency tremor (transform only) */
body.fx-on .crt.shudder { animation: shudder 0.3s steps(2) 2; }
@keyframes shudder {
  0%, 100% { transform: translate(0, 0); }
  25% { transform: translate(calc(1px + 2px * var(--instability)), 0); }
  50% { transform: translate(calc(-1px - 2px * var(--instability)), 0); }
  75% { transform: translate(0, calc(1px * var(--instability))); }
}

/* chroma-sweep — the RGB split offset sweeps out and back (shadow only, non-lum) */
body.fx-on .crt.chroma-sweep .glitch { animation: chroma-sweep 0.42s ease-in-out 1; }
@keyframes chroma-sweep {
  0%, 100% { text-shadow: -1px 0 0 var(--accent-cyan), 1px 0 0 var(--accent-magenta); }
  50% { text-shadow:
    calc(-4px - 4px * var(--instability)) 0 0 var(--accent-cyan),
    calc(4px + 4px * var(--instability)) 0 0 var(--accent-magenta); }
}

/* color-bleed — a wider colored chromatic smear, briefly tinged amber (non-lum) */
body.fx-on .crt.color-bleed .glitch { animation: color-bleed 0.34s steps(2) 1; }
@keyframes color-bleed {
  0%, 100% { text-shadow: -1px 0 0 var(--accent-cyan), 1px 0 0 var(--accent-magenta); }
  40% { text-shadow:
    calc(-3px - 3px * var(--instability)) 0.5px 0 var(--accent-magenta),
    calc(3px + 3px * var(--instability)) -0.5px 0 var(--accent-cyan),
    0 0 calc(4px * var(--glow)) var(--accent-amber); }
}

/* parallax — the content drifts against the scanband (transform only) */
body.fx-on .crt__screen.parallax .crt__content { animation: parallax 0.52s ease-in-out 1; }
@keyframes parallax {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(calc(3px + 5px * var(--instability)), calc(-1px - 2px * var(--instability))); }
}

/* hue-shift (LUMINANCE, intense-only): a rotation toward magenta and back */
body.fx-on .crt.hue-shift .crt__content { animation: hue-shift 0.36s steps(1) 1; }
@keyframes hue-shift {
  0%, 100% { filter: none; }
  50% { filter: hue-rotate(-50deg) saturate(1.4); }
}

/* color-fade (LUMINANCE, intense-only): a slow tint toward accent, NEVER to white */
body.fx-on .crt.color-fade .crt__content { animation: color-fade 0.6s ease-in-out 1; }
@keyframes color-fade {
  0%, 100% { filter: none; }
  50% { filter: sepia(0.5) hue-rotate(220deg) saturate(1.5); }
}

/* ============================================================
   Phase-3 Committee B — harder felt menace for the mid/late game ("physically polite"
   verdict). crawl/peel/wrench are transform/text-shadow only (gentle default); bruise is the
   one luminance member (intense-opt-in + the shared <3Hz budget in effects.js — a single
   steps(1) flip that DARKENS toward magenta, never toward white). All amplitude-scaled by
   --instability (so the 0.72 ceiling clamps them) and all listed in BOTH kill blocks below.
   ============================================================ */

/* crawl — the content inches sideways then settles, the screen leaning toward you (transform only) */
body.fx-on .crt__screen.crawl .crt__content { animation: crawl 0.46s ease-in-out 1; }
@keyframes crawl {
  0%, 100% { transform: translate(0, 0); }
  30% { transform: translate(calc(2px + 4px * var(--instability)), calc(0.5px * var(--instability))); }
  60% { transform: translate(calc(3px + 6px * var(--instability)), calc(-0.5px * var(--instability))); }
}

/* peel — the RGB split smears UP and to one side then snaps back, glyphs lifting off the glass (shadow only) */
body.fx-on .crt.peel .glitch { animation: peel 0.4s ease-out 1; }
@keyframes peel {
  0%, 100% { text-shadow: -1px 0 0 var(--accent-cyan), 1px 0 0 var(--accent-magenta); }
  45% { text-shadow:
    calc(-2px - 3px * var(--instability)) calc(-1px - 2px * var(--instability)) 0 var(--accent-cyan),
    calc(2px + 3px * var(--instability)) calc(1px + 2px * var(--instability)) 0 var(--accent-magenta); }
}

/* wrench — a hard skew-and-recover, the glass torqued for a beat (transform only) */
body.fx-on .crt.wrench { animation: wrench 0.34s cubic-bezier(.36,.07,.19,.97) 1; }
@keyframes wrench {
  0%, 100% { transform: skewX(0) translate(0, 0); }
  35% { transform: skewX(calc(0.6deg + 1.2deg * var(--instability))) translate(calc(-2px - 3px * var(--instability)), 0); }
  70% { transform: skewX(calc(-0.4deg - 0.8deg * var(--instability))) translate(calc(2px + 2px * var(--instability)), 0); }
}

/* bruise (LUMINANCE, intense-only): a brief darken-and-purple-shift, the screen taking a hit. NEVER toward white. */
body.fx-on .crt.bruise .crt__content { animation: bruise 0.3s steps(1) 1; }
@keyframes bruise {
  0%, 100% { filter: none; }
  50% { filter: brightness(0.6) hue-rotate(-40deg) saturate(1.5); }
}

/* ============================================================
   SAFETY GATES — the master switches.
   When fx-on is absent (governor off), NOTHING here animates.
   reduce-motion / safe-mode additionally hard-disable, so even a
   stray fx-on can't move the screen.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .glitch,
  body.fx-on .glitch,
  body.fx-on .jitter,
  body.fx-on .corrupt,
  body.fx-on .crt__screen.mosh .crt__content,
  body.fx-on .crt__screen.tear,
  body.fx-on .crt.lurch,
  body.fx-on .crt.invert-beat .crt__content,
  body.fx-on .crt.shake,
  body.fx-on .crt.rgb-pulse .glitch,
  body.fx-on .crt__screen.jitter-beat .crt__content,
  body.fx-on .crt__scanband.scan-roll,
  body.fx-on .crt.reading .crt__content,
  body.fx-on .crt.color-flash .crt__content,
  body.fx-on .crt.blackout-blink .crt__content,
  body.fx-on .crt.shudder,
  body.fx-on .crt.chroma-sweep .glitch,
  body.fx-on .crt.color-bleed .glitch,
  body.fx-on .crt__screen.parallax .crt__content,
  body.fx-on .crt.hue-shift .crt__content,
  body.fx-on .crt.color-fade .crt__content,
  body.fx-on .crt__screen.crawl .crt__content,
  body.fx-on .crt.peel .glitch,
  body.fx-on .crt.wrench,
  body.fx-on .crt.bruise .crt__content {
    animation: none !important;
  }
  .glitch { text-shadow: 0 0 calc(2px * var(--glow)) var(--glow-color) !important; }
}

body.safe-mode .glitch,
body.safe-mode.fx-on .glitch,
body.safe-mode.fx-on .jitter,
body.safe-mode.fx-on .corrupt,
body.safe-mode.fx-on .crt__screen.mosh .crt__content,
body.safe-mode.fx-on .crt__screen.tear,
body.safe-mode.fx-on .crt.lurch,
body.safe-mode.fx-on .crt.invert-beat .crt__content,
body.safe-mode.fx-on .crt.shake,
body.safe-mode.fx-on .crt.rgb-pulse .glitch,
body.safe-mode.fx-on .crt__screen.jitter-beat .crt__content,
body.safe-mode.fx-on .crt__scanband.scan-roll,
body.safe-mode.fx-on .crt.reading .crt__content,
body.safe-mode.fx-on .crt.color-flash .crt__content,
body.safe-mode.fx-on .crt.blackout-blink .crt__content,
body.safe-mode.fx-on .crt.shudder,
body.safe-mode.fx-on .crt.chroma-sweep .glitch,
body.safe-mode.fx-on .crt.color-bleed .glitch,
body.safe-mode.fx-on .crt__screen.parallax .crt__content,
body.safe-mode.fx-on .crt.hue-shift .crt__content,
body.safe-mode.fx-on .crt.color-fade .crt__content,
body.safe-mode.fx-on .crt__screen.crawl .crt__content,
body.safe-mode.fx-on .crt.peel .glitch,
body.safe-mode.fx-on .crt.wrench,
body.safe-mode.fx-on .crt.bruise .crt__content {
  animation: none !important;
}
body.safe-mode .glitch {
  text-shadow: 0 0 calc(2px * var(--glow)) var(--glow-color) !important;
}
