{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "scroll-scene",
  "type": "registry:component",
  "title": "ScrollScene",
  "description": "A sticky R3F canvas whose camera dollies across scroll 'acts', driven by a single scrubbed ScrollTrigger that reads your existing Lenis-smoothed scroll — no competing scroll root.",
  "dependencies": [
    "three",
    "@react-three/fiber",
    "gsap"
  ],
  "registryDependencies": [],
  "tier": "free",
  "note": "Pairs with a Lenis + gsap.ticker smooth-scroll setup (see SmoothScroll). Render via next/dynamic ssr:false.",
  "files": [
    {
      "path": "components/prism/ScrollScene.tsx",
      "content": "\"use client\";\n\nimport { useRef, useLayoutEffect } from \"react\";\nimport { Canvas, useFrame, useThree } from \"@react-three/fiber\";\nimport * as THREE from \"three\";\nimport gsap from \"gsap\";\nimport { ScrollTrigger } from \"gsap/ScrollTrigger\";\n\ngsap.registerPlugin(ScrollTrigger);\n\n/**\n * ScrollScene — a sticky R3F canvas whose camera dollies/pans across scroll \"acts\".\n * Driven by a SINGLE scrubbed GSAP ScrollTrigger that reads the same Lenis-smoothed\n * scroll position the rest of Prism uses (SmoothScroll pumps Lenis via gsap.ticker +\n * ScrollTrigger.update), so there's no second scroll root to fight — unlike drei's\n * ScrollControls. Progress lives in a ref so useFrame reads it with zero re-renders.\n */\n\nconst ACTS: { pos: [number, number, number]; look: [number, number, number] }[] = [\n  { pos: [0, 0, 6], look: [0, 0, 0] },\n  { pos: [4, 1.5, 3], look: [0, 0, 0] },\n  { pos: [-3, -1, 4.5], look: [1, 0, 0] },\n];\n\nfunction Rig({ progress }: { progress: React.RefObject<number> }) {\n  const { camera } = useThree();\n  const pos = useRef(new THREE.Vector3());\n  const look = useRef(new THREE.Vector3());\n  const tmp = useRef(new THREE.Vector3());\n\n  useFrame(() => {\n    const p = THREE.MathUtils.clamp(progress.current, 0, 1);\n    const seg = p * (ACTS.length - 1);\n    const i = Math.min(Math.floor(seg), ACTS.length - 2);\n    const t = THREE.MathUtils.smoothstep(seg - i, 0, 1);\n    pos.current.fromArray(ACTS[i].pos).lerp(tmp.current.fromArray(ACTS[i + 1].pos), t);\n    look.current.fromArray(ACTS[i].look).lerp(tmp.current.fromArray(ACTS[i + 1].look), t);\n    camera.position.lerp(pos.current, 0.12);\n    camera.lookAt(look.current);\n  });\n  return null;\n}\n\nfunction Acts() {\n  return (\n    <>\n      <ambientLight intensity={0.45} />\n      <directionalLight position={[5, 5, 5]} intensity={1.3} />\n      <mesh position={[0, 0, 0]}>\n        <icosahedronGeometry args={[1, 0]} />\n        <meshStandardMaterial color=\"#5b7cff\" roughness={0.2} metalness={0.6} />\n      </mesh>\n      <mesh position={[2, 1, -1]}>\n        <torusKnotGeometry args={[0.5, 0.18, 128, 16]} />\n        <meshStandardMaterial color=\"#1fd4e6\" roughness={0.3} metalness={0.4} />\n      </mesh>\n      <mesh position={[-2, -1, 0.5]}>\n        <boxGeometry args={[0.9, 0.9, 0.9]} />\n        <meshStandardMaterial color=\"#ff3d81\" roughness={0.4} metalness={0.3} />\n      </mesh>\n    </>\n  );\n}\n\nexport function ScrollScene({ acts = 3 }: { acts?: number }) {\n  const wrap = useRef<HTMLDivElement>(null);\n  const progress = useRef(0);\n\n  useLayoutEffect(() => {\n    const el = wrap.current;\n    if (!el) return;\n    const reduce = window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n    const st = ScrollTrigger.create({\n      trigger: el,\n      start: \"top top\",\n      end: \"bottom bottom\",\n      scrub: reduce ? false : true,\n      invalidateOnRefresh: true,\n      onUpdate: (self) => {\n        progress.current = self.progress;\n      },\n    });\n    return () => st.kill();\n  }, []);\n\n  return (\n    <div ref={wrap} style={{ height: `${acts * 100}vh`, position: \"relative\" }}>\n      <div style={{ position: \"sticky\", top: 0, height: \"100vh\", width: \"100%\" }}>\n        <Canvas\n          dpr={[1, 1.5]}\n          gl={{ antialias: true, powerPreference: \"high-performance\" }}\n          camera={{ position: [0, 0, 6], fov: 50 }}\n        >\n          <Rig progress={progress} />\n          <Acts />\n        </Canvas>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/prism/ScrollScene.tsx"
    }
  ]
}