const { useRef, useEffect } = React; // ── CUSTOM CURSOR ───────────────────────────────────────────────────────────── const CustomCursor = () => { const dotRef = useRef(null); const lblRef = useRef(null); useEffect(() => { let cx = -100, cy = -100, tx = -100, ty = -100; let raf; const onMove = (e) => { tx = e.clientX; ty = e.clientY; }; window.addEventListener('mousemove', onMove); const lerp = () => { raf = requestAnimationFrame(lerp); cx += (tx - cx) * 0.11; cy += (ty - cy) * 0.11; const dot = dotRef.current; const lbl = lblRef.current; if (dot) { dot.style.left = cx + 'px'; dot.style.top = cy + 'px'; } if (lbl) { lbl.style.left = cx + 'px'; lbl.style.top = cy + 'px'; } }; lerp(); const onOver = (e) => { const interactive = e.target.closest('a, button, [data-cursor]'); if (!interactive) return; dotRef.current?.classList.add('expanded'); const label = e.target.closest('[data-cursor-label]')?.getAttribute('data-cursor-label'); if (label && lblRef.current) { lblRef.current.textContent = label; lblRef.current.classList.add('visible'); } }; const onOut = (e) => { if (e.target.closest('a, button, [data-cursor]') && !e.relatedTarget?.closest('a, button, [data-cursor]')) { dotRef.current?.classList.remove('expanded'); if (lblRef.current) lblRef.current.classList.remove('visible'); } }; document.addEventListener('mouseover', onOver); document.addEventListener('mouseout', onOut); return () => { cancelAnimationFrame(raf); window.removeEventListener('mousemove', onMove); document.removeEventListener('mouseover', onOver); document.removeEventListener('mouseout', onOut); }; }, []); return ( <>
); }; // ── DIVIDER — jaali geometric ───────────────────────────────────────────────── const JaaliDivider = ({ flip = false }) => (
{Array.from({ length: 25 }).map((_, i) => { const x = (i / 24) * 1440; return ( ); })}
); // ── APP ─────────────────────────────────────────────────────────────────────── const App = () => { // Scroll-reveal for .fu elements useEffect(() => { const els = document.querySelectorAll('.fu:not(.hero-fu)'); const observer = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('vis'); }); }, { threshold: 0.15 }); els.forEach(el => observer.observe(el)); return () => observer.disconnect(); }, []); return ( <>
); }; ReactDOM.createRoot(document.getElementById('root')).render();