/* App + tweaks wiring + scroll reveal */ function App() { const defaults = window.__TWEAK_DEFAULTS; const [t, setTweak] = useTweaks(defaults); // Sync into window so non-React 3D handlers can read latest React.useEffect(() => { window.__tweaks = t; window.dispatchEvent(new Event('codekid:tweak')); // Apply accent CSS var const accentMap = { cyan: '#00E5FF', purple: '#7C3AED', green: '#00FFA3' }; document.documentElement.style.setProperty('--accent', accentMap[t.accent] || '#00E5FF'); }, [t]); // Scroll reveal — plain scroll listener (IntersectionObserver unreliable in preview iframe) React.useEffect(() => { const check = () => { const trigger = window.innerHeight * 0.92; document.querySelectorAll('.reveal:not(.in)').forEach(el => { const r = el.getBoundingClientRect(); if (r.top < trigger && r.bottom > 0) el.classList.add('in'); }); }; const raf = requestAnimationFrame(() => requestAnimationFrame(check)); window.addEventListener('scroll', check, { passive: true }); window.addEventListener('resize', check); return () => { cancelAnimationFrame(raf); window.removeEventListener('scroll', check); window.removeEventListener('resize', check); }; }, []); // Particle field const particles = t.particles ? : null; return ( <> {particles}