/* ==========================================================================
   SYNAPSE — Effects
   The brand's visual language: perception motifs (gaze, bounding box, scan),
   real-time motifs (waveform, pulse, ticker) and ambient depth (mesh, grain).
   ========================================================================== */

/* ==========================================================================
   Ambient backdrops
   ========================================================================== */

/* Aurora mesh — slow drifting colour field. Put inside a positioned parent. */
.fx-aurora {
  position: absolute;
  inset: -20% -10%;
  z-index: -1;
  pointer-events: none;
  filter: blur(70px) saturate(140%);
  opacity: var(--aurora-opacity, 0.65);
}
.fx-aurora i {
  position: absolute;
  display: block;
  border-radius: 50%;
  mix-blend-mode: screen;
  animation: drift var(--dur, 26s) var(--ease-in-out) infinite alternate;
}
.fx-aurora i:nth-child(1) { width: 46vw; height: 46vw; left: -6%; top: -12%; background: rgba(20 205 186 / 0.55); --dur: 24s; }
.fx-aurora i:nth-child(2) { width: 38vw; height: 38vw; right: 2%; top: 4%; background: rgba(124 92 255 / 0.5); --dur: 31s; animation-delay: -6s; }
.fx-aurora i:nth-child(3) { width: 30vw; height: 30vw; left: 34%; bottom: -14%; background: rgba(245 165 36 / 0.28); --dur: 38s; animation-delay: -14s; }
[data-theme="light"] .fx-aurora { opacity: 0.4; filter: blur(90px) saturate(120%); }
[data-theme="light"] .fx-aurora i { mix-blend-mode: multiply; }

@keyframes drift {
  0% { transform: translate3d(0, 0, 0) scale(1); }
  50% { transform: translate3d(6%, -5%, 0) scale(1.14); }
  100% { transform: translate3d(-5%, 7%, 0) scale(0.94); }
}

/* Grain — film texture over everything, keeps flat gradients from banding */
.fx-grain::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: var(--grain-opacity);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
  mix-blend-mode: overlay;
}

/* Technical grid — the "instrument panel" floor */
.fx-grid {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image:
    linear-gradient(to right, var(--border-faint) 1px, transparent 1px),
    linear-gradient(to bottom, var(--border-faint) 1px, transparent 1px);
  background-size: var(--grid-size, 68px) var(--grid-size, 68px);
  mask-image: radial-gradient(120% 90% at 50% 0%, #000 20%, transparent 78%);
}
.fx-grid--dots {
  background-image: radial-gradient(var(--border) 1px, transparent 1px);
  background-size: 26px 26px;
}

/* Radial spotlight glow */
.fx-glow {
  position: absolute;
  z-index: -1;
  pointer-events: none;
  width: var(--glow-size, 620px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle, var(--glow-color, rgba(63 227 208 / 0.22)), transparent 68%);
  filter: blur(20px);
}
.fx-glow--violet { --glow-color: rgba(155 132 255 / 0.24); }
.fx-glow--amber { --glow-color: rgba(255 190 92 / 0.18); }

/* Top/bottom fades to blend bands */
.fx-fade-top::before,
.fx-fade-bottom::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  height: 140px;
  pointer-events: none;
  z-index: 1;
}
.fx-fade-top::before { inset-block-start: 0; background: linear-gradient(180deg, var(--bg), transparent); }
.fx-fade-bottom::after { inset-block-end: 0; background: linear-gradient(0deg, var(--bg), transparent); }

/* ==========================================================================
   Perception motifs
   ========================================================================== */

/* Bounding box with a label — the computer-vision signature.
   Wrap any media: <div class="bbox" data-label="face 0.98"> … </div> */
.bbox { position: relative; }
.bbox::before {
  content: "";
  position: absolute;
  inset: var(--bbox-inset, 8%);
  border: 1.5px solid var(--bbox-color, var(--primary));
  border-radius: 3px;
  box-shadow: 0 0 24px -6px var(--bbox-color, var(--primary));
  pointer-events: none;
  animation: bbox-lock var(--dur-5) var(--ease-out-expo) both;
}
.bbox::after {
  content: attr(data-label);
  position: absolute;
  inset-block-start: calc(var(--bbox-inset, 8%) - 1.55em);
  inset-inline-start: var(--bbox-inset, 8%);
  padding: 0.16em 0.45em;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--on-primary);
  background: var(--bbox-color, var(--primary));
  border-radius: 3px 3px 0 0;
  pointer-events: none;
  white-space: nowrap;
}
.bbox--violet { --bbox-color: var(--secondary); }
.bbox--amber { --bbox-color: var(--accent); }

@keyframes bbox-lock {
  0% { opacity: 0; clip-path: inset(46% 46% 46% 46%); }
  100% { opacity: 1; clip-path: inset(0 0 0 0); }
}

/* Corner brackets — capture frame */
.frame { position: relative; }
.frame::before,
.frame::after {
  content: "";
  position: absolute;
  width: 26px;
  height: 26px;
  border: 1.5px solid var(--primary);
  pointer-events: none;
}
.frame::before { inset-block-start: -1px; inset-inline-start: -1px; border-inline-end: 0; border-block-end: 0; border-start-start-radius: 6px; }
.frame::after { inset-block-end: -1px; inset-inline-end: -1px; border-inline-start: 0; border-block-start: 0; border-end-end-radius: 6px; }

/* Scanline sweep — "processing" state over media */
.fx-scan { position: relative; overflow: hidden; }
.fx-scan::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  height: 34%;
  background: linear-gradient(180deg, transparent, rgba(63 227 208 / 0.16) 62%, rgba(63 227 208 / 0.5));
  border-block-end: 1px solid var(--primary);
  animation: scan 3.4s var(--ease-in-out) infinite;
  pointer-events: none;
  mix-blend-mode: screen;
}
@keyframes scan {
  0%, 100% { transform: translateY(-110%); }
  50% { transform: translateY(320%); }
}

/* Gaze rings — concentric pulses, used behind avatars/kiosks */
.fx-rings { position: absolute; inset: 0; z-index: -1; pointer-events: none; display: grid; place-items: center; }
.fx-rings i {
  position: absolute;
  width: 100%;
  aspect-ratio: 1;
  border: 1px solid var(--primary-ring);
  border-radius: 50%;
  animation: ring-out 3.6s var(--ease-out) infinite;
}
.fx-rings i:nth-child(2) { animation-delay: 1.2s; }
.fx-rings i:nth-child(3) { animation-delay: 2.4s; }
@keyframes ring-out {
  0% { transform: scale(0.35); opacity: 0; }
  25% { opacity: 0.9; }
  100% { transform: scale(1.35); opacity: 0; }
}

/* ==========================================================================
   Real-time motifs
   ========================================================================== */

/* Waveform — live audio. 18 bars, staggered via nth-child. */
.waveform { display: flex; align-items: center; gap: 3px; height: var(--wave-h, 26px); }
.waveform i {
  width: 3px;
  flex: none;
  border-radius: var(--r-pill);
  background: var(--primary);
  animation: wave 1.05s var(--ease-in-out) infinite alternate;
  transform-origin: center;
}
.waveform i:nth-child(3n) { background: var(--secondary); }
.waveform i:nth-child(1) { animation-delay: -0.9s; height: 32%; }
.waveform i:nth-child(2) { animation-delay: -0.1s; height: 62%; }
.waveform i:nth-child(3) { animation-delay: -0.5s; height: 100%; }
.waveform i:nth-child(4) { animation-delay: -0.7s; height: 46%; }
.waveform i:nth-child(5) { animation-delay: -0.3s; height: 78%; }
.waveform i:nth-child(6) { animation-delay: -0.8s; height: 38%; }
.waveform i:nth-child(7) { animation-delay: -0.2s; height: 92%; }
.waveform i:nth-child(8) { animation-delay: -0.6s; height: 54%; }
.waveform i:nth-child(9) { animation-delay: -0.4s; height: 70%; }
.waveform i:nth-child(10) { animation-delay: -1s; height: 30%; }
.waveform i:nth-child(11) { animation-delay: -0.15s; height: 86%; }
.waveform i:nth-child(12) { animation-delay: -0.55s; height: 44%; }
.waveform i:nth-child(13) { animation-delay: -0.35s; height: 66%; }
.waveform i:nth-child(14) { animation-delay: -0.75s; height: 36%; }
.waveform i:nth-child(15) { animation-delay: -0.25s; height: 96%; }
.waveform i:nth-child(16) { animation-delay: -0.65s; height: 50%; }
.waveform i:nth-child(17) { animation-delay: -0.45s; height: 74%; }
.waveform i:nth-child(18) { animation-delay: -0.85s; height: 34%; }
@keyframes wave { from { transform: scaleY(0.28); } to { transform: scaleY(1); } }
.waveform--muted i { background: var(--text-faint); animation-play-state: paused; transform: scaleY(0.4); }

/* Pulse dot (live indicator) */
@keyframes pulse-dot {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 currentColor; }
  50% { opacity: 0.55; box-shadow: 0 0 0 4px color-mix(in oklab, currentColor 22%, transparent); }
}

/* Marquee ticker */
.marquee {
  position: relative;
  display: flex;
  overflow: hidden;
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  user-select: none;
}
.marquee__track {
  display: flex;
  flex: none;
  gap: var(--marquee-gap, var(--s-10));
  padding-inline-end: var(--marquee-gap, var(--s-10));
  animation: marquee var(--marquee-dur, 38s) linear infinite;
  will-change: transform;
}
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee--reverse .marquee__track { animation-direction: reverse; }
@keyframes marquee { to { transform: translateX(-100%); } }

.marquee__item {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  white-space: nowrap;
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-snug);
  color: var(--text-faint);
}
.marquee__item svg { width: 14px; height: 14px; color: var(--primary); }

/* Typing caret */
.caret::after {
  content: "";
  display: inline-block;
  width: 0.55em;
  height: 1.05em;
  margin-inline-start: 0.12em;
  vertical-align: -0.16em;
  background: var(--primary);
  animation: blink 1.1s steps(2, start) infinite;
}
@keyframes blink { to { visibility: hidden; } }

/* Ping / packet trail along a line */
.fx-packet { position: relative; overflow: hidden; }
.fx-packet::after {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  width: 40%;
  height: 100%;
  background: linear-gradient(90deg, transparent, var(--primary), transparent);
  animation: packet 2.6s var(--ease-in-out) infinite;
}
@keyframes packet {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(350%); }
}

/* ==========================================================================
   Reveal on scroll — driven by IntersectionObserver adding .is-in
   ========================================================================== */
/* Progressive enhancement: only hide until JS marks <html class="js">.
   Without JS, content stays visible (no blank heroes). */
html.js [data-reveal] {
  opacity: 0;
  transform: translateY(var(--reveal-y, 22px)) scale(var(--reveal-s, 1));
  transition:
    opacity var(--dur-5) var(--ease-out-expo) var(--reveal-delay, 0ms),
    transform var(--dur-5) var(--ease-out-expo) var(--reveal-delay, 0ms),
    filter var(--dur-5) var(--ease-out-expo) var(--reveal-delay, 0ms);
  filter: blur(var(--reveal-blur, 0));
  will-change: opacity, transform;
}
[data-reveal].is-in,
html.js [data-reveal].is-in { opacity: 1; transform: none; filter: blur(0); }
[data-reveal="fade"] { --reveal-y: 0; }
[data-reveal="up"] { --reveal-y: 34px; }
[data-reveal="zoom"] { --reveal-y: 0; --reveal-s: 0.965; }
[data-reveal="blur"] { --reveal-blur: 10px; --reveal-y: 12px; }
[data-reveal="left"] { --reveal-y: 0; transform: translateX(-28px); }
[data-reveal="right"] { --reveal-y: 0; transform: translateX(28px); }

/* Word-by-word headline reveal (JS wraps words in <span class="word">).
   Activate when the element OR an ancestor gets .is-in — heroes put
   reveal-words on the H1 inside a data-reveal parent. */
html.js .reveal-words .word { display: inline-block; overflow: hidden; vertical-align: top; }
html.js .reveal-words .word > span {
  display: inline-block;
  transform: translateY(110%);
  transition: transform var(--dur-5) var(--ease-out-expo);
  transition-delay: calc(var(--i, 0) * 42ms);
}
.reveal-words.is-in .word > span,
.is-in .reveal-words .word > span,
html.js .reveal-words.is-in .word > span,
html.js .is-in .reveal-words .word > span { transform: translateY(0); }

/* Staggered children: set on a container */
[data-stagger] > * { transition-delay: calc(var(--i, 0) * var(--stagger, 70ms)); }

/* ==========================================================================
   Interactive surfaces
   ========================================================================== */

/* Cursor spotlight — JS sets --mx/--my on the element */
.fx-spot { position: relative; isolation: isolate; }
.fx-spot::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  background: radial-gradient(
    var(--spot-size, 340px) circle at var(--mx, 50%) var(--my, 0%),
    var(--spot-color, rgba(63 227 208 / 0.14)),
    transparent 62%
  );
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out);
  pointer-events: none;
}
.fx-spot:hover::before { opacity: 1; }
.fx-spot > * { position: relative; z-index: 1; }

/* 3D tilt — JS sets --rx/--ry */
.fx-tilt {
  transform: perspective(900px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transform-style: preserve-3d;
  transition: transform var(--dur-3) var(--ease-out);
}
.fx-tilt__lift { transform: translateZ(38px); }

/* Magnetic button — JS sets --tx/--ty */
.fx-magnet { transform: translate3d(var(--tx, 0), var(--ty, 0), 0); }

/* Gradient text */
.grad-text {
  background: var(--grad-text);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.grad-text--vision { background: var(--grad-vision); -webkit-background-clip: text; background-clip: text; }

/* Outline (stroke) text */
.stroke-text {
  color: transparent;
  -webkit-text-stroke: 1px var(--border-strong);
}

/* Animated gradient border */
.fx-border {
  position: relative;
  background: var(--bg-raised);
  border: 0;
}
.fx-border::before {
  content: "";
  position: absolute;
  inset: 0;
  padding: 1px;
  border-radius: inherit;
  background: conic-gradient(from var(--angle, 0deg), transparent 55%, var(--primary), var(--secondary), transparent 90%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  animation: spin-angle 6s linear infinite;
  pointer-events: none;
}
@property --angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}
@keyframes spin-angle { to { --angle: 360deg; } }

/* Float / bob */
.fx-float { animation: float 7s var(--ease-in-out) infinite alternate; }
.fx-float--slow { animation-duration: 11s; }
@keyframes float { from { transform: translateY(-8px); } to { transform: translateY(10px); } }

/* Toast animations */
@keyframes toast-in { from { opacity: 0; transform: translateX(20px) scale(0.97); } }
@keyframes toast-out { to { opacity: 0; transform: translateX(20px) scale(0.97); } }

/* Shimmer skeleton */
.skeleton {
  border-radius: var(--r-sm);
  background: linear-gradient(90deg, var(--bg-inset) 25%, var(--bg-panel-hover) 50%, var(--bg-inset) 75%);
  background-size: 220% 100%;
  animation: shimmer 1.5s linear infinite;
  color: transparent;
}
@keyframes shimmer { to { background-position: -120% 0; } }

/* Counting number lock-in */
.counting { font-variant-numeric: tabular-nums; }

/* View-transition friendly page fade */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}
