/* ============================================================
   AXION — motion.css
   Reversible scroll reveals, parallax hooks, reduced motion
   All animated properties: transform + opacity only.
   ============================================================ */

/* Reveal primitives — .in is TOGGLED by IntersectionObserver,
   so every reveal plays forward on scroll down and reverses
   on scroll up (never one-time consumed). */
html.js [data-anim] {
  opacity: 0;
  transition:
    opacity .65s var(--ease-out),
    transform .65s var(--ease-out);
  transition-delay: calc(var(--d, 0) * 60ms);
  will-change: transform, opacity;
}
/* exiting (scrolled away): fade out fast, no stagger — keeps reverse scroll snappy */
html.js [data-anim]:not(.in) {
  transition-duration: .3s;
  transition-delay: 0s;
  transition-timing-function: ease-out;
}
html.js [data-anim].in {
  opacity: 1;
  transform: none !important;
}

html.js [data-anim="up"]    { transform: translateY(34px); }
html.js [data-anim="down"]  { transform: translateY(-34px); }
html.js [data-anim="left"]  { transform: translateX(-40px); }
html.js [data-anim="right"] { transform: translateX(40px); }
html.js [data-anim="scale"] { transform: scale(.93); }
html.js [data-anim="blur"]  { transform: translateY(20px); filter: blur(8px); transition-property: opacity, transform, filter; }
html.js [data-anim="blur"].in { filter: blur(0); }
html.js [data-anim="fade"]  { transform: none; }

/* delay steps set inline via data-d="n" -> style="--d:n" in JS */

/* word-by-word hero title rise */
html.js .split-words .w {
  display: inline-block;
  overflow: clip;
  vertical-align: bottom;
}
html.js .split-words .w > i {
  display: inline-block; font-style: normal;
  transform: translateY(110%);
  transition: transform .7s var(--ease-out);
  /* tighter stagger, hard-capped so trailing (gradient) words never lag */
  transition-delay: min(calc(var(--wi) * 45ms), .4s);
}
html.js .split-words:not(.in) .w > i {
  transition-duration: .35s;
  transition-delay: 0s;
}
html.js .split-words.in .w > i { transform: none; }

/* gradient keywords: once split, the <em> hands its gradient to the
   sliding word spans — background-clip:text over transformed children
   paints late in Chromium, which caused the "stuck" colored word */
html.js .split-words em.em-split {
  background: none;
  -webkit-text-fill-color: currentColor;
  color: inherit;
}
html.js .split-words .w > i.g {
  color: var(--coral);
}
:root[data-theme="light"] html.js .split-words .w > i.g {
  background: linear-gradient(100deg, var(--teal) 15%, var(--coral) 92%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

/* parallax layers — JS drives --py in px via rAF */
.plx { transform: translate3d(0, calc(var(--py, 0) * 1px), 0); will-change: transform; }

/* dock reveal on load */
html.js .dock { animation: dock-in .8s var(--ease-out) both; }
@keyframes dock-in { from { transform: translateY(-140%); opacity: 0; } }

/* stack card depth (JS sets --s scale + --dim) */
.stack__card { transform: scale(var(--s, 1)); }
.stack__card::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: var(--bg); opacity: var(--dim, 0);
  pointer-events: none;
  transition: opacity .15s linear;
}
/* mobile/tablet: sticky stacking is disabled in components.css (see note
   there) — kill the scale/dim overlay too so a stale --s/--dim never paints */
@media (max-width: 64rem) {
  .stack__card { transform: none !important; }
  .stack__card::after { display: none; }
}

/* counters get a soft pop when they finish */
.stat__value.done { animation: stat-pop .5s var(--ease-spring); }
@keyframes stat-pop { 50% { transform: scale(1.06); } }

/* ---------- Capture mode (?capture): static full-page render for QA ---------- */
html.capture [data-anim],
html.capture .split-words .w > i {
  opacity: 1 !important;
  transform: none !important;
  filter: none !important;
  transition: none !important;
}
html.capture .hero { min-height: auto; padding-top: calc(var(--dock-h) + 130px); }
html.capture .stack__card { position: static; }

/* ---------- Reduced motion: calm everything ---------- */
@media (prefers-reduced-motion: reduce) {
  html.js [data-anim],
  html.js .split-words .w > i {
    opacity: 1; transform: none; filter: none;
    transition: none;
  }
  .plx { transform: none; }
  .core__ring, .core__glow, .core__nucleus, .core__nucleus::after,
  .hero__chip, .ticker__track, .quotes__row,
  .wave i, .mini-chat__typing i, .mini-flow__link::after,
  .calldemo__ring::before, .calldemo__ring::after,
  .hero__scroll::after, .cta::before {
    animation: none !important;
  }
  html.js .dock { animation: none; }
  .stack__card { transform: none; position: static; }
  * { transition-duration: .01ms !important; }
}

/* Mini-chat looping conversation animations for enhanced liveness */
@keyframes conversation-in {
  0%, 100% { opacity: 0; transform: translateY(10px); }
  6%, 46% { opacity: 1; transform: translateY(0); }
  52% { opacity: 0; transform: translateY(-10px); }
}
@keyframes conversation-typing {
  0%, 100% { opacity: 0; transform: translateY(10px); }
  10%, 28% { opacity: 1; transform: translateY(0); }
  34%, 90% { opacity: 0; transform: translateY(-10px); }
}
@keyframes conversation-out {
  0%, 32%, 100% { opacity: 0; transform: translateY(10px); }
  38%, 78% { opacity: 1; transform: translateY(0); }
  84% { opacity: 0; transform: translateY(-10px); }
}

.mini-chat__bubble--in {
  animation: conversation-in 8s ease-in-out infinite;
}
.mini-chat__typing {
  animation: conversation-typing 8s ease-in-out infinite;
}
.mini-chat__bubble--out {
  animation: conversation-out 8s ease-in-out infinite;
}
