/* [RFC-0106] GSAP scroll-triggered reveal animation. Hooks every [data-motion-reveal] element with fade / fade-up / fade-up-stagger variants. Respects prefers-reduced-motion. Do not introduce app-specific runtime composition or deployment behavior into this reusable package source file. RFC-0133: backfilled MODULE_MAP and CHANGE_SUMMARY markers for compass.validate compliance. */ export interface GsapRevealOptions { prefersReducedMotion?: boolean; } export async function initGsapReveal(options: GsapRevealOptions = {}): Promise { const prefersReducedMotion = options.prefersReducedMotion ?? window.matchMedia("(prefers-reduced-motion: reduce)").matches; if (prefersReducedMotion) return; const targets = document.querySelectorAll("[data-motion-reveal]"); if (targets.length === 0) return; const { gsap } = await import("gsap"); const { ScrollTrigger } = await import("gsap/ScrollTrigger"); gsap.registerPlugin(ScrollTrigger); for (const el of Array.from(targets)) { if (el.dataset.motionRevealReady === "true") continue; el.dataset.motionRevealReady = "true"; const variant = el.dataset.motionRevealVariant ?? "fade-up"; const threshold = Number.parseFloat(el.dataset.motionRevealThreshold ?? "0.15"); const once = el.dataset.motionRevealOnce !== "false"; const fromVars: gsap.TweenVars = { opacity: 0 }; if (variant === "fade-up" || variant === "fade-up-stagger") { fromVars.y = 16; } const toVars: gsap.TweenVars = { opacity: 1, y: 0, duration: 0.6, ease: "power2.out", scrollTrigger: { trigger: el, start: `top ${100 - threshold * 100}%`, toggleActions: once ? "play none none none" : "play none none reverse", }, }; if (variant === "fade-up-stagger") { const children = el.querySelectorAll(":scope > *"); gsap.from(Array.from(children), { ...fromVars, ...toVars, stagger: 0.1 }); } else { gsap.from(el, { ...fromVars, ...toVars }); } } }