Migrate the standalone Vite + React Three Fiber experience into the existing
Next.js site as the body of the How It Works page, replacing the Miles3 /
WhyChooseDoormile / TheDoormileWay content sections while preserving the
Elementor hero, global Header/Footer, layout, routing and SEO.
- New self-contained module: src/modules/how-it-works-3d/ (R3F scene, hooks,
zustand store, animations, curves, constants, utils, scoped CSS). App.jsx →
Experience3D.jsx; 3d_scene.jsx → models/Scene3D.jsx.
- 32MB GLB moved to public/models/3d_scene_final.glb; useGLTF paths updated.
- Client-only entry via dynamic ssr:false loader (Experience3DLoader).
- Self-managed fixed pin (tall section + absolute stage toggled
absolute(top)→fixed→absolute(bottom) from ScrollTrigger pin state), mirroring
the site's StrategySection, since the fixed header + ancestor overflow:hidden
break CSS sticky / GSAP pin.
- experience.css fully scoped under .dm-hiw-3d to avoid colliding with the
site's Elementor CSS.
- Global Lenis disabled on /how-it-works; module runs its own tuned Lenis;
jump-to-section scroll math made spacer-relative.
- Added zustand + maath; ESLint-ignored the ported module.
Rendering fixes (root causes found by driving headless Chrome):
- Bump three 0.171 → 0.184 to match @react-three/fiber@9.6 / drei@10.7 /
postprocessing@6.39 (0.171 silently failed to render this GLB and caused the
EffectComposer getContextAttributes().alpha crash). Other 3D routes verified.
- EffectComposer: Bloom + Vignette only. SSAO needs a NormalPass (v3 dropped
the old `disableNormalPass`), and that extra full-scene pass exhausted the
WebGL context on this heavy scene.
- Cap Canvas dpr to [1,1.5] to bound framebuffer memory on retina displays.
- Defer Canvas mount via IntersectionObserver (mountScene), matching
StrategySection, to ease StrictMode/first-render GPU pressure.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>