/* ─────────────────────────────────────────────────────────────────────────────
   Websly Motion System (v5.1)

   Provides:
   - Card-grid scroll-in animations (fade-up, blur-up, scale-in, flip-3d, etc.)
   - Hero text-effect initial states (the JS engine drives the reveal)
   - Parallax layer styles
   - Smooth-scroll inertia rail (when Customizer mode = "inertia")

   Pairs with assets/js/websly-motion.js. This file ONLY handles initial
   states + keyframes; the engine handles when each animation fires.

   Reduced-motion guard at the bottom forces all animations off when
   the OS-level `prefers-reduced-motion` is set or when the Customizer
   density is 'off'.
   ───────────────────────────────────────────────────────────────────────── */

/* ─── Card scroll-in animations ───────────────────────────────────────────
   Apply to a parent container with `data-ws-card-anim="<preset>"` and
   each child card automatically picks up the right initial state. The JS
   engine flips `data-ws-anim-state="in"` once the parent enters viewport.

   Cards must be the DIRECT children of the container, OR have the
   `[data-ws-card]` attribute on themselves for custom positioning.
   ─────────────────────────────────────────────────────────────────────── */

[data-ws-card-anim] > [data-ws-card],
[data-ws-card-anim] > .ws-card,
[data-ws-card-anim] > .websly-card,
[data-ws-card-anim] > .feature-card,
[data-ws-card-anim] > .pricing-card,
[data-ws-card-anim] > .pricing-tier,
[data-ws-card-anim] > .testimonial-card,
[data-ws-card-anim] > .service-card,
[data-ws-card-anim] > article,
[data-ws-card-anim] > li {
    will-change: transform, opacity, filter;
    transition-property: transform, opacity, filter;
    transition-duration: var(--ws-anim-duration, 700ms);
    transition-timing-function: var(--ws-anim-ease, cubic-bezier(0.22, 1, 0.36, 1));
}

/* fade-up — 30px translate, opacity */
[data-ws-card-anim="fade-up"] > * {
    opacity: 0;
    transform: translate3d(0, 30px, 0);
}
[data-ws-card-anim="fade-up"][data-ws-anim-state="in"] > * {
    opacity: 1;
    transform: translate3d(0, 0, 0);
}

/* fade-up-blur — fade-up plus a soft blur unblurring */
[data-ws-card-anim="fade-up-blur"] > * {
    opacity: 0;
    transform: translate3d(0, 32px, 0);
    filter: blur(12px);
}
[data-ws-card-anim="fade-up-blur"][data-ws-anim-state="in"] > * {
    opacity: 1;
    transform: translate3d(0, 0, 0);
    filter: blur(0);
}

/* scale-in — soft scale-up, Apple-style */
[data-ws-card-anim="scale-in"] > * {
    opacity: 0;
    transform: scale(0.92);
}
[data-ws-card-anim="scale-in"][data-ws-anim-state="in"] > * {
    opacity: 1;
    transform: scale(1);
}

/* flip-3d — rotate around the X axis like a card flipping into place */
[data-ws-card-anim="flip-3d"] {
    perspective: 1200px;
}
[data-ws-card-anim="flip-3d"] > * {
    opacity: 0;
    transform: rotateX(-65deg) translate3d(0, 20px, 0);
    transform-origin: center top;
    transform-style: preserve-3d;
    backface-visibility: hidden;
}
[data-ws-card-anim="flip-3d"][data-ws-anim-state="in"] > * {
    opacity: 1;
    transform: rotateX(0) translate3d(0, 0, 0);
}

/* slide-side — odd cards from left, even from right */
[data-ws-card-anim="slide-side"] > *:nth-child(odd) {
    opacity: 0;
    transform: translate3d(-40px, 0, 0);
}
[data-ws-card-anim="slide-side"] > *:nth-child(even) {
    opacity: 0;
    transform: translate3d(40px, 0, 0);
}
[data-ws-card-anim="slide-side"][data-ws-anim-state="in"] > * {
    opacity: 1;
    transform: translate3d(0, 0, 0);
}

/* stagger-cascade — strong directional rise with subtle scale */
[data-ws-card-anim="stagger-cascade"] > * {
    opacity: 0;
    transform: translate3d(0, 60px, 0) scale(0.96);
}
[data-ws-card-anim="stagger-cascade"][data-ws-anim-state="in"] > * {
    opacity: 1;
    transform: translate3d(0, 0, 0) scale(1);
}

/* soft-rise — minimal 12px translate, fast stagger (Linear / Vercel feel) */
[data-ws-card-anim="soft-rise"] > * {
    opacity: 0;
    transform: translate3d(0, 12px, 0);
}
[data-ws-card-anim="soft-rise"][data-ws-anim-state="in"] > * {
    opacity: 1;
    transform: translate3d(0, 0, 0);
}

/* mask-reveal — overflow-hidden parent, child slides up from below the mask */
[data-ws-card-anim="mask-reveal"] > * {
    overflow: hidden;
    isolation: isolate;
}
[data-ws-card-anim="mask-reveal"] > * > * {
    transform: translate3d(0, 100%, 0);
    transition: transform var(--ws-anim-duration, 800ms) cubic-bezier(0.22, 1, 0.36, 1);
}
[data-ws-card-anim="mask-reveal"][data-ws-anim-state="in"] > * > * {
    transform: translate3d(0, 0, 0);
}

/* Stagger delays applied via inline style by the engine, but a CSS fallback
   provides up to 12 cards of pre-computed delay so animations work even
   if JS fails to set the per-card index. */
[data-ws-card-anim] > *:nth-child(1)  { transition-delay: calc(var(--ws-anim-stagger, 80ms) * 0); }
[data-ws-card-anim] > *:nth-child(2)  { transition-delay: calc(var(--ws-anim-stagger, 80ms) * 1); }
[data-ws-card-anim] > *:nth-child(3)  { transition-delay: calc(var(--ws-anim-stagger, 80ms) * 2); }
[data-ws-card-anim] > *:nth-child(4)  { transition-delay: calc(var(--ws-anim-stagger, 80ms) * 3); }
[data-ws-card-anim] > *:nth-child(5)  { transition-delay: calc(var(--ws-anim-stagger, 80ms) * 4); }
[data-ws-card-anim] > *:nth-child(6)  { transition-delay: calc(var(--ws-anim-stagger, 80ms) * 5); }
[data-ws-card-anim] > *:nth-child(7)  { transition-delay: calc(var(--ws-anim-stagger, 80ms) * 6); }
[data-ws-card-anim] > *:nth-child(8)  { transition-delay: calc(var(--ws-anim-stagger, 80ms) * 7); }
[data-ws-card-anim] > *:nth-child(9)  { transition-delay: calc(var(--ws-anim-stagger, 80ms) * 8); }
[data-ws-card-anim] > *:nth-child(10) { transition-delay: calc(var(--ws-anim-stagger, 80ms) * 9); }
[data-ws-card-anim] > *:nth-child(11) { transition-delay: calc(var(--ws-anim-stagger, 80ms) * 10); }
[data-ws-card-anim] > *:nth-child(12) { transition-delay: calc(var(--ws-anim-stagger, 80ms) * 11); }

/* "all-at-once" mode — JS sets data-ws-stagger="off" to clear delays */
[data-ws-card-anim][data-ws-stagger="off"] > * {
    transition-delay: 0ms !important;
}

/* ─── Hero text animations — initial states only ──────────────────────────
   The engine splits text into per-letter <span class="ws-char">…</span>
   wrappers and per-word <span class="ws-word">…</span>. CSS then provides
   the initial state per preset; the engine reveals on intersection.
   ─────────────────────────────────────────────────────────────────────── */

[data-ws-text-anim] {
    /* Avoid layout reflow during JS splitting. Fail-safe: if motion JS doesn't
       add .ws-text-ready within 3s (script error, ad blocker, slow load),
       force visibility:visible so titles never stay permanently hidden. */
    visibility: hidden;
    animation: ws-text-fail-safe-reveal 0s linear 3s forwards;
}
[data-ws-text-anim].ws-text-ready {
    visibility: visible;
    animation: none;
}
@keyframes ws-text-fail-safe-reveal {
    to { visibility: visible; }
}

[data-ws-text-anim] .ws-word {
    display: inline-block;
    white-space: nowrap; /* keeps a word from breaking mid-letter */
}
[data-ws-text-anim] .ws-char {
    display: inline-block;
    will-change: transform, opacity, filter;
}

/* blur-reveal — Apple-style each-letter blur unblurring */
[data-ws-text-anim="blur-reveal"] .ws-char {
    opacity: 0;
    filter: blur(14px);
    transform: translate3d(0, 0.2em, 0);
    transition: opacity 1400ms cubic-bezier(0.22, 1, 0.36, 1),
                filter 1400ms cubic-bezier(0.22, 1, 0.36, 1),
                transform 1400ms cubic-bezier(0.22, 1, 0.36, 1);
}
[data-ws-text-anim="blur-reveal"][data-ws-anim-state="in"] .ws-char {
    opacity: 1;
    filter: blur(0);
    transform: translate3d(0, 0, 0);
}

/* asterisk-resolve — letters initially shown as ✱; engine swaps to real */
[data-ws-text-anim="asterisk-resolve"] .ws-char {
    opacity: 0.85;
}
[data-ws-text-anim="asterisk-resolve"][data-ws-anim-state="in"] .ws-char {
    opacity: 1;
}
[data-ws-text-anim="asterisk-resolve"] .ws-char.is-resolving {
    color: var(--theme-accent, currentColor);
    opacity: 0.6;
}

/* wave-in — sine-wave delayed entrance */
[data-ws-text-anim="wave-in"] .ws-char {
    opacity: 0;
    transform: translate3d(0, 0.4em, 0) rotate(8deg);
    transition: opacity 1200ms cubic-bezier(0.22, 1, 0.36, 1),
                transform 1400ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
[data-ws-text-anim="wave-in"][data-ws-anim-state="in"] .ws-char {
    opacity: 1;
    transform: translate3d(0, 0, 0) rotate(0);
}

/* gradient-wipe — background-clip text with animated gradient position.
   Uses @property (Chrome 85+ / Safari 16.4+ / Firefox 128+). Falls back to
   plain text on unsupported browsers via the @supports guard. */
@supports (background: paint(something)) or (transition: --x 1s) {
    @property --ws-wipe-stop {
        syntax: '<percentage>';
        inherits: false;
        initial-value: 0%;
    }
    [data-ws-text-anim="gradient-wipe"] {
        --ws-wipe-stop: 0%;
        background: linear-gradient(
            90deg,
            currentColor 0%,
            currentColor calc(var(--ws-wipe-stop) - 5%),
            color-mix(in srgb, currentColor 28%, transparent) calc(var(--ws-wipe-stop) + 0%),
            color-mix(in srgb, currentColor 28%, transparent) 100%
        );
        -webkit-background-clip: text;
        background-clip: text;
        color: transparent;
        transition: --ws-wipe-stop 2400ms cubic-bezier(0.22, 1, 0.36, 1);
    }
    [data-ws-text-anim="gradient-wipe"][data-ws-anim-state="in"] {
        --ws-wipe-stop: 105%;
    }
}
/* No-@property fallback: title still renders, just no animation. */
@supports not ((background: paint(something)) or (transition: --x 1s)) {
    [data-ws-text-anim="gradient-wipe"] {
        /* No special styling — text renders in its inherited colour. */
    }
}

/* split-rotate — 3D rotateX entrance per letter */
[data-ws-text-anim="split-rotate"] {
    perspective: 1000px;
}
[data-ws-text-anim="split-rotate"] .ws-char {
    opacity: 0;
    transform: rotateX(-90deg) translate3d(0, 0.3em, 0);
    transform-origin: center bottom;
    transition: opacity 1200ms cubic-bezier(0.22, 1, 0.36, 1),
                transform 1400ms cubic-bezier(0.22, 1, 0.36, 1);
}
[data-ws-text-anim="split-rotate"][data-ws-anim-state="in"] .ws-char {
    opacity: 1;
    transform: rotateX(0) translate3d(0, 0, 0);
}

/* mask-stagger — overflow-hidden per word, char rises from below.
   Padding/margin trick keeps descenders (g, p, y, q) un-clipped at rest. */
[data-ws-text-anim="mask-stagger"] .ws-word {
    overflow: hidden;
    padding-bottom: 0.12em;
    margin-bottom: -0.12em;
    line-height: 1.15;
}
[data-ws-text-anim="mask-stagger"] .ws-char {
    transform: translate3d(0, 110%, 0);
    transition: transform 1400ms cubic-bezier(0.22, 1, 0.36, 1);
}
[data-ws-text-anim="mask-stagger"][data-ws-anim-state="in"] .ws-char {
    transform: translate3d(0, 0, 0);
}

/* scramble — handled entirely by JS; CSS just keeps the rendered state stable */
[data-ws-text-anim="scramble"] .ws-char {
    font-variant-numeric: tabular-nums; /* prevents width jitter during scramble */
}

/* ─── Parallax layers ──────────────────────────────────────────────────────
   Apply `data-ws-parallax="<speed>"` to any element. Speed is a multiplier
   relative to scroll: 0 = pinned (no movement), 0.5 = half-speed, 1 = normal,
   -0.3 = inverse direction. Engine sets transform via translate3d on
   each scroll frame.
   ─────────────────────────────────────────────────────────────────────── */

[data-ws-parallax] {
    will-change: transform;
}

/* ─── Smooth-scroll inertia rail ──────────────────────────────────────────
   Active only when JS sets `data-ws-smooth="inertia"` on <html>. The engine
   wraps page content in a scroll proxy and animates its translateY.
   ─────────────────────────────────────────────────────────────────────── */

html[data-ws-smooth="inertia"] {
    overflow: hidden;
    height: 100vh;
}
html[data-ws-smooth="inertia"] body {
    overflow: hidden;
    height: 100vh;
}
html[data-ws-smooth="inertia"] .ws-smooth-wrapper {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    width: 100%;
    will-change: transform;
}
html[data-ws-smooth="native"] {
    scroll-behavior: smooth;
}

/* ─── Reduced-motion + density="off" guard ────────────────────────────────
   Forces every motion-system animation off when the OS or the Customizer
   density setting requests it. Existing legacy animations have their own
   reduced-motion guards in websly-effects.css.
   ─────────────────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
    [data-ws-card-anim] > *,
    [data-ws-text-anim] .ws-char,
    [data-ws-text-anim] .ws-word,
    [data-ws-parallax] {
        transition: none !important;
        animation: none !important;
        transform: none !important;
        opacity: 1 !important;
        filter: none !important;
    }
}

html[data-ws-anim-density="off"] [data-ws-card-anim] > *,
html[data-ws-anim-density="off"] [data-ws-text-anim] .ws-char,
html[data-ws-anim-density="off"] [data-ws-text-anim] .ws-word,
html[data-ws-anim-density="off"] [data-ws-parallax] {
    transition: none !important;
    animation: none !important;
    transform: none !important;
    opacity: 1 !important;
    filter: none !important;
}

/* When density="reduced", lighter durations + no parallax */
html[data-ws-anim-density="reduced"] [data-ws-card-anim] {
    --ws-anim-duration: 400ms;
    --ws-anim-stagger: 40ms;
}
html[data-ws-anim-density="reduced"] [data-ws-parallax] {
    transform: none !important;
}
