/* ============================================================
   Nova FX · finalefx.css — finale + footer craft pack
   Pure CSS + vanilla JS (finalefx.js). Zero external requests.
   Every selector is namespaced .fx-*. Continuous animations are
   IO-gated via .fx-live (JS toggles while the block is in view);
   one-shot reveals arm with .fx-arm and fire with .fx-in.
   Honors prefers-reduced-motion — everything collapses static.
   ------------------------------------------------------------
   Components:
     .fx-portal       wormhole ring + converging particles (CTA)
     .fx-aurora-line  aurora beam travelling the CTA top border
     .fx-footer       footer starfield strip + self-drawing rule
                      + ghost wallet marks
     .fx-wordmark     giant ghost NOVA behind footer (parallax)
     .fx-chipwave     chain chips, staggered translateY wave
   ============================================================ */

/* ------------------------------------------------------------
   1 · .fx-portal — wormhole behind the final CTA
   Add the class to the closing CTA <section>. JS injects
   .fx-portal-fx (tilt plane > rings + core + haze, plus .fx-pp
   particle spans). Everything sits at z-index:0 behind copy.
   ------------------------------------------------------------ */
.fx-portal{position:relative;overflow:hidden;isolation:isolate}
.fx-portal>.container{position:relative;z-index:2}
.fx-portal-fx{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden;
 perspective:1100px;-webkit-perspective:1100px}

/* tilted plane — rotateX warps the rings into a flat ellipse,
   a portal seen at an angle */
.fx-portal-tilt{position:absolute;left:50%;top:54%;width:min(640px,86vw);aspect-ratio:1;
 transform:translate(-50%,-50%) rotateX(64deg);transform-style:preserve-3d}

/* spinning conic ring, masked into a thin torus */
.fx-portal-ring{position:absolute;inset:0;border-radius:50%;
 background:conic-gradient(from 0deg,
   rgba(56,189,248,0) 0deg,rgba(56,189,248,.6) 60deg,
   rgba(47,107,255,.85) 130deg,rgba(124,92,255,.7) 200deg,
   rgba(56,189,248,.35) 285deg,rgba(56,189,248,0) 360deg);
 -webkit-mask:radial-gradient(closest-side,transparent 56%,#000 62%,#000 72%,transparent 78%);
 mask:radial-gradient(closest-side,transparent 56%,#000 62%,#000 72%,transparent 78%);
 filter:saturate(1.15);
 opacity:.85;
 animation:fxPortalSpin 18s linear infinite;animation-play-state:paused}
/* second, wider counter-ring — fainter, slower */
.fx-portal-ring.r2{inset:-9%;opacity:.32;filter:blur(2px) saturate(1.1);
 background:conic-gradient(from 180deg,
   rgba(124,92,255,0) 0deg,rgba(124,92,255,.5) 80deg,
   rgba(56,189,248,.6) 170deg,rgba(47,107,255,.45) 250deg,
   rgba(124,92,255,0) 360deg);
 -webkit-mask:radial-gradient(closest-side,transparent 60%,#000 66%,#000 70%,transparent 75%);
 mask:radial-gradient(closest-side,transparent 60%,#000 66%,#000 70%,transparent 75%);
 animation:fxPortalSpinR 34s linear infinite;animation-play-state:paused}
/* dark throat inside the ring — sells the "hole" */
.fx-portal-core{position:absolute;left:50%;top:50%;width:52%;aspect-ratio:1;border-radius:50%;
 transform:translate(-50%,-50%);
 background:radial-gradient(circle,rgba(4,9,26,.95) 0%,rgba(4,9,26,.55) 45%,rgba(4,9,26,0) 72%)}
/* soft glow breathing behind the throat */
.fx-portal-haze{position:absolute;inset:16%;border-radius:50%;
 background:radial-gradient(circle,rgba(47,107,255,.32),rgba(56,189,248,.1) 48%,transparent 72%);
 filter:blur(22px);
 animation:fxPortalPulse 6.5s ease-in-out infinite;animation-play-state:paused}

/* particles converge INTO the throat (screen-space, not tilted) */
.fx-portal-pts{position:absolute;inset:0}
.fx-pp{position:absolute;left:50%;top:54%;width:3px;height:3px;border-radius:50%;
 background:var(--fx-c,#38bdf8);
 box-shadow:0 0 9px 1px var(--fx-c,#38bdf8);
 opacity:0;
 transform:translate(-50%,-50%) translate(var(--fx-tx,0),var(--fx-ty,0)) scale(.9);
 animation:fxPortalIn var(--fx-t,5s) linear infinite;
 animation-delay:var(--fx-d,0s);animation-play-state:paused}
/* a few particles read larger/brighter */
.fx-pp.big{width:4.5px;height:4.5px;box-shadow:0 0 13px 2px var(--fx-c,#38bdf8)}

.fx-portal.fx-live .fx-portal-ring,
.fx-portal.fx-live .fx-portal-haze,
.fx-portal.fx-live .fx-pp{animation-play-state:running}

@keyframes fxPortalSpin{to{transform:rotate(360deg)}}
@keyframes fxPortalSpinR{to{transform:rotate(-360deg)}}
@keyframes fxPortalPulse{0%,100%{opacity:.65;transform:scale(.96)}50%{opacity:1;transform:scale(1.05)}}
@keyframes fxPortalIn{
 0%{opacity:0;transform:translate(-50%,-50%) translate(var(--fx-tx,0),var(--fx-ty,0)) scale(.95)}
 14%{opacity:.95}
 78%{opacity:.45}
 100%{opacity:0;transform:translate(-50%,-50%) translate(0,0) scale(.12)}}

@media (max-width:860px){
 .fx-portal-tilt{top:58%;width:min(480px,110vw)}
}

/* ------------------------------------------------------------
   2 · .fx-aurora-line — aurora beam on the CTA top border
   <div class="fx-aurora-line" aria-hidden="true"></div>
   JS auto-injects one into .fx-portal sections if missing.
   Base hairline is always visible; the vivid segment sweeps
   left→right forever (off-screen at the wrap point, so the
   loop is seamless).
   ------------------------------------------------------------ */
.fx-aurora-line{position:absolute;top:0;left:0;right:0;height:2px;z-index:3;
 pointer-events:none;overflow:visible;
 background:linear-gradient(90deg,transparent 0%,rgba(148,178,255,.16) 30%,
   rgba(148,178,255,.16) 70%,transparent 100%)}
.fx-aurora-line::before{content:"";position:absolute;top:0;left:0;right:0;height:2px;
 background:linear-gradient(90deg,
   transparent 0%,#2f6bff 20%,#38bdf8 44%,#7c5cff 68%,#38bdf8 84%,transparent 100%);
 background-size:34% 100%;background-repeat:no-repeat;background-position:-40% 0;
 animation:fxAuroraSweep 5.2s linear infinite;animation-play-state:paused}
/* blurred twin = the aurora halo bleeding downward */
.fx-aurora-line::after{content:"";position:absolute;top:-3px;left:0;right:0;height:14px;
 background:linear-gradient(90deg,
   transparent 0%,rgba(47,107,255,.9) 20%,rgba(56,189,248,.95) 44%,
   rgba(124,92,255,.9) 68%,rgba(56,189,248,.85) 84%,transparent 100%);
 background-size:34% 100%;background-repeat:no-repeat;background-position:-40% 0;
 filter:blur(7px);opacity:.5;
 animation:fxAuroraSweep 5.2s linear infinite;animation-play-state:paused}
.fx-live .fx-aurora-line::before,.fx-live .fx-aurora-line::after,
.fx-aurora-line.fx-live::before,.fx-aurora-line.fx-live::after{animation-play-state:running}
@keyframes fxAuroraSweep{from{background-position:-40% 0}to{background-position:140% 0}}

/* ------------------------------------------------------------
   3 · .fx-footer — footer polish layer
   Add to <footer class="foot fx-footer">. JS injects:
   .fx-foot-stars (star strip under the top border),
   .fx-foot-rule  (self-drawing hairline + center node),
   .fx-ghost      (scattered wallet marks, opacity .04).
   ------------------------------------------------------------ */
.fx-footer{position:relative;overflow:hidden;isolation:isolate}
.fx-footer>.container{position:relative;z-index:2}

/* starfield strip — dense near the top border, fades downward */
.fx-foot-stars{position:absolute;top:0;left:0;right:0;height:170px;z-index:0;
 pointer-events:none;
 background-image:
  radial-gradient(1.3px 1.3px at 4% 18%,rgba(233,240,255,.75),transparent 70%),
  radial-gradient(1px 1px at 11% 54%,rgba(233,240,255,.45),transparent 70%),
  radial-gradient(1.2px 1.2px at 17% 30%,rgba(56,189,248,.6),transparent 70%),
  radial-gradient(1px 1px at 23% 74%,rgba(233,240,255,.4),transparent 70%),
  radial-gradient(1.4px 1.4px at 29% 12%,rgba(233,240,255,.7),transparent 70%),
  radial-gradient(1px 1px at 36% 44%,rgba(148,178,255,.5),transparent 70%),
  radial-gradient(1.2px 1.2px at 42% 22%,rgba(56,189,248,.55),transparent 70%),
  radial-gradient(1px 1px at 48% 66%,rgba(233,240,255,.42),transparent 70%),
  radial-gradient(1.4px 1.4px at 55% 16%,rgba(233,240,255,.78),transparent 70%),
  radial-gradient(1px 1px at 61% 50%,rgba(233,240,255,.4),transparent 70%),
  radial-gradient(1.2px 1.2px at 68% 28%,rgba(124,92,255,.55),transparent 70%),
  radial-gradient(1px 1px at 74% 70%,rgba(233,240,255,.42),transparent 70%),
  radial-gradient(1.3px 1.3px at 81% 14%,rgba(56,189,248,.6),transparent 70%),
  radial-gradient(1px 1px at 87% 48%,rgba(233,240,255,.45),transparent 70%),
  radial-gradient(1.2px 1.2px at 93% 26%,rgba(233,240,255,.6),transparent 70%),
  radial-gradient(1px 1px at 98% 62%,rgba(148,178,255,.4),transparent 70%);
 background-repeat:no-repeat;
 -webkit-mask-image:linear-gradient(180deg,#000 0%,transparent 100%);
 mask-image:linear-gradient(180deg,#000 0%,transparent 100%);
 opacity:.55;
 animation:fxStarTw 7s ease-in-out infinite alternate;animation-play-state:paused}
.fx-footer.fx-live .fx-foot-stars{animation-play-state:running}
@keyframes fxStarTw{from{opacity:.35}to{opacity:.7}}

/* self-drawing hairline over the existing footer border-top.
   Draws from the CENTER outward; the node pops in near the end. */
.fx-foot-rule{position:absolute;top:-1px;left:0;right:0;height:2px;z-index:4;
 pointer-events:none;transform:scaleX(0);transform-origin:center;
 background:linear-gradient(90deg,
   transparent 0%,rgba(56,189,248,.65) 32%,rgba(124,92,255,.8) 50%,
   rgba(56,189,248,.65) 68%,transparent 100%);
 transition:transform 1.25s cubic-bezier(.6,.05,.2,1)}
.fx-foot-rule i{position:absolute;left:50%;top:50%;width:7px;height:7px;border-radius:50%;
 transform:translate(-50%,-50%) scale(0);
 background:#bfe6ff;
 box-shadow:0 0 10px 3px rgba(56,189,248,.85),0 0 26px 8px rgba(124,92,255,.4);
 transition:transform .45s cubic-bezier(.3,1.4,.4,1) 1s;
 animation:fxNodePulse 3.2s ease-in-out infinite 1.6s;animation-play-state:paused}
.fx-foot-rule.fx-in{transform:scaleX(1)}
.fx-foot-rule.fx-in i{transform:translate(-50%,-50%) scale(1)}
.fx-footer.fx-live .fx-foot-rule.fx-in i{animation-play-state:running}
@keyframes fxNodePulse{0%,100%{box-shadow:0 0 10px 3px rgba(56,189,248,.85),0 0 26px 8px rgba(124,92,255,.4)}
 50%{box-shadow:0 0 6px 2px rgba(56,189,248,.55),0 0 14px 5px rgba(124,92,255,.22)}}

/* ghost wallet/chain marks — pure ambience, never interactive */
.fx-ghost{position:absolute;z-index:0;opacity:.04;pointer-events:none;
 filter:grayscale(1) brightness(1.4);user-select:none}

/* ------------------------------------------------------------
   4 · .fx-wordmark — giant ghost NOVA behind the footer
   JS injects <div class="fx-wordmark">NOVA</div> into .fx-footer
   (or author pastes it; data-text overrides the word).
   Slight parallax: JS writes --fx-py on scroll while visible.
   ------------------------------------------------------------ */
.fx-wordmark{position:absolute;left:50%;bottom:-.3em;z-index:0;
 transform:translateX(-50%) translateY(var(--fx-py,0px));
 font-family:Inter,system-ui,sans-serif;font-weight:800;
 font-size:clamp(170px,30vw,430px);line-height:1;letter-spacing:-.03em;
 white-space:nowrap;user-select:none;pointer-events:none;
 color:transparent;
 background:linear-gradient(180deg,rgba(163,196,255,.85) 0%,rgba(47,107,255,.35) 70%,rgba(124,92,255,.2) 100%);
 -webkit-background-clip:text;background-clip:text;
 opacity:.05;
 will-change:transform}

/* ------------------------------------------------------------
   5 · .fx-chipwave — chain chips, staggered wave
   <div class="fx-chipwave" data-chains="eth,sol,btc,…"></div>
   JS builds .fx-chip spans (colored dot + label; optional
   <img> when data-chain-img gives an asset base path).
   ------------------------------------------------------------ */
.fx-chipwave{display:flex;flex-wrap:wrap;gap:9px;justify-content:center;
 position:relative;z-index:2}
.fx-chip{display:inline-flex;align-items:center;gap:7px;
 padding:7px 13px;border-radius:999px;
 background:rgba(13,30,61,.55);border:1px solid rgba(148,178,255,.15);
 font-size:11.5px;font-weight:600;letter-spacing:.02em;color:#a9bcdf;
 animation:fxChipWave 2.9s ease-in-out infinite;
 animation-delay:calc(var(--fx-i,0)*-.14s); /* negative → chips start mid-wave */
 animation-play-state:paused}
.fx-chip-dot{flex:none;width:7px;height:7px;border-radius:50%;
 background:var(--fx-c,#38bdf8);
 box-shadow:0 0 8px 1px var(--fx-c,#38bdf8)}
.fx-chip img{flex:none;width:14px;height:14px;border-radius:4px;display:block}
.fx-chipwave.fx-live .fx-chip{animation-play-state:running}
@keyframes fxChipWave{0%,100%{transform:translateY(0)}50%{transform:translateY(-6px)}}

/* ------------------------------------------------------------
   prefers-reduced-motion — everything lands in a calm end state
   ------------------------------------------------------------ */
@media (prefers-reduced-motion:reduce){
 .fx-portal-ring,.fx-portal-haze{animation:none}
 .fx-portal-ring{opacity:.5}
 .fx-portal-ring.r2{opacity:.2}
 .fx-pp{display:none}
 .fx-aurora-line::before,.fx-aurora-line::after{animation:none;
   background-position:33% 0;opacity:.5}
 .fx-foot-stars{animation:none;opacity:.5}
 .fx-foot-rule{transition:none;transform:scaleX(1)}
 .fx-foot-rule i{transition:none;animation:none;
   transform:translate(-50%,-50%) scale(1)}
 .fx-wordmark{transform:translateX(-50%)}
 .fx-chip{animation:none}
}
