/* ── FX : kinetic headings, tilt cards, scroll-filled text ──
   Loaded last. Every effect is progressive: without JS or under prefers-reduced-motion the site is unchanged. */

/* 1. Kinetic headings: each letter rises into place, then follows the cursor like a wave. */
@keyframes fxCharIn {
  from { opacity: 0; transform: translateY(105%) rotate(7deg); }
  to   { opacity: 1; transform: none; }
}
.fx-split .hero-line-inner { transform: none; animation: none; }
.fx-word { display: inline-block; white-space: nowrap; }
.fx-char, .fx-unit {
  display: inline-block; opacity: 0; will-change: transform, opacity;
  animation: fxCharIn .9s cubic-bezier(.16, 1, .3, 1) forwards;
  animation-delay: calc(.3s + var(--i, 0) * 26ms);
}
.fx-char { transition: translate .4s cubic-bezier(.16, 1, .3, 1), rotate .4s cubic-bezier(.16, 1, .3, 1); }
.fx-ready .fx-char, .fx-ready .fx-unit { will-change: auto; }

/* 3. Tilt cards: 3D tilt toward the pointer with a soft light following it. */
.fx-tilt {
  position: relative; overflow: hidden; will-change: transform;
  transform: perspective(900px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) translateY(var(--ty, 0px));
  transition: transform .3s ease-out, border-color .2s, opacity .8s ease;
}
.fx-tilt:hover { --ty: -4px; transform: perspective(900px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) translateY(var(--ty, 0px)); }
.fx-tilt::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; opacity: 0; transition: opacity .25s;
  background: radial-gradient(260px circle at var(--gx, 50%) var(--gy, 50%), rgba(115, 96, 228, .18), transparent 62%);
}
.fx-tilt:hover::after { opacity: 1; }

/* 5. Text that fills word by word as it scrolls into view. */
.fx-fw { opacity: .22; transition: opacity .35s ease; }
.fx-fw.is-on { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .fx-char, .fx-unit { animation: none; opacity: 1; }
  .fx-fw { opacity: 1; transition: none; }
  .fx-tilt, .fx-tilt:hover { transform: none; }
}
