"use client"; import { gsap } from "gsap"; type ScopeRef = { current: Element | null }; const ENTER_DURATION = 0.28; const EXIT_DURATION = 0.18; const ENTER_EASE = "power2.out"; const EXIT_EASE = "power1.in"; export function prefersReducedMotion() { return typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches; } export function runScopedMotion(scopeRef: ScopeRef, setup: (scope: Element) => void) { const scope = scopeRef.current; if (!scope || prefersReducedMotion()) return undefined; const context = gsap.context(() => setup(scope), scope); return () => context.revert(); } export function revealChildren(scope: Element, selector = "[data-animate]") { if (prefersReducedMotion()) return; const items = gsap.utils.toArray(selector, scope); if (!items.length) return; gsap.fromTo( items, { autoAlpha: 0, y: 14 }, { autoAlpha: 1, y: 0, duration: ENTER_DURATION, ease: ENTER_EASE, stagger: 0.045, clearProps: "transform,opacity,visibility" } ); } export function crossfadeIn(element: Element | null, y = 10) { if (!element || prefersReducedMotion()) return; gsap.fromTo( element, { autoAlpha: 0, y }, { autoAlpha: 1, y: 0, duration: ENTER_DURATION, ease: ENTER_EASE, clearProps: "transform,opacity,visibility" } ); } export function pulseFeedback(element: Element | null) { if (!element || prefersReducedMotion()) return; gsap.fromTo( element, { scale: 0.985 }, { scale: 1, duration: 0.22, ease: ENTER_EASE, clearProps: "transform" } ); } export function modalEnter(element: Element | null) { if (!element || prefersReducedMotion()) return; gsap.fromTo( element, { autoAlpha: 0, scale: 0.98, y: 10 }, { autoAlpha: 1, scale: 1, y: 0, duration: ENTER_DURATION, ease: ENTER_EASE, clearProps: "transform,opacity,visibility" } ); } export function modalExit(element: Element | null, onComplete: () => void) { if (!element || prefersReducedMotion()) { onComplete(); return; } gsap.to(element, { autoAlpha: 0, scale: 0.985, y: 6, duration: EXIT_DURATION, ease: EXIT_EASE, onComplete }); }