/* ==========================================================================
   PRISM — Animations & scroll-reveal system
   ========================================================================== */

/* ---- Keyframes ---- */
@keyframes blobDrift {
  0%   { transform: translate(0, 0) scale(1); }
  50%  { transform: translate(30px, -36px) scale(1.08); }
  100% { transform: translate(-24px, 24px) scale(0.95); }
}

@keyframes floatY {
  0%, 100% { transform: translateY(0) rotate(var(--tilt, 0deg)); }
  50%      { transform: translateY(-14px) rotate(var(--tilt, 0deg)); }
}

@keyframes typingDot {
  0%, 60%, 100% { opacity: 0.25; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-3px); }
}

@keyframes wavePulse {
  0%, 100% { height: 20%; }
  50% { height: 100%; }
}
.waveform__bar:nth-child(1) { animation-delay: 0s; }
.waveform__bar:nth-child(2) { animation-delay: 0.12s; }
.waveform__bar:nth-child(3) { animation-delay: 0.24s; }
.waveform__bar:nth-child(4) { animation-delay: 0.36s; }
.waveform__bar:nth-child(5) { animation-delay: 0.48s; }
.waveform__bar:nth-child(6) { animation-delay: 0.6s; }
.waveform__bar:nth-child(7) { animation-delay: 0.72s; }
.waveform__bar:nth-child(8) { animation-delay: 0.84s; }

@keyframes shakeX {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-5px); }
  40% { transform: translateX(4px); }
  60% { transform: translateX(-3px); }
  80% { transform: translateX(2px); }
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

/* Trust logo marquee: track contains the logo set duplicated exactly once,
   so translating by -50% loops seamlessly with no visible seam or jump. */
@keyframes trustScroll {
  /* Track holds 4 identical copies; moving by exactly one copy (-25%) loops
     seamlessly and always leaves 3 copies filling the viewport. */
  from { transform: translateX(0); }
  to { transform: translateX(-25%); }
}

/* Process section: a small comet travels continuously along the step
   track — left-to-right on desktop, top-to-bottom once steps stack. */
@keyframes runnerX {
  0%   { left: 0%; opacity: 0; }
  8%   { opacity: 1; }
  92%  { opacity: 1; }
  100% { left: 100%; opacity: 0; }
}
@keyframes runnerY {
  0%   { top: 0%; opacity: 0; }
  8%   { opacity: 1; }
  92%  { opacity: 1; }
  100% { top: 100%; opacity: 0; }
}

/* ---------------------------------------------------------------------- */
/* Scroll reveal                                                           */
/* Hidden state only applies once JS confirms it can restore visibility    */
/* (html.js gate) — guarantees content is visible with JS disabled.        */
/* IntersectionObserver toggles .is-visible on both scroll directions.     */
/* ---------------------------------------------------------------------- */
.js [data-reveal] {
  opacity: 0;
  transition: opacity var(--dur-slower) var(--ease-out),
              transform var(--dur-slower) var(--ease-out);
  transition-delay: calc(var(--reveal-i, 0) * 70ms);
}
.js [data-reveal="up"]     { transform: translateY(28px); }
.js [data-reveal="down"]   { transform: translateY(-28px); }
.js [data-reveal="left"]   { transform: translateX(32px); }
.js [data-reveal="right"]  { transform: translateX(-32px); }
.js [data-reveal="scale"]  { transform: scale(0.94); }
.js [data-reveal="fade"]   { transform: none; }

.js [data-reveal].is-visible {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .js [data-reveal] { transition-duration: var(--dur-fast); transform: none !important; }
}

/* ---- Utility fade for tab/accordion panel swaps ---- */
.fade-in { animation: fadeIn var(--dur-slow) var(--ease-out); }
@keyframes fadeIn {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ---- Hover tilt hook (JS sets --rx/--ry via inline style) ---- */
.tilt-target {
  transform: rotateX(var(--ry, 0deg)) rotateY(var(--rx, 0deg));
}
