/* ─────────────────────────────────────────────────────────────────────────
 * Motion Intensity — Visual Intensity System 5.3 Phase 3b
 *
 * Animations for elements that opted into the global motion contract via
 * data-ws-text-motion / data-ws-image-motion (emitted by
 * websly_motion_text_attrs_global() and websly_motion_image_attrs_global()).
 *
 * Pairs with assets/js/websly-motion-intensity.js for the IntersectionObserver
 * that adds .is-revealed when an element enters the viewport.
 *
 * Honoured by:
 *   --ws-motion-intensity-mult   :root var, scales duration + distance
 *   --ws-motion-stagger-ms       :root var, stagger between siblings
 *   --ws-motion-duration-base    :root var, base reveal duration (600ms)
 *
 * Exempt blocks NEVER receive these attributes (the helpers return '' for
 * them) so this CSS only ever applies to opted-in blocks.
 * ─────────────────────────────────────────────────────────────────────── */

/* ═════════════════════════════════════════════════════════════════════
   Initial states (before .is-revealed) — set so the IntersectionObserver
   has something to transition FROM. transform/opacity/filter are GPU-
   accelerated and cheap.
   ═══════════════════════════════════════════════════════════════════ */

[data-ws-text-motion] {
    will-change: opacity, transform, filter;
    transition-property: opacity, transform, filter;
    transition-duration: calc(var(--ws-motion-duration-base, 600ms) * var(--ws-motion-intensity-mult, 1));
    transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}

/* fade-up — opacity 0 + slight downward offset */
[data-ws-text-motion="fade-up"] {
    opacity: 0;
    transform: translate3d(0, calc(16px * var(--ws-motion-intensity-mult, 1)), 0);
}
[data-ws-text-motion="fade-up"].is-revealed {
    opacity: 1;
    transform: translate3d(0, 0, 0);
}

/* blur-in — opacity 0 + Gaussian blur. Heaviest of the four — GPU does fine
   on modern devices; falls back to fade-only when blur unsupported. */
[data-ws-text-motion="blur-in"] {
    opacity: 0;
    filter: blur(calc(12px * var(--ws-motion-intensity-mult, 1)));
}
[data-ws-text-motion="blur-in"].is-revealed {
    opacity: 1;
    filter: blur(0);
}

/* letter-stagger — wrapper has no animation itself; per-letter spans
   (injected by JS) get the cascade. */
[data-ws-text-motion="letter-stagger"] {
    opacity: 1;
}
[data-ws-text-motion="letter-stagger"] .ws-motion-letter {
    display: inline-block;
    opacity: 0;
    transform: translate3d(0, calc(0.4em * var(--ws-motion-intensity-mult, 1)), 0);
    transition: opacity 500ms cubic-bezier(0.16, 1, 0.3, 1),
                transform 500ms cubic-bezier(0.16, 1, 0.3, 1);
    transition-delay: calc(var(--ws-motion-letter-index, 0) * var(--ws-motion-stagger-ms, 100ms) * 0.5);
}
[data-ws-text-motion="letter-stagger"].is-revealed .ws-motion-letter {
    opacity: 1;
    transform: translate3d(0, 0, 0);
}

/* word-scramble — JS replaces text with random characters that resolve.
   CSS just keeps the element visible (no opacity dance). */
[data-ws-text-motion="word-scramble"] {
    opacity: 1;
}

/* ═════════════════════════════════════════════════════════════════════
   Image motion presets (Phase 5 will wire these into block render
   callbacks; CSS defined now for completeness).
   ═══════════════════════════════════════════════════════════════════ */

[data-ws-image-motion] {
    will-change: opacity, transform, filter;
    transition-property: opacity, transform, filter;
    transition-duration: calc(var(--ws-motion-duration-base, 600ms) * var(--ws-motion-intensity-mult, 1));
    transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}

[data-ws-image-motion="fade"] { opacity: 0; }
[data-ws-image-motion="fade"].is-revealed { opacity: 1; }

[data-ws-image-motion="zoom-out"] {
    opacity: 0;
    transform: scale(1.1);
}
[data-ws-image-motion="zoom-out"].is-revealed {
    opacity: 1;
    transform: scale(1);
}

[data-ws-image-motion="blur-fade"] {
    opacity: 0;
    filter: blur(16px);
}
[data-ws-image-motion="blur-fade"].is-revealed {
    opacity: 1;
    filter: blur(0);
}

[data-ws-image-motion="slide-up"] {
    opacity: 0;
    transform: translate3d(0, 32px, 0);
}
[data-ws-image-motion="slide-up"].is-revealed {
    opacity: 1;
    transform: translate3d(0, 0, 0);
}

/* ═════════════════════════════════════════════════════════════════════
   GLOBAL KILL SWITCHES — both win over individual preset styles via the
   body-class selector specificity AND !important on the reset values.
   ═══════════════════════════════════════════════════════════════════ */

/* Intensity = Off → show everything instantly, no animation ever runs. */
body.ws-motion-intensity-off [data-ws-text-motion],
body.ws-motion-intensity-off [data-ws-image-motion] {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    transition: none !important;
}
body.ws-motion-intensity-off [data-ws-text-motion] .ws-motion-letter {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
}

/* OS-level reduced-motion preference — sacred. Same outcome as Off. */
@media (prefers-reduced-motion: reduce) {
    [data-ws-text-motion],
    [data-ws-image-motion] {
        opacity: 1 !important;
        transform: none !important;
        filter: none !important;
        transition: none !important;
    }
    [data-ws-text-motion] .ws-motion-letter {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }
}

/* Noscript — if JS doesn't load, the observer never fires, so elements
   would stay invisible forever. Reveal everything immediately. */
.no-js [data-ws-text-motion],
.no-js [data-ws-image-motion] {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
}
