/**
 * Animation Utilities
 *
 * Schema-driven entry animations with duration and delay modifiers.
 *
 * They play WHEN SCROLLED TO, not on page load. The observer is in theme.js
 * (enqueued unconditionally, so it cannot go missing the way four effect
 * scripts did this week) and toggles `.is-in-view`. It removes the class on the
 * way out, so the animation replays the next time the block is reached.
 *
 * `html.js-animate` is stamped by that observer, so the gate below only applies
 * once JS is present: a visitor without it keeps the old play-on-load behaviour
 * rather than a page of opacity: 0.
 */

/* Animation base */
[class*="animate-"] {
    opacity: 0;
    animation-fill-mode: forwards;
    animation-duration: 400ms;
    animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}

/* Held at the start until the observer says the block is on screen. Suppressing
   animation-name rather than pausing, so removing .is-in-view rewinds it to the
   first frame and the next entry plays it from the beginning. */
html.js-animate [class*="animate-"]:not(.is-in-view) {
    animation-name: none;
    opacity: 0;
}

/* Fade */
.animate-fade {
    animation-name: fade;
}

@keyframes fade {
    to { opacity: 1; }
}

/* Fade up */
.animate-fade-up {
    transform: translateY(20px);
    animation-name: fade-up;
}

@keyframes fade-up {
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Fade down */
.animate-fade-down {
    transform: translateY(-20px);
    animation-name: fade-down;
}

@keyframes fade-down {
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Fade left (enters from right) */
.animate-fade-left {
    transform: translateX(20px);
    animation-name: fade-left;
}

@keyframes fade-left {
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

/* Fade right (enters from left) */
.animate-fade-right {
    transform: translateX(-20px);
    animation-name: fade-right;
}

@keyframes fade-right {
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

/* Scale */
.animate-scale {
    transform: scale(0.95);
    animation-name: scale;
}

@keyframes scale {
    to {
        opacity: 1;
        transform: scale(1);
    }
}

/* Duration modifiers */
.duration-fast { animation-duration: 200ms; }
.duration-normal { animation-duration: 400ms; }
.duration-slow { animation-duration: 600ms; }

/* Delay modifiers */
.delay-100 { animation-delay: 100ms; }
.delay-200 { animation-delay: 200ms; }
.delay-300 { animation-delay: 300ms; }
.delay-500 { animation-delay: 500ms; }

/* Accessibility - respect reduced motion preference */
@media (prefers-reduced-motion: reduce) {
    [class*="animate-"] {
        animation: none;
        opacity: 1;
        transform: none;
    }
}

/* ==========================================================================
   Scrub spread containment
   ==========================================================================

   `scrub-spread` translates each character sideways by its distance from the
   centre of the line. On a long heading the outermost characters travel far
   enough to paint outside the element, and a transform that leaves the box on
   the right adds document width — a horizontal scrollbar on every page using
   it. The JS caps the travel; this clips whatever the cap still lets through.

   overflow-x rather than overflow: clipping the block axis on a heading cuts
   the descenders off its own last line. `clip` rather than `hidden` because
   `hidden` makes the element a scroll container, which steals the wheel. */

[data-motion="scrub-spread"] {
    overflow-x: clip;
}
