/* ═══════════════════════════════════════════════════════════════════════
   Phase 6: Horror Atmosphere Effects — Complete Keyframe Registry
   Fog drift · Grain noise · Glitch · Holographic foil · Mesh blobs ·
   Spotlight border · Scanlines · Vignette pulse · Scroll-driven reveals ·
   @starting-style for popovers/dialogs · Per-component contain-intrinsic-size
   ═══════════════════════════════════════════════════════════════════════ */

/* ── GPU off-loading (Phase 1.1) ──────────────────────
   When js/gpu/ambient-renderer.ts successfully spins up a WebGPU/WebGL2
   fullscreen underlay it sets html[data-sgfx-gpu="1"]. The fog, mesh and
   grain are then rendered on the GPU canvas, so the equivalent DOM layers
   are hidden to avoid double-rendering. If the GPU path is unavailable the
   flag is never set and these CSS effects remain the fallback. */
html[data-sgfx-gpu="1"] .bg-fog,
html[data-sgfx-gpu="1"] .mesh-bg,
html[data-sgfx-gpu="1"] .bg-grain {
  display: none !important;
}

/* ── Containment (per-component, not blanket) ────────── */
.bg-fog,
.bg-grain,
.particles,
.hero-floating-icons,
.reality-warp,
#particle-container,
[data-tilt] {
  contain: layout paint style;
}

/* Real per-component intrinsic sizes — replaces blanket 360px */
.kinetic-marquee {
  content-visibility: auto;
  contain-intrinsic-size: auto 48px;
}
.scroll-animate.scroll-animate--sm {
  content-visibility: auto;
  contain-intrinsic-size: auto 280px;
}
.scroll-animate.scroll-animate--md {
  content-visibility: auto;
  contain-intrinsic-size: auto 560px;
}
.scroll-animate {
  content-visibility: auto;
  contain-intrinsic-size: auto 480px;
}
[data-scrub] {
  content-visibility: auto;
  contain-intrinsic-size: auto 600px;
}
[data-parallax] {
  content-visibility: auto;
  contain-intrinsic-size: auto 640px;
}

@media (max-width: 640px) {
  .kinetic-marquee { contain-intrinsic-size: auto 40px; }
  .scroll-animate { contain-intrinsic-size: auto 240px; }
  [data-scrub] { contain-intrinsic-size: auto 300px; }
  [data-parallax] { contain-intrinsic-size: auto 320px; }
}

/* ═══════════════════════════════════════════════════════
   BACKGROUND VIDEO — Full-viewport horror reel + crossfade
   Restored May 2026. Modernized with OKLCH/color-mix and a
   ::view-transition-group hook so route-island-loader.js can
   GPU-composite the swap on Chromium/Safari (graceful fallback
   to plain opacity transition on Firefox <128).
   ═══════════════════════════════════════════════════════ */
.bg-video-wrap {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  /* Subtle radial gradient so the first paint isn't a flat black slab while
     the video is decoding — eases the user into the video reveal. */
  background:
    radial-gradient(ellipse at 50% 35%,
      oklch(14% 0.018 18) 0%,
      oklch(8% 0.012 20) 60%,
      oklch(5% 0.008 20) 100%);
  view-transition-name: bg-video;
}

.bg-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Brighter so the video shows through beautifully as a living background
     while still keeping headline text legible via the darken overlay. */
  filter: brightness(0.85) contrast(1.12) saturate(1.05);
  opacity: 0;
  transition: opacity 1.2s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: opacity;
}

.bg-video.active {
  opacity: 1;
}

/* Homepage hero legibility — dim the active background video and deepen the
   darken overlay so the editorial title/subtitle clear WCAG contrast over
   busy footage. Scoped to the homepage so other routes keep full-strength video. */
body[data-scope="index"] .bg-video.active { opacity: 0.22; }
body[data-scope="index"] .bg-video-darken { opacity: 0.7; }

/* Modern entrance: when the active video first attaches, fade in from 0 via
   @starting-style rather than the JS-driven opacity shim. */
@supports (transition-behavior: allow-discrete) {
  @starting-style {
    .bg-video.active { opacity: 0; }
  }
}

.bg-video-scanlines {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: repeating-linear-gradient(
    0deg,
    transparent 0,
    transparent 2px,
    color-mix(in oklch, black 6%, transparent) 2px,
    color-mix(in oklch, black 6%, transparent) 4px
  );
  mix-blend-mode: multiply;
}

.bg-video-vignette {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: radial-gradient(
    ellipse at 50% 40%,
    transparent 35%,
    color-mix(in oklch, black 30%, transparent) 65%,
    color-mix(in oklch, black 55%, transparent) 100%
  );
}

.bg-video-darken {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  /* Lighter readability scrim — lets the video breathe while keeping text
     legible via the high-contrast title styling and glass-card surfaces. */
  background:
    linear-gradient(180deg,
      color-mix(in oklch, oklch(6% 0.012 20) 18%, transparent) 0%,
      color-mix(in oklch, oklch(6% 0.012 20) 8%, transparent) 40%,
      color-mix(in oklch, oklch(6% 0.012 20) 28%, transparent) 100%);
}

/* GPU-composited crossfade when route-island-loader wraps the
   swap in document.startViewTransition(). Falls back silently
   when ::view-transition-* isn't supported. */
@supports (view-transition-name: x) {
  ::view-transition-group(bg-video) {
    animation-duration: 1.4s;
    animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  }
  ::view-transition-old(bg-video),
  ::view-transition-new(bg-video) {
    animation-duration: 1.4s;
    mix-blend-mode: plus-lighter;
  }
}

/* ═══════════════════════════════════════════════════════
   CROSS-DOCUMENT (MPA) VIEW TRANSITIONS — Phase 2.2
   Opts every same-origin navigation into the View Transitions
   API so moving between pages (and into a game shell) plays a
   cinematic "fog wipe" / "portal tear" instead of a hard reload.
   Browsers without support ignore @view-transition and navigate
   normally; js/view-transitions.ts assigns the shared element
   names. Pairs with <meta name="view-transition" content="same-origin">.
   ═══════════════════════════════════════════════════════ */
@view-transition {
  navigation: auto;
}

@supports (view-transition-name: x) {
  /* Default cinematic between documents: outgoing page sinks into crimson
     fog, incoming page rises through it with a vertical clip reveal. */
  @keyframes sgai-vt-old {
    from { opacity: 1; filter: blur(0) saturate(1); transform: scale(1); }
    to   { opacity: 0; filter: blur(8px) saturate(1.5); transform: scale(1.03); }
  }
  @keyframes sgai-vt-new {
    from {
      opacity: 0;
      filter: blur(10px) brightness(0.55);
      transform: scale(1.02);
      clip-path: inset(0 0 100% 0);
    }
    to {
      opacity: 1;
      filter: blur(0) brightness(1);
      transform: scale(1);
      clip-path: inset(0 0 0 0);
    }
  }
  ::view-transition-old(root) {
    animation: sgai-vt-old 420ms cubic-bezier(0.4, 0, 0.2, 1) both;
  }
  ::view-transition-new(root) {
    animation: sgai-vt-new 520ms cubic-bezier(0.22, 1, 0.36, 1) both;
    mix-blend-mode: normal;
  }

  /* Shared elements (tagged in js/view-transitions.ts) morph smoothly. */
  ::view-transition-group(hero-title),
  ::view-transition-group(site-nav),
  ::view-transition-group(site-footer),
  ::view-transition-group(featured-visual) {
    animation-duration: 520ms;
    animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
  }

  /* "Portal tear" — entering a game shell. The element named `game-portal`
     rips open from the centre line. */
  @keyframes sgai-vt-portal-in {
    from { clip-path: polygon(50% 0, 50% 0, 50% 100%, 50% 100%); opacity: 0.2; }
    to   { clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); opacity: 1; }
  }
  @keyframes sgai-vt-portal-out {
    from { clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); opacity: 1; }
    to   { clip-path: polygon(50% 0, 50% 0, 50% 100%, 50% 100%); opacity: 0; }
  }
  ::view-transition-new(game-portal) {
    animation: sgai-vt-portal-in 600ms cubic-bezier(0.83, 0, 0.17, 1) both;
  }
  ::view-transition-old(game-portal) {
    animation: sgai-vt-portal-out 460ms cubic-bezier(0.83, 0, 0.17, 1) both;
  }
}

/* Reduced motion: collapse cross-document transitions to an instant swap. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(root),
  ::view-transition-new(root),
  ::view-transition-old(game-portal),
  ::view-transition-new(game-portal) {
    animation: none !important;
  }
}

@media (prefers-reduced-motion: reduce) {
  .bg-video {
    display: none;
  }
}

/* ═══════════════════════════════════════════════════════
   FOG DRIFT — 3-layer parallax mist animation
   ═══════════════════════════════════════════════════════ */
.bg-fog {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  opacity: var(--fog-far-opacity, 0.18);
  background:
    radial-gradient(ellipse at 30% 80%, var(--fog-tint-far, rgba(204,17,34,0.1)), transparent 60%),
    radial-gradient(ellipse at 70% 20%, var(--fog-tint-mid, rgba(100,20,80,0.08)), transparent 50%),
    radial-gradient(ellipse at 50% 50%, var(--fog-tint-near, rgba(204,170,0,0.05)), transparent 40%);
  animation: fogDrift 28s ease-in-out infinite alternate;
}

@keyframes fogDrift {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  25%  { transform: translate3d(2vw, -1vh, 0) scale(1.04); }
  50%  { transform: translate3d(-1vw, -3vh, 0) scale(0.97); }
  75%  { transform: translate3d(-3vw, 1vh, 0) scale(1.02); }
  100% { transform: translate3d(1vw, 2vh, 0) scale(1); }
}

/* Three-layer explicit drifts for fine-grained control */
.fog-layer--far  { animation: fogDriftFar  40s linear infinite; }
.fog-layer--mid  { animation: fogDriftMid  28s linear infinite reverse; }
.fog-layer--near { animation: fogDriftNear 20s linear infinite; }

@keyframes fogDriftFar {
  0%   { background-position: 0% 50%, 0% 0%; }
  100% { background-position: 200% 50%, 100% 100%; }
}
@keyframes fogDriftMid {
  0%   { background-position: 50% 0%, 0% 0%; }
  100% { background-position: 150% 100%, 100% 100%; }
}
@keyframes fogDriftNear {
  0%   { background-position: 0% 100%, 0% 0%; }
  100% { background-position: 80% 0%, 100% 100%; }
}

/* ═══════════════════════════════════════════════════════
   GRAIN NOISE — Animated film grain
   ═══════════════════════════════════════════════════════ */
.bg-grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: var(--z-overlay-fx);
  opacity: var(--grain-intensity, 0.04);
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
  background-repeat: repeat;
  background-size: 128px 128px;
  animation: grainShift 0.8s steps(6) infinite;
}

@keyframes grainShift {
  0%   { background-position: 0% 0%; }
  20%  { background-position: -23px 41px; }
  40%  { background-position: 17px -33px; }
  60%  { background-position: -41px 12px; }
  80%  { background-position: 31px 28px; }
  100% { background-position: -8px -19px; }
}

/* ═══════════════════════════════════════════════════════
   GLITCH — Hierarchical intensity: micro (cards), page (hero), section (dividers)
   ═══════════════════════════════════════════════════════ */

/* Micro — triggered on hover for game cards, buttons */
@keyframes glitchMicro {
  0%   { transform: translate(0); }
  7%   { clip-path: none; }
  8%   { clip-path: inset(20% 0 70% 0); transform: translate(-3px, 0); }
  9%   { clip-path: inset(70% 0 20% 0); transform: translate(3px, -1px); }
  10%  { clip-path: none; transform: translate(0); }
  100% { clip-path: none; transform: translate(0); }
}

/* Page-level — hero glitch on first load */
@keyframes glitchPage {
  0%   { transform: translate(0); opacity: 1; }
  5%   { transform: translate(-8px, 2px); opacity: 0.8; }
  5.5% { transform: translate(6px, -1px); opacity: 0.9; }
  6%   { transform: translate(0); opacity: 1; }
  8%   { transform: translate(4px, 3px); clip-path: inset(30% 0 60% 0); }
  8.3% { transform: translate(-5px, -2px); clip-path: inset(60% 0 30% 0); }
  8.6% { transform: translate(0); clip-path: none; opacity: 1; }
  100% { transform: translate(0); opacity: 1; clip-path: none; }
}

/* Section — triggered on scroll into view */
@keyframes glitchSection {
  0%   { transform: translate(0); }
  10%  { transform: translate(var(--glitch-offset-x, -4px), var(--glitch-offset-y, 0px)); }
  12%  { transform: translate(calc(var(--glitch-offset-x, 0) * -1.5), 2px); filter: hue-rotate(var(--glitch-hue-shift, 12deg)); }
  14%  { transform: translate(0); filter: hue-rotate(0deg); }
  100% { transform: translate(0); filter: hue-rotate(0deg); }
}

/* Hue shift ghost — color-channel split effect */
@keyframes glitchHue {
  0%   { text-shadow: none; }
  8%   { text-shadow: var(--glitch-color-r, rgba(204,17,34,0.6)) 2px 0, var(--glitch-color-c, rgba(6,182,212,0.6)) -2px 0; }
  10%  { text-shadow: var(--glitch-color-r, rgba(204,17,34,0.3)) -1px 0, var(--glitch-color-c, rgba(6,182,212,0.3)) 1px 0; }
  12%  { text-shadow: none; }
  100% { text-shadow: none; }
}

/* Clip shift — bar-shaped slice reveal */
@keyframes glitchClip {
  0%   { clip-path: none; }
  3%   { clip-path: inset(15% 0 75% 0); }
  5%   { clip-path: inset(75% 0 15% 0); }
  6%   { clip-path: none; }
  9%   { clip-path: inset(40% 0 40% 0); }
  10%  { clip-path: none; }
  100% { clip-path: none; }
}

/* ═══════════════════════════════════════════════════════
   HOLOGRAPHIC FOIL — Iridescent conic gradient rotation
   ═══════════════════════════════════════════════════════ */
@keyframes foilSpin {
  0%   { --foil-angle: 0deg; --foil-brightness: 1; }
  25%  { --foil-brightness: 1.15; }
  50%  { --foil-brightness: 0.9; }
  75%  { --foil-brightness: 1.1; }
  100% { --foil-angle: 360deg; --foil-brightness: 1; }
}

@keyframes foilShimmer {
  0%   { background-position: 0% 50%; }
  50%  { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

/* ═══════════════════════════════════════════════════════
   MESH GRADIENT BLOBS — Organic lava-lamp movement
   ═══════════════════════════════════════════════════════ */
.mesh-bg {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    radial-gradient(circle at 20% 80%, var(--mesh-blob-1, rgba(204,17,34,0.08)) 0%, transparent 50%),
    radial-gradient(circle at 80% 20%, var(--mesh-blob-2, rgba(100,20,80,0.06)) 0%, transparent 45%),
    radial-gradient(circle at 50% 50%, var(--mesh-blob-3, rgba(204,170,0,0.04)) 0%, transparent 40%);
  animation: meshDrift 22s ease-in-out infinite alternate;
}

@keyframes meshDrift {
  0%   { background-position: 20% 80%, 80% 20%, 50% 50%; }
  33%  { background-position: 30% 70%, 70% 30%, 40% 60%; }
  66%  { background-position: 15% 85%, 85% 15%, 60% 40%; }
  100% { background-position: 25% 75%, 75% 25%, 50% 50%; }
}

/* Individual blob keyframes for independent control */
@keyframes meshDrift1 {
  0%   { transform: translate(0, 0) scale(1); }
  25%  { transform: translate(4vw, -2vh) scale(1.1); }
  50%  { transform: translate(-2vw, 3vh) scale(0.9); }
  75%  { transform: translate(-3vw, -1vh) scale(1.05); }
  100% { transform: translate(1vw, 2vh) scale(1); }
}
@keyframes meshDrift2 {
  0%   { transform: translate(0, 0) scale(1); }
  25%  { transform: translate(-3vw, 2vh) scale(1.08); }
  50%  { transform: translate(4vw, -1vh) scale(0.92); }
  75%  { transform: translate(-1vw, 3vh) scale(1.02); }
  100% { transform: translate(2vw, -2vh) scale(1); }
}
@keyframes meshDrift3 {
  0%   { transform: translate(0, 0) scale(1); }
  33%  { transform: translate(2vw, 3vh) scale(0.95); }
  66%  { transform: translate(-4vw, -2vh) scale(1.1); }
  100% { transform: translate(1vw, -1vh) scale(1); }
}

/* ═══════════════════════════════════════════════════════
   SPOTLIGHT BORDER — Radial gradient tracking pointer
   ═══════════════════════════════════════════════════════ */
@keyframes spotlightPulse {
  0%   { opacity: 0.6; }
  50%  { opacity: 0.9; }
  100% { opacity: 0.6; }
}
@keyframes spotlightBreath {
  0%   { --spotlight-blur: 40px; }
  50%  { --spotlight-blur: 80px; }
  100% { --spotlight-blur: 40px; }
}

/* ═══════════════════════════════════════════════════════
   SCANLINES — CRT vertical overlay
   ═══════════════════════════════════════════════════════ */
@keyframes scanlineScroll {
  0%   { transform: translateY(0); }
  100% { transform: translateY(4px); }
}
.scanlines-overlay {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: var(--z-overlay-fx);
  opacity: 0.03;
  background: repeating-linear-gradient(
    0deg,
    transparent,
    transparent 2px,
    rgba(0,0,0,0.2) 2px,
    rgba(0,0,0,0.2) 4px
  );
  animation: scanlineScroll 0.15s linear infinite;
}

/* ═══════════════════════════════════════════════════════
   VIGNETTE — Breathing dark border
   ═══════════════════════════════════════════════════════ */
.vignette {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 2;
  background: radial-gradient(ellipse at center, transparent 55%, rgba(0,0,0,0.55) 100%);
  animation: vignetteBreath 8s ease-in-out infinite alternate;
}
@keyframes vignetteBreath {
  0%   { opacity: 0.7; }
  50%  { opacity: 0.9; }
  100% { opacity: 0.7; }
}

/* ═══════════════════════════════════════════════════════
   PARALLAX — Multi-layer scroll-bound offset
   ═══════════════════════════════════════════════════════ */
@keyframes parallaxBg {
  0%   { --parallax-bg-y: 0%; }
  100% { --parallax-bg-y: 20%; }
}
@keyframes parallaxMid {
  0%   { --parallax-mid-y: 0%; }
  100% { --parallax-mid-y: 12%; }
}
@keyframes parallaxFg {
  0%   { --parallax-fg-y: 0%; }
  100% { --parallax-fg-y: 6%; }
}

/* ═══════════════════════════════════════════════════════
   MAGNETIC BUTTON — Pointer-tracking pull
   ═══════════════════════════════════════════════════════ */
@keyframes magneticSnap {
  0%   { --magnetic-dx: 0; --magnetic-dy: 0; }
  100% { --magnetic-dx: 0; --magnetic-dy: 0; }
}
@keyframes magneticReturn {
  0%   { transform: translate3d(calc(var(--magnetic-dx, 0) * 1px), calc(var(--magnetic-dy, 0) * 1px), 0) scale(1.04); }
  100% { transform: translate3d(0, 0, 0) scale(1); }
}

/* ═══════════════════════════════════════════════════════
   KINETIC MARQUEE — Endless horizontal text bands
   ═══════════════════════════════════════════════════════ */
.kinetic-marquee {
  overflow: hidden;
  white-space: nowrap;
  font-family: var(--font-display, Fraunces, Georgia, serif);
  font-size: var(--marquee-font-size, 2rem);
  color: var(--marquee-color, rgba(204,17,34,0.3));
  letter-spacing: 0.08em;
}
.kinetic-marquee__track {
  display: inline-block;
  animation: marqueeScroll var(--marquee-speed, 30s) linear infinite;
}
@keyframes marqueeScroll {
  0%   { transform: translateX(0); }
  100% { transform: translateX(-50%); }
}

/* ═══════════════════════════════════════════════════════
   PULSE EFFECTS — Fear meter, heartbeat, blood drip
   ═══════════════════════════════════════════════════════ */
@keyframes fearPulse {
  0%   { --fear-meter-intensity: 0.15; }
  2%   { --fear-meter-intensity: 0.42; }
  4%   { --fear-meter-intensity: 0.18; }
  6%   { --fear-meter-intensity: 0.38; }
  8%   { --fear-meter-intensity: 0.15; }
  100% { --fear-meter-intensity: 0.15; }
}
@keyframes heartbeatPulse {
  0%   { transform: scale(1); }
  15%  { transform: scale(1.08); }
  30%  { transform: scale(1); }
  45%  { transform: scale(1.05); }
  60%  { transform: scale(1); }
  100% { transform: scale(1); }
}
@keyframes bloodDrip {
  0%   { transform: translateY(-100%); opacity: 0; }
  10%  { opacity: 1; }
  90%  { opacity: 1; }
  100% { transform: translateY(100%); opacity: 0; }
}

/* ═══════════════════════════════════════════════════════
   TEXT EFFECTS — Scramble, reveal, stroke draw
   ═══════════════════════════════════════════════════════ */
@keyframes textScramble {
  0%   { opacity: 1; }
  15%  { opacity: 0.6; }
  30%  { opacity: 1; }
  45%  { opacity: 0.3; }
  50%  { opacity: 1; }
  100% { opacity: 1; }
}
@keyframes textRevealMask {
  0%   { clip-path: inset(0 100% 0 0); }
  100% { clip-path: inset(0 0 0 0); }
}
@keyframes textRevealBlur {
  0%   { filter: blur(12px); opacity: 0; }
  100% { filter: blur(0); opacity: 1; }
}

/* ═══════════════════════════════════════════════════════
   SCROLL-DRIVEN ANIMATIONS (animation-timeline: view/scroll)
   ═══════════════════════════════════════════════════════ */
@supports (animation-timeline: view()) {
  /* Section entrance reveals via scroll */
  .scroll-animate {
    animation: viewReveal linear both;
    animation-timeline: view();
    animation-range: entry 10% cover 30%;
  }
  .scroll-animate--sm {
    animation: viewRevealSm linear both;
    animation-timeline: view();
    animation-range: entry 5% cover 25%;
  }
  /* Progress bars tied to scroll */
  .scroll-progress {
    animation: scrollProgress linear both;
    animation-timeline: scroll(root);
  }
  /* Parallax via scroll */
  [data-scrub] {
    animation: parallaxBg linear both;
    animation-timeline: scroll(root);
  }
}

@keyframes viewReveal {
  0%   { opacity: 0; transform: translate3d(0, 32px, 0) scale(0.96); filter: blur(8px); }
  100% { opacity: 1; transform: translate3d(0, 0, 0) scale(1); filter: blur(0); }
}
@keyframes viewRevealSm {
  0%   { opacity: 0; transform: translate3d(0, 16px, 0); }
  100% { opacity: 1; transform: translate3d(0, 0, 0); }
}
@keyframes scrollProgress {
  0%   { transform: scaleX(0); }
  100% { transform: scaleX(1); }
}

/* ═══════════════════════════════════════════════════════
   @STARTING-STYLE — Enter animations for popovers/dialogs
   ═══════════════════════════════════════════════════════ */

/* Popovers (sort, filter dropdowns) */
[popover] {
  transition:
    opacity var(--dur-fast, 160ms) var(--ease-emphasized, ease-out),
    transform var(--dur-fast, 160ms) var(--ease-emphasized, ease-out),
    overlay var(--dur-fast, 160ms) var(--ease-emphasized, ease-out) allow-discrete,
    display var(--dur-fast, 160ms) var(--ease-emphasized, ease-out) allow-discrete;
}
@starting-style {
  [popover]:popover-open {
    opacity: 0;
    transform: translateY(8px) scale(0.96);
  }
}

/* Dialogs (upgrade, modal) */
.upgrade-dialog {
  transition:
    opacity var(--dur-normal, 240ms) var(--ease-emphasized, ease-out),
    transform var(--dur-normal, 240ms) var(--ease-emphasized, ease-out),
    overlay var(--dur-normal, 240ms) var(--ease-emphasized, ease-out) allow-discrete,
    display var(--dur-normal, 240ms) var(--ease-emphasized, ease-out) allow-discrete;
}
@starting-style {
  .upgrade-dialog[open] {
    opacity: 0;
    transform: scale(0.9);
  }
}

/* backdrop fade */
.upgrade-dialog::backdrop {
  transition: opacity var(--dur-normal, 240ms) ease;
  background: rgba(0, 0, 0, 0.72);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
@starting-style {
  .upgrade-dialog[open]::backdrop {
    opacity: 0;
  }
}

/* ═══════════════════════════════════════════════════════
   HOVER TRIGGERED GLITCH — Game cards, premium elements
   ═══════════════════════════════════════════════════════ */
@keyframes inputGlitch {
  0%   { box-shadow: 0 0 0 2px var(--accent-red-glow, rgba(204,17,34,0.3)); }
  25%  { box-shadow: 0 0 0 2px var(--accent-red, #cc1122), 0 0 12px var(--accent-red-glow, rgba(204,17,34,0.4)); }
  50%  { box-shadow: 0 0 0 2px rgba(204,17,34,0.1); }
  75%  { box-shadow: 0 0 0 2px var(--accent-red, #cc1122), 0 0 20px var(--accent-red-glow, rgba(204,17,34,0.5)); }
  100% { box-shadow: 0 0 0 2px var(--accent-red-glow, rgba(204,17,34,0.3)); }
}

/* ═══════════════════════════════════════════════════════
   CTA BUTTON PULSE — Play Now, Subscribe, CTA buttons
   ═══════════════════════════════════════════════════════ */
@keyframes ctaGlowPulse {
  0%   { box-shadow: 0 0 0 0 var(--accent-red, rgba(204,17,34,0.4)); }
  70%  { box-shadow: 0 0 0 12px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
@keyframes ctaShine {
  0%   { left: -100%; }
  100% { left: 150%; }
}

/* ═══════════════════════════════════════════════════════
   FLAME / CANDLE FLICKER — Ambient horror atmosphere
   ═══════════════════════════════════════════════════════ */
@keyframes flameFlicker {
  0%   { opacity: 0.9; transform: scale(1, 1) rotate(0deg); }
  25%  { opacity: 0.6; transform: scale(0.9, 1.1) rotate(-3deg); }
  50%  { opacity: 0.85; transform: scale(1.05, 0.95) rotate(2deg); }
  75%  { opacity: 0.5; transform: scale(0.85, 1.15) rotate(-1deg); }
  100% { opacity: 0.9; transform: scale(1, 1) rotate(0deg); }
}
@keyframes candleShadow {
  0%   { transform: scaleX(1); filter: blur(2px); }
  50%  { transform: scaleX(1.3); filter: blur(4px); }
  100% { transform: scaleX(1); filter: blur(2px); }
}

/* ═══════════════════════════════════════════════════════
   SKELETON SHIMMER — Universal loading skeleton
   ═══════════════════════════════════════════════════════ */
@keyframes skeletonShimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* ═══════════════════════════════════════════════════════
   GHOST TRAIL / SHADOW DETACH — Phase animation
   ═══════════════════════════════════════════════════════ */
@keyframes ghostTrail {
  0%   { opacity: 0.4; transform: translateX(-10px); }
  100% { opacity: 0; transform: translateX(10px); }
}
@keyframes shadowDetach {
  0%   { transform: translate(0, 0) scale(1); opacity: 0.6; }
  50%  { transform: translate(12px, -8px) scale(0.9); opacity: 0.2; }
  100% { transform: translate(0, 0) scale(1); opacity: 0.6; }
}
@keyframes staticBurst {
  0%   { clip-path: inset(0 0 100% 0); }
  100% { clip-path: inset(0); }
}

/* ═══════════════════════════════════════════════════════
   REDUCED-MOTION OVERRIDES
   ───────────────────────────────────────────────────────
   2026 Foundation Pass: both the system signal
   (prefers-reduced-motion) AND the comfort-center toggles
   (html[data-comfort-reduced-motion="true"],
    html[data-comfort-disable-flashing="true"]) drop all
   heavy motion to a static state. The user-motion-scale
   custom property (written by ui-preferences.ts) lets JS
   animations also opt-in to scaling down.
   ═══════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  .bg-fog,
  .bg-grain,
  .particles,
  .hero-floating-icons,
  .kinetic-marquee,
  .reality-warp,
  #particle-container,
  .scanlines-overlay,
  .mesh-bg,
  .glitch-fx,
  .spotlight-border::before,
  .flash-trigger,
  .horror-flash {
    animation: none !important;
    transform: none !important;
  }

  /* Disable scroll-driven animations */
  .scroll-animate,
  .scroll-animate--sm,
  .scroll-progress,
  [data-scrub] {
    animation: none !important;
    animation-timeline: auto !important;
  }

  /* Instant transitions only */
  [popover],
  .upgrade-dialog {
    transition: none !important;
  }

  /* Static vignette */
  .vignette { animation: none !important; opacity: 0.7; }
}

/* ─── Comfort-center explicit motion + flash overrides ─── */
html[data-comfort-reduced-motion="true"] .bg-fog,
html[data-comfort-reduced-motion="true"] .bg-grain,
html[data-comfort-reduced-motion="true"] .particles,
html[data-comfort-reduced-motion="true"] .hero-floating-icons,
html[data-comfort-reduced-motion="true"] .kinetic-marquee,
html[data-comfort-reduced-motion="true"] .reality-warp,
html[data-comfort-reduced-motion="true"] #particle-container,
html[data-comfort-reduced-motion="true"] .scanlines-overlay,
html[data-comfort-reduced-motion="true"] .mesh-bg,
html[data-comfort-reduced-motion="true"] .glitch-fx,
html[data-comfort-reduced-motion="true"] .spotlight-border::before {
  animation: none !important;
  transform: none !important;
}

html[data-comfort-disable-flashing="true"] .flash-trigger,
html[data-comfort-disable-flashing="true"] .horror-flash,
html[data-comfort-disable-flashing="true"] .glitch-fx,
html[data-comfort-disable-flashing="true"] .scanlines-overlay {
  animation: none !important;
  opacity: 0 !important;
}

/* When the preferences coordinator scales motion to 0, neutralize
   JS-driven CSS variables that fuel keyframes. Effects that read
   --user-motion-scale (e.g. parallax magnitude) collapse cleanly. */
:root[style*="--user-motion-scale: 0"] .bg-fog,
:root[style*="--user-motion-scale: 0"] .scanlines-overlay,
:root[style*="--user-motion-scale: 0"] .glitch-fx {
  animation-play-state: paused !important;
}
