/* ============================================================================
   AI WINGMAN "WOW" EFFECTS LAYER, shared across every CLIENT-FACING surface.
   Built once, imported everywhere: /demo (all seven sections), /portfolio, /gpts.
   NEVER on the private /ceo cockpit.

   Four effects, in the order the spec names them:
     1. Cursor spotlight glow      .fx-spotlight       (JS sets --fx-x / --fx-y)
     2. Gold shimmer sweep         .fx-shimmer         (pure CSS)
     3. Aurora gradient mesh       .fx-aurora          (pure CSS)
     4. Magnetic buttons + tilt    .fx-magnetic .fx-tilt (JS sets transforms)

   DESIGN LAW: elegant, never gaudy. Every effect is a whisper. If you can name
   the effect while reading the page, it is too strong.

   PERFORMANCE LAW: CSS-first. Only transform and opacity are animated, so every
   effect stays on the compositor and never triggers layout or paint. The JS
   companion is tiny, passive-listener, and rAF-throttled. Lighthouse 90+ mobile.

   ACCESSIBILITY LAW, non-negotiable: prefers-reduced-motion: reduce disables ALL
   of it, globally, in one block at the bottom of this file. Every page must stay
   complete and beautiful with motion off, so nothing here may carry meaning: no
   effect reveals content, changes contrast, or is the only affordance for a
   control. They are all decoration over a page that already works.
   ============================================================================ */

:root {
  /* Pointer position in px, written by wow.js. Defaults put the spotlight
     off-canvas so nothing flashes before the first pointer event. */
  --fx-x: -9999px;
  --fx-y: -9999px;
  /* Brand palette, duplicated here so this file stands alone on any surface.
     /demo uses --c-violet etc, /portfolio uses --gold; this layer needs neither. */
  --fx-gold: #C9A84C;
  --fx-gold-hi: #E8C96A;
  --fx-violet: #c084fc;
  --fx-cyan: #22d3ee;
  --fx-indigo: #818cf8;
}

/* ---------------------------------------------------------------------------
   1. CURSOR SPOTLIGHT GLOW
   A soft radial light that follows the pointer and lifts whatever it passes
   over. One fixed, pointer-events:none layer for the whole page; it is never
   in the way of a click. Sits above the background and below all content.
   --------------------------------------------------------------------------- */
/* LAYER ORDER, the thing that makes this readable instead of a cracked screen
   (Ray, 2026-07-23: the waves were painting IN FRONT of the cards).
     0  .fx-waves      ambient motion, BEHIND everything the visitor reads
     1  .fx-lift       the page's own content, lifted above the waves
     2  .fx-spotlight  the pointer light, deliberately still ON TOP, because it
                       is meant to lift what it passes over and it is only a few
                       percent alpha
   Both page roots (.app on /demo, body on /portfolio) end their background stack
   in a SOLID colour, so the waves cannot simply be sunk to z-index -1: they would
   be painted over. The content gets lifted instead. */
/* CARDS ARE THE OCCLUDERS. The waves sit at z-index 1 above the wrapper
   backgrounds; anything that should hide them (a card the visitor is reading)
   is raised to z-index 2. adopt() in wow.js tags every card with .fx-tilt, so
   pinning the tilt layer to z-index 2 hides the waves behind cards and leaves
   them visible in every gap and behind plain copy. Text is intentionally NOT
   raised: a faint line drifting behind body copy is the alive look Ray liked;
   only the cards read as "cracked" when a line crosses them. */
.fx-tilt { position: relative; z-index: 2; }
/* The entry banner and any glass panel that is a reading surface but not a
   .fx-tilt card opts in with this. */
.fx-occlude { position: relative; z-index: 2; }

.fx-spotlight {
  position: fixed;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: radial-gradient(
    circle 320px at var(--fx-x) var(--fx-y),
    rgba(201, 168, 76, 0.10) 0%,
    rgba(192, 132, 252, 0.055) 38%,
    transparent 68%
  );
  opacity: 0;
  transition: opacity 0.5s ease;
  will-change: background;
}
/* Only fades in once the pointer is actually on the page. */
.fx-spotlight.fx-on { opacity: 1; }

/* Touch devices get a slow ambient drift instead of a pointer follow: same
   light, moving on its own, so a phone is not just a dead flat page. */
@media (hover: none) {
  .fx-spotlight {
    background: radial-gradient(
      circle 300px at 50% 40%,
      rgba(201, 168, 76, 0.09) 0%,
      rgba(192, 132, 252, 0.05) 40%,
      transparent 70%
    );
    opacity: 1;
    animation: fx-drift 26s ease-in-out infinite;
  }
}
@keyframes fx-drift {
  0%   { transform: translate3d(-8%, -6%, 0) scale(1); }
  33%  { transform: translate3d(9%, 5%, 0) scale(1.08); }
  66%  { transform: translate3d(-5%, 8%, 0) scale(0.96); }
  100% { transform: translate3d(-8%, -6%, 0) scale(1); }
}

/* ---------------------------------------------------------------------------
   1b. AMBIENT WAVES (Ray, 2026-07-23)
   The reason this exists: the card tilt and the magnetic buttons are POINTER
   effects, and Ray's primary surface is the installed PWA on a phone, where
   hover never fires. Without this the app is a still page. These waves give the
   background slow, self-driven life that needs no pointer at all, which is the
   "Spline-like subtle wave movement" he asked for at a fraction of the weight.

   HOW: three sine bands drawn as inline SVG data URIs (no network request, no
   library) tiled horizontally and translated at different speeds and directions.
   Only transform animates, so this is one compositor layer per band and costs
   effectively nothing per frame.

   VISIBILITY (Ray, 2026-07-23: "on the published web version i see no animation").
   The lines sit behind the content AND behind .stage-runner, whose background is
   60 percent opaque, so the first pass at ~12 percent effective alpha was
   invisible. Strokes are now ~0.6 alpha at 2.4px and bands ~0.9 opacity, which
   reads clearly in the transparent regions (behind body copy, between cards) and
   dims politely where a card sits over it. Still subtle, still semi-transparent,
   but actually THERE. If it ever reads as busy, lower the band opacities first.
   --------------------------------------------------------------------------- */
.fx-waves {
  /* VIEWPORT-FIXED, not page-anchored: the field must be there wherever the
     visitor is on the page, so it stays put while the content scrolls past it.
     Ray, 2026-07-23: "it doesn't appear to stay in view on scroll". */
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
/* FIVE inline-SVG bands spread across the FULL viewport height, each an <svg>
   element (built in wow.js), NOT a CSS-background data URI. Every band is fixed
   full-width and drifts horizontally; because each path draws its periodic wave
   twice, translating by -50 percent loops seamlessly. They sit behind the copy
   and behind the cards (cards are z-index 2 in the tilt rule above), so the
   motion reads in the gaps and behind plain text but never crosses a card. */
.fx-waves .fx-wave {
  position: absolute;
  /* left:0 (NOT -50%) so the band always spans the viewport. The element is 200%
     wide and the keyframe translates it by -50% of ITS OWN width (= one full
     viewport). Anchored at left:0 it covers [0,200%] at rest and [-100%,100%] at
     the end of the loop, so the viewport [0,100%] is filled at every phase. With
     left:-50% the end phase left the right half of the screen bare, which is the
     "top wave is not fully across the screen" gap Ray reported 2026-07-24. */
  left: 0;
  width: 200%;
  height: 26vh;
  overflow: visible;
  will-change: transform;
}
.fx-wave.fx-wave-c { animation: fx-wave-x 78s linear infinite; }
.fx-wave.fx-wave-b { animation: fx-wave-x 52s linear infinite reverse; }
.fx-wave.fx-wave-a { animation: fx-wave-x 64s linear infinite; }
.fx-wave.fx-wave-d { animation: fx-wave-x 70s linear infinite reverse; }
.fx-wave.fx-wave-e { animation: fx-wave-x 58s linear infinite; }
/* One keyframe for all: move left by half the element's own width. Each band
   contains its wave twice, so half a width is exactly one pattern = seamless. */
@keyframes fx-wave-x {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}
/* Phones: the waves looked SCRUNCHED because the same ~8 humps got squeezed into a
   narrow screen (Ray, 2026-07-25: "i dont like how they are scrunched on the phone,
   i want those wide as well"). The element is 200% wide on desktop; widening it to
   640% on a phone spreads the SAME periodic path over far more width, so each hump
   is roughly as broad as on desktop (~2.5 humps per viewport instead of ~8). left:0
   + a -50% translate is still a seamless loop at any width. All four bands stay on
   so it reads as full, not sparse. */
@media (max-width: 700px) {
  .fx-waves .fx-wave { width: 640%; height: 32vh; }
}

/* ---------------------------------------------------------------------------
   1c. FIXED AURORA LAYER (Ray, 2026-07-24: "why is the aurora not scrolling with
   the page"). The old aurora lived on the scrolling content, so it slid away.
   This one is viewport-fixed like the waves, so it is always present and sweeps
   across the screen while you scroll. It shows on the demo sections that do NOT
   carry the waves; wow.js toggles it. Mostly transparent, calm.
   --------------------------------------------------------------------------- */
.fx-aurora-bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
.fx-aurora-bg span {
  position: absolute;
  border-radius: 50%;
  filter: blur(72px);
  opacity: 0.5;
  will-change: transform;
}
.fx-aurora-bg span:nth-child(1) {
  width: 62vw; height: 62vw; max-width: 780px; max-height: 780px;
  top: -12%;
  background: radial-gradient(circle, rgba(192,132,252,0.34) 0%, transparent 68%);
  animation: fx-aurora-a 30s ease-in-out infinite;
}
.fx-aurora-bg span:nth-child(2) {
  width: 54vw; height: 54vw; max-width: 700px; max-height: 700px;
  bottom: -16%;
  background: radial-gradient(circle, rgba(34,211,238,0.26) 0%, transparent 68%);
  animation: fx-aurora-b 38s ease-in-out infinite;
}
.fx-aurora-bg span:nth-child(3) {
  width: 50vw; height: 50vw; max-width: 640px; max-height: 640px;
  top: 28%;
  background: radial-gradient(circle, rgba(201,168,76,0.22) 0%, transparent 68%);
  animation: fx-aurora-a 48s ease-in-out infinite reverse;
}
@media (max-width: 700px) {
  .fx-aurora-bg span:nth-child(3) { display: none; }
  .fx-aurora-bg span { filter: blur(54px); }
}

/* ---------------------------------------------------------------------------
   2. GOLD SHIMMER SWEEP
   A slow light sweep across gold headlines and gold prices. Implemented as a
   moving linear-gradient painted INTO the text (background-clip: text), so it
   costs one compositor layer and no JS at all.

   CONTRAST SAFETY: the gradient never drops below the base gold, it only adds
   a brighter highlight passing through. Text is fully legible at every frame,
   which is why it is safe to run on a headline.
   --------------------------------------------------------------------------- */
.fx-shimmer {
  background-image: linear-gradient(
    100deg,
    var(--fx-gold) 0%,
    var(--fx-gold) 38%,
    var(--fx-gold-hi) 50%,
    var(--fx-gold) 62%,
    var(--fx-gold) 100%
  );
  background-size: 300% 100%;
  background-position: 100% 0;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation: fx-sweep 7s ease-in-out infinite;
}
@keyframes fx-sweep {
  0%, 12%  { background-position: 130% 0; }
  55%, 100% { background-position: -30% 0; }
}

/* ---------------------------------------------------------------------------
   3. AURORA / GRADIENT MESH BACKGROUND
   Two or three large blurred radial gradients in the brand palette, drifting
   like distant northern lights, behind heroes and major section headers.

   Apply .fx-aurora to a POSITIONED container; the light is painted by ::before
   and ::after so it needs no extra markup. The container must not be
   overflow:visible over the whole page, hence overflow:hidden here.
   --------------------------------------------------------------------------- */
.fx-aurora {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.fx-aurora::before,
.fx-aurora::after {
  content: "";
  position: absolute;
  z-index: -1;
  pointer-events: none;
  border-radius: 50%;
  filter: blur(70px);
  opacity: 0.5;
  will-change: transform;
}
.fx-aurora::before {
  width: 55vw; height: 55vw;
  max-width: 720px; max-height: 720px;
  top: -18%; left: -12%;
  background: radial-gradient(circle, rgba(192,132,252,0.34) 0%, transparent 68%);
  animation: fx-aurora-a 30s ease-in-out infinite;
}
.fx-aurora::after {
  width: 48vw; height: 48vw;
  max-width: 640px; max-height: 640px;
  bottom: -22%; right: -10%;
  background: radial-gradient(circle, rgba(34,211,238,0.26) 0%, transparent 68%);
  animation: fx-aurora-b 38s ease-in-out infinite;
}
/* A third, gold, only where the surface opts in. Kept off by default so most
   sections carry two lights and stay quiet. */
.fx-aurora-gold::before {
  background: radial-gradient(circle, rgba(201,168,76,0.30) 0%, transparent 68%);
}
@keyframes fx-aurora-a {
  0%   { transform: translate3d(-30vw, 0, 0) scale(1); }
  50%  { transform: translate3d(90vw, 12%, 0) scale(1.15); }
  100% { transform: translate3d(-30vw, 0, 0) scale(1); }
}
@keyframes fx-aurora-b {
  0%   { transform: translate3d(30vw, 0, 0) scale(1.06); }
  50%  { transform: translate3d(-90vw, -10%, 0) scale(0.9); }
  100% { transform: translate3d(30vw, 0, 0) scale(1.06); }
}

/* Phones do less work: one light, no blur-heavy second layer. Large blurs are
   the single most expensive thing on this page and this is what protects the
   mobile Lighthouse score. */
@media (max-width: 700px) {
  .fx-aurora::after { display: none; }
  .fx-aurora::before { filter: blur(52px); opacity: 0.42; }
}

/* ---------------------------------------------------------------------------
   4a. MAGNETIC BUTTONS
   The button pulls very slightly toward the cursor and settles back. JS writes
   --fx-mx / --fx-my in px; the transition handles the settle, so releasing the
   pointer is smooth without any JS animation loop.
   --------------------------------------------------------------------------- */
.fx-magnetic {
  --fx-mx: 0px;
  --fx-my: 0px;
  transform: translate3d(var(--fx-mx), var(--fx-my), 0);
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: transform;
}

/* ---------------------------------------------------------------------------
   4b. 3D CARD TILT, with a soft glare sweep
   The card rotates a couple of degrees toward the pointer and a low-opacity
   highlight tracks across its face. Two degrees is the ceiling on purpose:
   more reads as a gimmick and starts to hurt text legibility.
   --------------------------------------------------------------------------- */
.fx-tilt {
  --fx-rx: 0deg;
  --fx-ry: 0deg;
  --fx-gx: 50%;
  --fx-gy: 50%;
  --fx-glare: 0;
  position: relative;
  transform: perspective(900px) rotateX(var(--fx-rx)) rotateY(var(--fx-ry));
  transform-style: preserve-3d;
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: transform;
}
/* The glare rides on top of the card but must never eat a click. */
.fx-tilt::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border-radius: inherit;
  background: radial-gradient(
    circle 220px at var(--fx-gx) var(--fx-gy),
    rgba(255, 255, 255, 0.10) 0%,
    transparent 60%
  );
  opacity: var(--fx-glare);
  transition: opacity 0.35s ease;
}

/* ---------------------------------------------------------------------------
   REDUCED MOTION: everything above goes quiet, in one place.
   Not "less motion", NO motion: no drift, no sweep, no tilt, no magnet. The
   spotlight and aurora survive as STILL light, because a soft static gradient
   is depth, not movement, and dropping them entirely would leave the page
   flatter than it was before this layer existed.
   --------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  /* The waves are motion and nothing else, so they go entirely. Unlike the
     spotlight and aurora there is no still version worth keeping: a frozen sine
     line is a stray graphic, not depth. */
  .fx-waves { display: none !important; }
  .fx-wave { animation: none !important; }
  .fx-aurora-bg span { animation: none !important; }
  .fx-spotlight,
  .fx-aurora::before,
  .fx-aurora::after,
  .fx-shimmer {
    animation: none !important;
    transition: none !important;
  }
  .fx-shimmer {
    /* Back to flat gold: no gradient, no clipping, full contrast. */
    background-image: none;
    -webkit-text-fill-color: currentColor;
    color: var(--fx-gold);
  }
  .fx-magnetic,
  .fx-tilt {
    transform: none !important;
    transition: none !important;
  }
  .fx-tilt::after { opacity: 0 !important; }
}
