// ui_kits/site/Hero.jsx — the signature surface. WebGL field + draggable physics dot in the wordmark. const Hero = ({ mode }) => { const heroRef = React.useRef(null); const dotRef = React.useRef(null); const [scrollVel, setScrollVel] = React.useState(0); // Scroll velocity drives the shader React.useEffect(() => { let last = scrollY, lastT = performance.now(); const onScroll = () => { const now = performance.now(); const v = (scrollY - last) / Math.max(1, now - lastT); setScrollVel(Math.min(2, Math.abs(v) * 6)); last = scrollY; lastT = now; }; addEventListener('scroll', onScroll, { passive: true }); const decay = setInterval(() => setScrollVel(v => v * 0.85), 80); return () => { removeEventListener('scroll', onScroll); clearInterval(decay); }; }, []); // Physics dot — drag, release, springs home React.useEffect(() => { const dot = dotRef.current; if (!dot) return; const home = { x: 0, y: 0 }; let x = 0, y = 0, vx = 0, vy = 0; let dragging = false; let pointer = { x: 0, y: 0 }; let raf; const onDown = (e) => { dragging = true; dot.setPointerCapture(e.pointerId); pointer.x = e.clientX; pointer.y = e.clientY; const r = dot.getBoundingClientRect(); pointer.dx = r.left + r.width / 2 - e.clientX; pointer.dy = r.top + r.height / 2 - e.clientY; }; const onMove = (e) => { if (!dragging) return; const r = heroRef.current.getBoundingClientRect(); const cx = (r.width / 2 + (e.clientX + pointer.dx - r.left - r.width / 2)); const cy = (r.height / 2 + (e.clientY + pointer.dy - r.top - r.height / 2)); // Position relative to wordmark anchor const target = dot.parentElement.getBoundingClientRect(); x = e.clientX + pointer.dx - (target.left + target.width / 2); y = e.clientY + pointer.dy - (target.top + target.height / 2); vx = 0; vy = 0; }; const onUp = () => { dragging = false; }; dot.addEventListener('pointerdown', onDown); addEventListener('pointermove', onMove); addEventListener('pointerup', onUp); const tick = () => { if (!dragging) { // spring back to home const k = 0.10, d = 0.78; const ax = -k * (x - home.x); const ay = -k * (y - home.y); vx = (vx + ax) * d; vy = (vy + ay) * d; x += vx; y += vy; } dot.style.transform = `translate(${x}px, ${y}px)`; raf = requestAnimationFrame(tick); }; tick(); return () => { cancelAnimationFrame(raf); dot.removeEventListener('pointerdown', onDown); removeEventListener('pointermove', onMove); removeEventListener('pointerup', onUp); }; }, []); return (
→ koa · brave · bold · fearless

hi.
i build for the web

mostly the parts that move.

view work say hi
→ press : for the command palette → scroll
); }; window.Hero = Hero;