/*
 * Motion kit — hand-written once here, shipped verbatim into every generated
 * theme (never LLM-generated). All effects are driven by custom properties
 * owned by a profile stylesheet
 * (assets/motion/profiles/*.css). Entrances, heroes, hovers, and ambient
 * effects deliberately have independent clocks: coupling all four to one
 * duration made quick profiles frantic and slow profiles feel deceptively
 * fast. Generated CSS never overrides these values, so the committed profile
 * remains the authority for the site's motion character.
 *
 * Movement contract (BIGR: team feedback "too strong / dizzy"):
 * - Every translation is VERTICAL. The kit never moves content sideways —
 *   no translateX, no diagonal arrivals, no horizontal pans, no rotation.
 * - Every easing decelerates smoothly into rest with NO overshoot: no
 *   control point leaves [0, 1], so nothing springs past its rest position.
 * - Profiles differ through duration, travel distance, easing, masks, and
 *   focus (blur), not through direction of travel.
 *
 * Accessibility / progressive-enhancement contract:
 * - EVERYTHING lives inside @media screen and (prefers-reduced-motion:
 *   no-preference): reduced-motion users get a fully static, fully visible
 *   page, and so does PRINT — without the screen guard the motion-js reveal
 *   rules would apply to print media too, so printing before scrolling
 *   would emit blank (opacity: 0) sections.
 * - Scroll-reveal targets are hidden ONLY under the driver-owned
 *   `html.motion-js` scope. After DOM setup, only explicitly registered
 *   `.motion-target` elements stay hidden; dynamically inserted blocks fail
 *   open. If the script never loads or observer setup fails, nothing remains
 *   hidden. This preserves the build's progressive-enhancement contract.
 * - A focus escape persistently skips the owning entrance, so keyboard
 *   navigation can never land on content that hides again on focus-out.
 * - hero-entrance and the ambient effects are pure CSS (no JS dependency)
 *   and never end in a hidden state.
 */

:root {
    --motion-enter-duration: 900ms;
    --motion-hero-duration: 1300ms;
    --motion-hero-delay: 60ms;
    --motion-hover-duration: 360ms;
    --motion-ambient-duration: 16s;
    --motion-enter-ease: cubic-bezier(0.4, 0.2, 0.2, 1);
    --motion-hero-ease: cubic-bezier(0.4, 0.2, 0.2, 1);
    --motion-hover-ease: cubic-bezier(0.25, 0.1, 0.25, 1);
    --motion-ambient-ease: cubic-bezier(0.37, 0, 0.63, 1);
    --motion-reveal-keyframe: motion-calm-reveal;
    --motion-rise-keyframe: motion-calm-rise;
    --motion-fade-keyframe: motion-calm-fade;
    --motion-scale-keyframe: motion-calm-scale;
    --motion-blur-keyframe: motion-kit-blur;
    --motion-wipe-keyframe: motion-kit-wipe;
    --motion-wipe-up-keyframe: motion-kit-wipe-up;
    --motion-aperture-keyframe: motion-kit-aperture;
    --motion-zoom-keyframe: motion-kit-zoom;
    --motion-hero-keyframe: motion-calm-hero;
    --motion-stagger-keyframe: motion-calm-rise;
    --motion-drift-keyframe: motion-calm-drift;
    --motion-ken-burns-keyframe: motion-calm-ken-burns;
    --motion-gradient-keyframe: motion-calm-gradient;
    --motion-distance: 12px;
    --motion-stagger: 160ms;
    --motion-scale-from: 0.98;
    --motion-blur-amount: 5px;
    --motion-zoom-from: 1.02;
    --motion-hero-scale-from: 0.985;
    --motion-ken-burns-scale: 1.05;
    --motion-ambient-distance: 8px;
    --motion-hover-lift: -3px;
    --motion-hover-scale: 1.03;
    --motion-hover-brightness: 0.92;
    --motion-hover-shadow-opacity: 0.18;
}

@media screen and (prefers-reduced-motion: no-preference) {

    /* ── Scroll reveals (motion.js adds .is-visible on intersection) ─────
       Revealed by an ANIMATION with `backwards` fill (not a transition, and
       not `both`): the resting state after the entrance is the element's own
       plain styles, so its transition/transform stay free for hover effects
       (hover-lift on a revealed card keeps its own timing and travel). */

    html.motion-js:not(.motion-ready) .reveal,
    html.motion-js:not(.motion-ready) .reveal-up,
    html.motion-js:not(.motion-ready) .reveal-fade,
    html.motion-js:not(.motion-ready) .reveal-scale,
    html.motion-js:not(.motion-ready) .reveal-blur,
    html.motion-js:not(.motion-ready) .reveal-wipe,
    html.motion-js:not(.motion-ready) .reveal-wipe-up,
    html.motion-js:not(.motion-ready) .reveal-aperture,
    html.motion-js:not(.motion-ready) .reveal-zoom,
    html.motion-js:not(.motion-ready) .stagger-children > *,
    html.motion-js.motion-ready .reveal.motion-target:not(.is-visible),
    html.motion-js.motion-ready .reveal-up.motion-target:not(.is-visible),
    html.motion-js.motion-ready .reveal-fade.motion-target:not(.is-visible),
    html.motion-js.motion-ready .reveal-scale.motion-target:not(.is-visible),
    html.motion-js.motion-ready .reveal-blur.motion-target:not(.is-visible),
    html.motion-js.motion-ready .reveal-wipe.motion-target:not(.is-visible),
    html.motion-js.motion-ready .reveal-wipe-up.motion-target:not(.is-visible),
    html.motion-js.motion-ready .reveal-aperture.motion-target:not(.is-visible),
    html.motion-js.motion-ready .reveal-zoom.motion-target:not(.is-visible),
    html.motion-js.motion-ready .stagger-children > *.motion-target:not(.is-visible) {
        opacity: 0;
    }

    html.motion-js .reveal.motion-target.is-visible {
        opacity: 1;
        animation-name: var(--motion-reveal-keyframe);
        animation-duration: var(--motion-enter-duration);
        animation-timing-function: var(--motion-enter-ease);
        animation-fill-mode: backwards;
    }
    html.motion-js .reveal-up.motion-target.is-visible {
        opacity: 1;
        animation-name: var(--motion-rise-keyframe);
        animation-duration: var(--motion-enter-duration);
        animation-timing-function: var(--motion-enter-ease);
        animation-fill-mode: backwards;
    }
    html.motion-js .reveal-fade.motion-target.is-visible {
        opacity: 1;
        animation-name: var(--motion-fade-keyframe);
        animation-duration: var(--motion-enter-duration);
        animation-timing-function: var(--motion-enter-ease);
        animation-fill-mode: backwards;
    }
    html.motion-js .reveal-scale.motion-target.is-visible {
        opacity: 1;
        animation-name: var(--motion-scale-keyframe);
        animation-duration: var(--motion-enter-duration);
        animation-timing-function: var(--motion-enter-ease);
        animation-fill-mode: backwards;
    }
    html.motion-js .reveal-blur.motion-target.is-visible {
        opacity: 1;
        animation-name: var(--motion-blur-keyframe);
        animation-duration: var(--motion-enter-duration);
        animation-timing-function: var(--motion-enter-ease);
        animation-fill-mode: backwards;
    }
    html.motion-js .reveal-wipe.motion-target.is-visible {
        opacity: 1;
        animation-name: var(--motion-wipe-keyframe);
        animation-duration: var(--motion-enter-duration);
        animation-timing-function: var(--motion-enter-ease);
        animation-fill-mode: backwards;
    }
    html.motion-js .reveal-wipe-up.motion-target.is-visible {
        opacity: 1;
        animation-name: var(--motion-wipe-up-keyframe);
        animation-duration: var(--motion-enter-duration);
        animation-timing-function: var(--motion-enter-ease);
        animation-fill-mode: backwards;
    }
    html.motion-js .reveal-aperture.motion-target.is-visible {
        opacity: 1;
        animation-name: var(--motion-aperture-keyframe);
        animation-duration: var(--motion-enter-duration);
        animation-timing-function: var(--motion-enter-ease);
        animation-fill-mode: backwards;
    }
    html.motion-js .reveal-zoom.motion-target.is-visible {
        opacity: 1;
        animation-name: var(--motion-zoom-keyframe);
        animation-duration: var(--motion-enter-duration);
        animation-timing-function: var(--motion-enter-ease);
        animation-fill-mode: backwards;
    }

    /* Staggered card/column reveals: the CONTAINER carries the authored class,
       while motion.js observes each direct child. Cards that share a row enter
       together and use these delays as a cascade; cards stacked on narrow
       screens wait until their own viewport entry instead of finishing
       offscreen. The backwards fill holds a delayed child at its keyframe
       start until its turn. */
    html.motion-js .stagger-children > *.motion-target.is-visible {
        opacity: 1;
        animation-name: var(--motion-stagger-keyframe);
        animation-duration: var(--motion-enter-duration);
        animation-timing-function: var(--motion-enter-ease);
        animation-fill-mode: backwards;
        animation-delay: calc(var(--motion-stagger) * var(--motion-stagger-order, 0));
    }

    /* ── Shared entrances (profile character comes from the variables) ──── */

    /* Soft-focus fade: no travel at all; the content sharpens into place. */
    @keyframes motion-kit-blur {
        from {
            opacity: 0;
            filter: blur(var(--motion-blur-amount));
        }
        40% { opacity: 1; }
        to { opacity: 1; filter: blur(0); }
    }
    /* Vertical curtain: the content is unmasked top-to-bottom in place. */
    @keyframes motion-kit-wipe {
        from {
            opacity: 0;
            clip-path: inset(0 0 100% 0);
        }
        40% { opacity: 1; }
        to { opacity: 1; clip-path: inset(0); }
    }
    /* Inverse curtain: the content is unmasked bottom-to-top in place. */
    @keyframes motion-kit-wipe-up {
        from {
            opacity: 0;
            clip-path: inset(100% 0 0 0);
        }
        40% { opacity: 1; }
        to { opacity: 1; clip-path: inset(0); }
    }
    /* Letterbox: the mask opens from the vertical center to both edges. */
    @keyframes motion-kit-aperture {
        from {
            opacity: 0;
            clip-path: inset(50% 0 50% 0);
        }
        40% { opacity: 1; }
        to { opacity: 1; clip-path: inset(0); }
    }
    /* Zoom settle: arrives slightly enlarged and eases down to rest —
       the inverse of reveal-scale, suited to photography and covers. */
    @keyframes motion-kit-zoom {
        from {
            opacity: 0;
            transform: scale(var(--motion-zoom-from));
        }
        40% { opacity: 1; }
        to { opacity: 1; transform: none; }
    }

    /* Calm: soft fades and measured settling. */
    @keyframes motion-calm-reveal {
        from {
            opacity: 0;
            transform: translateY(calc(var(--motion-distance) * 0.5));
        }
        40% { opacity: 1; }
        to { opacity: 1; transform: none; }
    }
    @keyframes motion-calm-rise {
        from {
            opacity: 0;
            transform: translateY(var(--motion-distance));
        }
        40% { opacity: 1; }
        to { opacity: 1; transform: none; }
    }
    @keyframes motion-calm-fade {
        from { opacity: 0; }
        40% { opacity: 1; }
        to { opacity: 1; }
    }
    @keyframes motion-calm-scale {
        from {
            opacity: 0;
            transform: scale(var(--motion-scale-from));
        }
        40% { opacity: 1; }
        to { opacity: 1; transform: none; }
    }

    /* Energetic: quick vertical arrivals that grow slightly as they settle.
       Speed and a fast-decelerating ease carry the liveliness; there is no
       diagonal travel and no spring past the rest position. */
    @keyframes motion-energetic-reveal {
        from {
            opacity: 0;
            transform: translateY(calc(var(--motion-distance) * 0.6)) scale(var(--motion-scale-from));
        }
        40% { opacity: 1; }
        to { opacity: 1; transform: none; }
    }
    @keyframes motion-energetic-rise {
        from {
            opacity: 0;
            transform: translateY(var(--motion-distance)) scale(var(--motion-scale-from));
        }
        40% { opacity: 1; }
        to { opacity: 1; transform: none; }
    }
    @keyframes motion-energetic-fade {
        from { opacity: 0; }
        40% { opacity: 1; }
        to { opacity: 1; }
    }
    @keyframes motion-energetic-scale {
        from { opacity: 0; transform: scale(var(--motion-scale-from)); }
        40% { opacity: 1; }
        to { opacity: 1; transform: none; }
    }

    /* Dramatic: vertical masks and a soft-focus pull — cinematic through
       masking and focus, never through sideways movement. */
    @keyframes motion-dramatic-reveal {
        from {
            opacity: 0;
            clip-path: inset(0 0 100% 0);
        }
        40% { opacity: 1; }
        to { opacity: 1; clip-path: inset(0); }
    }
    @keyframes motion-dramatic-rise {
        from {
            opacity: 0;
            clip-path: inset(100% 0 0 0);
            transform: translateY(var(--motion-distance));
        }
        40% { opacity: 1; }
        to { opacity: 1; clip-path: inset(0); transform: none; }
    }
    @keyframes motion-dramatic-fade {
        from { opacity: 0; }
        40% { opacity: 1; }
        to { opacity: 1; }
    }
    @keyframes motion-dramatic-scale {
        from {
            opacity: 0;
            transform: scale(var(--motion-scale-from));
        }
        40% { opacity: 1; }
        to { opacity: 1; transform: none; }
    }

    /* ── Page-load entrance (pure CSS — fires on first paint, no JS) ───── */

    .hero-entrance {
        animation-name: var(--motion-hero-keyframe);
        animation-duration: var(--motion-hero-duration);
        animation-timing-function: var(--motion-hero-ease);
        animation-delay: var(--motion-hero-delay);
        /* Preserve the authored resting transform/filter after the entrance. */
        animation-fill-mode: backwards;
    }
    @keyframes motion-calm-hero {
        from {
            opacity: 0;
            filter: blur(3px);
            transform: translateY(calc(var(--motion-distance) * 0.5)) scale(var(--motion-hero-scale-from));
        }
        40% { opacity: 1; filter: blur(0); }
        to { opacity: 1; filter: blur(0); transform: none; }
    }
    @keyframes motion-energetic-hero {
        from {
            opacity: 0;
            transform: translateY(var(--motion-distance)) scale(var(--motion-hero-scale-from));
        }
        40% { opacity: 1; }
        to { opacity: 1; transform: none; }
    }
    /* Focus pull: the hero sharpens and settles down from a slight zoom. */
    @keyframes motion-dramatic-hero {
        from {
            opacity: 0;
            filter: blur(var(--motion-blur-amount));
            transform: scale(var(--motion-hero-scale-from));
        }
        40% { opacity: 1; filter: blur(0); }
        to { opacity: 1; filter: blur(0); transform: none; }
    }

    /* Initial/restored content and a keyboard-focused entrance are marked
       persistently by motion.js. Removing the animation (rather than forcing
       transform:none forever) leaves authored hover transforms available. */
    .reveal.motion-skip,
    .reveal-up.motion-skip,
    .reveal-fade.motion-skip,
    .reveal-scale.motion-skip,
    .reveal-blur.motion-skip,
    .reveal-wipe.motion-skip,
    .reveal-wipe-up.motion-skip,
    .reveal-aperture.motion-skip,
    .reveal-zoom.motion-skip,
    .stagger-children > *.motion-skip,
    .hero-entrance.motion-skip {
        opacity: 1 !important;
        animation: none !important;
    }

    /* Immediate CSS bridge for the focus event; motion.js then adds the
       persistent motion-skip state before focus can leave. */
    html.motion-js .reveal:focus-within,
    html.motion-js .reveal-up:focus-within,
    html.motion-js .reveal-fade:focus-within,
    html.motion-js .reveal-scale:focus-within,
    html.motion-js .reveal-blur:focus-within,
    html.motion-js .reveal-wipe:focus-within,
    html.motion-js .reveal-wipe-up:focus-within,
    html.motion-js .reveal-aperture:focus-within,
    html.motion-js .reveal-zoom:focus-within,
    html.motion-js .stagger-children > *:focus-within,
    .hero-entrance:focus-within {
        opacity: 1 !important;
        transform: none !important;
        filter: none !important;
        clip-path: none !important;
    }

    /* ── Ambient effects (pure CSS, never hide anything) ──────────────── */

    /* Slow vertical drift for a decorative element or image. */
    .ambient-drift {
        animation-name: var(--motion-drift-keyframe);
        animation-duration: var(--motion-ambient-duration);
        animation-timing-function: var(--motion-ambient-ease);
        animation-iteration-count: infinite;
        animation-direction: alternate;
    }
    @keyframes motion-calm-drift {
        from { transform: translateY(0); }
        to { transform: translateY(calc(var(--motion-ambient-distance) * -1)); }
    }
    @keyframes motion-energetic-drift {
        from { transform: translateY(0); }
        55% {
            transform: translateY(calc(var(--motion-ambient-distance) * -1));
        }
        to {
            transform: translateY(calc(var(--motion-ambient-distance) * -0.6));
        }
    }
    @keyframes motion-dramatic-drift {
        from {
            transform: translateY(calc(var(--motion-ambient-distance) * 0.5));
        }
        to {
            transform: translateY(calc(var(--motion-ambient-distance) * -0.5));
        }
    }

    /* One very slow zoom of the image inside a cover/figure, then rest. */
    .ken-burns { overflow: hidden; }
    .ken-burns img {
        animation-name: var(--motion-ken-burns-keyframe);
        animation-duration: calc(var(--motion-ambient-duration) * 2.5);
        animation-timing-function: var(--motion-ambient-ease);
        animation-fill-mode: both;
    }
    @keyframes motion-calm-ken-burns {
        from { transform: scale(1); }
        to { transform: scale(var(--motion-ken-burns-scale)); }
    }
    @keyframes motion-energetic-ken-burns {
        from { transform: scale(1.02); }
        to { transform: scale(var(--motion-ken-burns-scale)); }
    }
    @keyframes motion-dramatic-ken-burns {
        from { transform: scale(var(--motion-ken-burns-scale)); }
        to { transform: scale(1); }
    }

    /* Slow-breathing gradient background. The doubled class selector outranks
       core's `.has-*-gradient-background { background: … }` shorthand (which
       would otherwise reset background-size) regardless of stylesheet order.
       The drift is vertical, like every other movement in the kit. */
    .gradient-shift.gradient-shift {
        background-size: 180% 180%;
        animation-name: var(--motion-gradient-keyframe);
        animation-duration: calc(var(--motion-ambient-duration) * 1.5);
        animation-timing-function: var(--motion-ambient-ease);
        animation-iteration-count: infinite;
        animation-direction: alternate;
    }
    @keyframes motion-calm-gradient {
        from { background-position: 50% 0%; }
        to { background-position: 50% 100%; }
    }
    @keyframes motion-energetic-gradient {
        from { background-position: 50% 100%; }
        to { background-position: 50% 0%; }
    }
    @keyframes motion-dramatic-gradient {
        from { background-position: 50% 20%; }
        to { background-position: 50% 80%; }
    }

    /* ── Hover micro-interactions ──────────────────────────────────
       These live in the deterministic kit rather than generated page CSS.
       That keeps profile timing and reduced-motion behavior reliable even
       when a site needs no other layout utilities or generated CSS is
       rejected. Profile variables still give each site a distinct feel. */

    .hover-lift {
        transition:
            transform var(--motion-hover-duration) var(--motion-hover-ease),
            box-shadow var(--motion-hover-duration) var(--motion-hover-ease);
    }
    .hover-lift:hover {
        transform: translateY(var(--motion-hover-lift));
        box-shadow: 0 16px 40px rgb(0 0 0 / var(--motion-hover-shadow-opacity));
    }

    .hover-reveal {
        position: relative;
        overflow: hidden;
    }
    .hover-reveal img {
        transition:
            transform var(--motion-hover-duration) var(--motion-hover-ease),
            filter var(--motion-hover-duration) var(--motion-hover-ease);
    }
    .hover-reveal:hover img {
        transform: scale(var(--motion-hover-scale));
        filter: brightness(var(--motion-hover-brightness));
    }
}
