/* ==========================================================================
   GP.OS FX layers — georgepeyrouton.org
   Background canvases (#fx-bg WebGL, #fx-matrix rain, #fx-fallback CSS
   space), ambient overlays (#fx-vignette, #fx-scanlines, #fx-noise), the
   warp + glitch overlays and the particle canvas (#fx-top).
   Every element here is created by js/fx.js.
   ========================================================================== */

/* ------------------------------------------------------------ background */
#fx-fallback,
#fx-bg,
#fx-matrix {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  height: 100lvh; /* cover the area behind collapsing mobile URL bars */
  max-width: none;
  z-index: var(--z-bg);
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}

#fx-bg {
  display: block;
  background: var(--bg);
  opacity: 0;
  transition: opacity 1.6s ease;
}
html.fx-bg-ready #fx-bg {
  opacity: 1;
}
html.fx-no-webgl #fx-bg {
  display: none;
}

/* Matrix rain sits above the WebGL canvas (same z-index, later in the DOM). */
#fx-matrix {
  display: block;
  opacity: 0;
  transition: opacity 0.9s ease;
}
#fx-matrix.is-on {
  opacity: 0.5;
}

/* ------------------------------------------- no-WebGL CSS space fallback */
#fx-fallback {
  display: none;
  overflow: hidden;
  perspective: 300px;
  perspective-origin: 50% 60%;
  background:
    linear-gradient(180deg, transparent 58.6%, rgba(var(--accent-2-rgb), 0.38) 60%, transparent 61.4%),
    radial-gradient(ellipse 70% 22% at 50% 60%, rgba(var(--accent-2-rgb), 0.16), transparent 70%),
    radial-gradient(ellipse 55% 40% at 18% 22%, rgba(var(--accent-rgb), 0.13), transparent 70%),
    radial-gradient(ellipse 50% 38% at 82% 30%, rgba(var(--accent-2-rgb), 0.11), transparent 70%),
    radial-gradient(ellipse 40% 30% at 60% 8%, rgba(var(--accent-rgb), 0.07), transparent 70%),
    linear-gradient(180deg, #02030a 0%, #060818 45%, #0e0a24 60%, #05040f 61%, #020309 100%);
}
html.fx-no-webgl #fx-fallback {
  display: block;
}

/* star field: two tiled layers of tiny radial dots, slowly drifting */
#fx-fallback::before {
  content: '';
  position: absolute;
  left: 0;
  right: -240px; /* room for the leftward drift */
  top: -260px;
  height: calc(60% + 260px);
  background-image:
    radial-gradient(1px 1px at 20px 30px, #fff, transparent),
    radial-gradient(1px 1px at 120px 85px, rgba(255, 255, 255, 0.75), transparent),
    radial-gradient(1.5px 1.5px at 60px 150px, rgba(var(--accent-rgb), 0.95), transparent),
    radial-gradient(1px 1px at 175px 195px, #fff, transparent),
    radial-gradient(1.5px 1.5px at 95px 222px, rgba(var(--accent-2-rgb), 0.85), transparent),
    radial-gradient(1px 1px at 210px 118px, rgba(255, 255, 255, 0.6), transparent),
    radial-gradient(2px 2px at 40px 260px, rgba(255, 255, 255, 0.9), transparent),
    radial-gradient(1px 1px at 300px 40px, rgba(255, 255, 255, 0.55), transparent);
  background-size: 230px 240px, 230px 240px, 230px 240px, 230px 240px, 260px 260px, 260px 260px, 330px 310px, 330px 310px;
  -webkit-mask-image: linear-gradient(180deg, #000 70%, transparent);
  mask-image: linear-gradient(180deg, #000 70%, transparent);
  animation: fx-stars-drift 90s linear infinite;
  opacity: 0.85;
}

/* synthwave grid floor */
#fx-fallback::after {
  content: '';
  position: absolute;
  left: -60%;
  width: 220%;
  top: 60%;
  height: 285px;
  transform-origin: 50% 0;
  transform: rotateX(75deg);
  background-image:
    linear-gradient(rgba(var(--accent-2-rgb), 0.55) 2px, transparent 2px),
    linear-gradient(90deg, rgba(var(--accent-rgb), 0.4) 2px, transparent 2px);
  background-size: 40px 40px, 40px 40px;
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 40%);
  mask-image: linear-gradient(180deg, transparent 0%, #000 40%);
  animation: fx-grid-scroll 1.2s linear infinite;
  opacity: 0.7;
}

@keyframes fx-stars-drift {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-230px, 240px, 0); }
}
@keyframes fx-grid-scroll {
  from { background-position: 0 0, 0 0; }
  to { background-position: 0 40px, 0 0; }
}

/* pulse() feedback when there is no shader to flash. It composes with the mode
   filter (--fx-fb-filter) so calm / matrix keep their tint during and after it. */
html.fx-no-webgl.fx-pulsing #fx-fallback {
  animation: fx-fallback-pulse 0.65s ease-out;
}
@keyframes fx-fallback-pulse {
  0% { filter: var(--fx-fb-filter, opacity(1)) brightness(1.9) saturate(1.4); }
  100% { filter: var(--fx-fb-filter, opacity(1)) brightness(1) saturate(1); }
}

/* fallback mode flavours */
html.fx-no-webgl.fx-mode-party #fx-fallback {
  animation: fx-hue 4s linear infinite;
}
html.fx-no-webgl.fx-mode-hyper #fx-fallback::after {
  animation-duration: 0.4s;
}
html.fx-no-webgl.fx-mode-hyper #fx-fallback::before {
  animation-duration: 25s;
}
html.fx-no-webgl.fx-mode-calm #fx-fallback {
  --fx-fb-filter: brightness(0.7) saturate(0.8);
  filter: var(--fx-fb-filter);
}
html.fx-no-webgl.fx-mode-calm #fx-fallback::after {
  animation-duration: 3.5s;
}
html.fx-no-webgl.fx-mode-matrix #fx-fallback {
  --fx-fb-filter: grayscale(1) sepia(1) hue-rotate(65deg) saturate(2.2) brightness(0.55);
  filter: var(--fx-fb-filter);
}

@keyframes fx-hue {
  to { filter: hue-rotate(360deg); }
}

/* ------------------------------------------------------ ambient overlays */
/* Above page content (z 1), below the nav (z 100). Purely decorative. */
#fx-vignette,
#fx-scanlines,
#fx-noise {
  position: fixed;
  pointer-events: none;
  z-index: 90;
  user-select: none;
  -webkit-user-select: none;
}

#fx-vignette {
  inset: 0;
  background: radial-gradient(ellipse 125% 95% at 50% 45%, transparent 58%, rgba(0, 0, 0, 0.42) 100%);
}

#fx-scanlines {
  inset: 0;
  overflow: hidden;
  background: repeating-linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.026) 0,
    rgba(255, 255, 255, 0.026) 1px,
    transparent 1px,
    transparent 3px
  );
}
/* a soft CRT "refresh" band rolling down the screen */
#fx-scanlines::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 24vh;
  background: linear-gradient(180deg, transparent, rgba(var(--accent-rgb), 0.035) 50%, transparent);
  animation: fx-scan-roll 9s linear infinite;
  will-change: transform;
}
@keyframes fx-scan-roll {
  from { transform: translate3d(0, -26vh, 0); }
  to { transform: translate3d(0, 104vh, 0); }
}

/* film grain: an SVG feTurbulence tile, jittered in steps */
#fx-noise {
  inset: -10%;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 200px 200px;
  animation: fx-grain 0.9s steps(1) infinite;
  will-change: transform;
}
@keyframes fx-grain {
  0%, 100% { transform: translate3d(0, 0, 0); }
  10% { transform: translate3d(-5%, -6%, 0); }
  20% { transform: translate3d(-7%, 3%, 0); }
  30% { transform: translate3d(4%, -7%, 0); }
  40% { transform: translate3d(-3%, 7%, 0); }
  50% { transform: translate3d(-6%, 2%, 0); }
  60% { transform: translate3d(6%, 0, 0); }
  70% { transform: translate3d(0, 5%, 0); }
  80% { transform: translate3d(3%, 6%, 0); }
  90% { transform: translate3d(-4%, 4%, 0); }
}

/* mode flavours for the overlays */
html.fx-mode-matrix #fx-scanlines {
  background: repeating-linear-gradient(
    180deg,
    rgba(57, 255, 20, 0.035) 0,
    rgba(57, 255, 20, 0.035) 1px,
    transparent 1px,
    transparent 3px
  );
}
html.fx-mode-matrix #fx-scanlines::after {
  background: linear-gradient(180deg, transparent, rgba(57, 255, 20, 0.05) 50%, transparent);
}
html.fx-mode-calm #fx-noise {
  opacity: 0.03;
}
html.fx-mode-calm #fx-scanlines::after {
  animation-duration: 18s;
}
html.fx-mode-hyper #fx-scanlines::after {
  animation-duration: 3s;
}
html.fx-mode-party #fx-vignette {
  background: radial-gradient(ellipse 125% 95% at 50% 45%, transparent 52%, rgba(var(--accent-2-rgb), 0.2) 100%);
  animation: fx-hue 3s linear infinite;
}

/* ------------------------------------------------------------------ warp */
/* Hyperspace speed lines + edge blur over the content during warp(). */
#fx-warp {
  position: fixed;
  inset: -12%;
  z-index: 89;
  pointer-events: none;
  visibility: hidden;
  opacity: 0;
  background:
    repeating-conic-gradient(
      from 0deg at 50% 50%,
      transparent 0deg 2.1deg,
      rgba(var(--accent-rgb), 0.22) 2.3deg 2.5deg,
      transparent 2.7deg 4.4deg,
      rgba(255, 255, 255, 0.14) 4.5deg 4.6deg
    ),
    radial-gradient(circle at 50% 50%, transparent 30%, rgba(var(--accent-rgb), 0.1) 62%, rgba(2, 3, 10, 0.6) 100%);
  -webkit-mask-image: radial-gradient(circle at 50% 50%, transparent 16%, #000 60%);
  mask-image: radial-gradient(circle at 50% 50%, transparent 16%, #000 60%);
  -webkit-backdrop-filter: blur(3px) saturate(1.35);
  backdrop-filter: blur(3px) saturate(1.35);
}
html.fx-warping #fx-warp {
  visibility: visible;
  animation: fx-warp-flash var(--fx-warp-ms, 900ms) var(--ease-in-out) both;
}
@keyframes fx-warp-flash {
  0% { opacity: 0; transform: scale(1.3) rotate(0deg); }
  38% { opacity: 1; transform: scale(1) rotate(2deg); }
  100% { opacity: 0; transform: scale(0.9) rotate(5deg); }
}
/* backdrop blur is too heavy for many phone GPUs */
html.fx-touch #fx-warp {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* ---------------------------------------------------------------- glitch */
/* Bars are positioned by fx.js every few frames; difference-blend slices the
   page underneath into inverted, offset strips. */
#fx-glitch {
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-fx-top) - 1);
  pointer-events: none;
  overflow: hidden;
  visibility: hidden;
}
html.fx-glitching #fx-glitch {
  visibility: visible;
}
#fx-glitch i {
  position: absolute;
  left: -5%;
  top: 0;
  width: 110%;
  height: 2vh;
  opacity: 0;
  background: var(--accent);
  mix-blend-mode: difference;
  will-change: transform, opacity;
}
#fx-glitch::before {
  content: '';
  position: absolute;
  inset: 0;
  opacity: 0;
  background: repeating-linear-gradient(
    0deg,
    rgba(var(--accent-rgb), 0.07) 0,
    rgba(var(--accent-rgb), 0.07) 2px,
    transparent 2px,
    transparent 4px
  );
}
html.fx-glitching #fx-glitch::before {
  opacity: 1;
  animation: fx-glitch-roll 0.12s steps(2) infinite;
}
@keyframes fx-glitch-roll {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(0, 4px, 0); }
}
html.fx-glitching #fx-scanlines {
  animation: fx-flicker 0.1s steps(2) infinite;
}
@keyframes fx-flicker {
  0% { opacity: 1; filter: brightness(2.6); }
  50% { opacity: 0.35; filter: none; }
  100% { opacity: 1; filter: brightness(2.6); }
}
/* reduced motion: a calm tint instead of strobing slices */
html.fx-rm.fx-glitching #fx-glitch {
  background: rgba(var(--accent-rgb), 0.06);
}
html.fx-rm.fx-glitching #fx-glitch::before,
html.fx-rm.fx-glitching #fx-scanlines {
  animation: none;
}

/* ------------------------------------------------------- particle canvas */
#fx-top {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  z-index: var(--z-fx-top);
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}

/* --------------------------------------------------------- accessibility */
html.fx-rm #fx-noise,
html.fx-rm #fx-scanlines::after,
html.fx-rm #fx-fallback::before,
html.fx-rm #fx-fallback::after,
html.fx-rm.fx-mode-party #fx-vignette,
html.fx-rm.fx-no-webgl.fx-mode-party #fx-fallback {
  animation: none;
}
html.fx-rm #fx-warp {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  #fx-noise,
  #fx-scanlines::after,
  #fx-fallback::before,
  #fx-fallback::after {
    animation: none !important;
  }
  #fx-warp {
    display: none;
  }
}

@media print {
  #fx-fallback,
  #fx-bg,
  #fx-matrix,
  #fx-vignette,
  #fx-scanlines,
  #fx-noise,
  #fx-warp,
  #fx-glitch,
  #fx-top {
    display: none !important;
  }
}
