"use client"; import { useEffect, useRef, useState, type MutableRefObject } from "react"; import type * as ThreeNamespace from "three"; type ThreeRuntime = typeof ThreeNamespace; type AcidUniforms = { uTime: { value: number }; uScroll: { value: number }; uPulse: { value: number }; uPointer: { value: ThreeNamespace.Vector2 }; }; type SceneControls = { uniforms: AcidUniforms; setScrollProgress(progress: number): void; dispose(): void; }; function shouldSkipWebgl() { if (typeof window === "undefined") { return true; } const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; const coarsePointer = window.matchMedia("(pointer: coarse)").matches; const narrowScreen = window.matchMedia("(max-width: 720px)").matches; const memory = typeof navigator !== "undefined" ? (navigator as Navigator & { deviceMemory?: number }).deviceMemory : undefined; return reduceMotion || (coarsePointer && narrowScreen) || (typeof memory === "number" && memory <= 3); } export default function AcidExperience() { const rootRef = useRef(null); const mountRef = useRef(null); const controlsRef = useRef(null); const [fallback, setFallback] = useState(false); useEffect(() => { const mount = mountRef.current; const root = mount?.closest(".new-acid-page") || null; if (!mount || !root) { return undefined; } rootRef.current = root; const reduceMotionQuery = window.matchMedia("(prefers-reduced-motion: reduce)"); if (shouldSkipWebgl()) { setFallback(true); } let disposed = false; let sceneCleanup: (() => void) | undefined; let motionCleanup: (() => void) | undefined; Promise.all([ import("gsap"), import("gsap/ScrollTrigger"), import("lenis"), shouldSkipWebgl() ? Promise.resolve(null) : import("three"), ]) .then(([gsapModule, scrollModule, lenisModule, THREE]) => { if (disposed) { return; } const gsap = gsapModule.gsap || gsapModule.default; const ScrollTrigger = scrollModule.ScrollTrigger; gsap.registerPlugin(ScrollTrigger); if (THREE && !reduceMotionQuery.matches && mountRef.current) { controlsRef.current = createScene(THREE, mountRef.current); } motionCleanup = setupMotion({ root, gsap, ScrollTrigger, Lenis: lenisModule.default, controls: controlsRef, reduceMotionQuery, }); }) .catch(() => { if (!disposed) { setFallback(true); } }); sceneCleanup = () => { controlsRef.current?.dispose(); controlsRef.current = null; }; return () => { disposed = true; motionCleanup?.(); sceneCleanup?.(); rootRef.current?.classList.remove("new-acid-motion-ready", "new-acid-lenis-active"); rootRef.current = null; }; }, []); return (