const { useRef, useEffect, useState } = React; // ── HERO SECTION ────────────────────────────────────────────────────────────── const HeroSection = () => { const canvasRef = useRef(null); const [wordIdx, setWordIdx] = useState(0); const cycleWords = ['Software', 'Ecommerce', 'AI', 'Automation', 'Growth']; // Cycling word swap useEffect(() => { const id = setInterval(() => setWordIdx(i => (i + 1) % cycleWords.length), 1500); return () => clearInterval(id); }, []); // Word reveal stagger useEffect(() => { const els = document.querySelectorAll('.hero-word'); els.forEach((el, i) => setTimeout(() => el.classList.add('revealed'), 300 + i * 90)); }, []); // Fade up elements useEffect(() => { const els = document.querySelectorAll('.hero-fu'); els.forEach((el, i) => setTimeout(() => el.classList.add('vis'), 1200 + i * 150)); }, []); // Three.js — TorusKnot wireframe hero useEffect(() => { const canvas = canvasRef.current; if (!canvas || !window.THREE) return; const T = window.THREE; const scene = new T.Scene(); const camera = new T.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); camera.position.z = 4.5; const renderer = new T.WebGLRenderer({ canvas, alpha: true, antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // Primary torusknot — jaali/lattice feel const geo = new T.TorusKnotGeometry(1.3, 0.38, 180, 20); const mat = new T.MeshBasicMaterial({ color: 0xF5F1EA, wireframe: true, transparent: true, opacity: 0.18 }); const mesh = new T.Mesh(geo, mat); scene.add(mesh); // Gold accent ring const rGeo = new T.TorusGeometry(2.5, 0.006, 6, 120); const rMat = new T.MeshBasicMaterial({ color: 0xD4AF37, transparent: true, opacity: 0.35 }); const ring = new T.Mesh(rGeo, rMat); ring.rotation.x = Math.PI * 0.5; scene.add(ring); // Second thinner ring const r2Geo = new T.TorusGeometry(1.9, 0.004, 6, 120); const r2Mat = new T.MeshBasicMaterial({ color: 0xE25822, transparent: true, opacity: 0.25 }); const ring2 = new T.Mesh(r2Geo, r2Mat); ring2.rotation.x = Math.PI * 0.35; ring2.rotation.y = Math.PI * 0.2; scene.add(ring2); let mx = 0, my = 0, tx = 0, ty = 0; const onMouse = e => { mx = (e.clientX / window.innerWidth - 0.5); my = (e.clientY / window.innerHeight - 0.5); }; window.addEventListener('mousemove', onMouse); let scrollY = 0; const onScroll = () => { scrollY = window.scrollY; }; window.addEventListener('scroll', onScroll, { passive: true }); const clock = new T.Clock(); let raf; const tick = () => { raf = requestAnimationFrame(tick); const t = clock.getElapsedTime(); tx += (mx - tx) * 0.04; ty += (my - ty) * 0.04; mesh.rotation.x = t * 0.10 + ty * 0.35; mesh.rotation.y = t * 0.16 + tx * 0.35; ring.rotation.z = t * 0.04; ring.rotation.x = Math.PI * 0.5 + ty * 0.15; ring2.rotation.z = -t * 0.07; ring2.rotation.y = t * 0.03 + tx * 0.1; const prog = Math.min(scrollY / window.innerHeight, 1); camera.position.z = 4.5 - prog * 2.0; mat.opacity = Math.max(0, 0.18 - prog * 0.25); rMat.opacity = Math.max(0, 0.35 - prog * 0.5); r2Mat.opacity = Math.max(0, 0.25 - prog * 0.4); renderer.render(scene, camera); }; tick(); const onResize = () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }; window.addEventListener('resize', onResize); return () => { cancelAnimationFrame(raf); window.removeEventListener('mousemove', onMouse); window.removeEventListener('scroll', onScroll); window.removeEventListener('resize', onResize); geo.dispose(); mat.dispose(); rGeo.dispose(); rMat.dispose(); r2Geo.dispose(); r2Mat.dispose(); renderer.dispose(); }; }, []); const lines = [ ['We', 'build,', 'grow', '&'], ['automate', '—', 'for', 'brands'], ['that', 'refuse', 'to', 'be'], ['ordinary.'], ]; const allWords = lines.flat(); let wi = 0; return (
{/* Radial vignette */}
{/* Nav */} {/* Top-left label */}
[ JAIPUR · EST 2020 ]
{/* Logo center-top */}
ERIONT
{/* Hero text */}

{lines.map((line, li) => (
{line.map((word) => { const idx = wi++; const isAutomate = word === 'automate'; return ( {isAutomate ? {word} : word} ); })}
))}

{/* Cycling word */}
{cycleWords[wordIdx]}
{/* Scroll indicator */}
SCROLL
{/* Bottom marquee */}
{Array(8).fill(null).map((_, i) => ( Available for new projects · Q2 2026 · ))}
); }; // ── WOW SECTION — Point cloud sphere ───────────────────────────────────────── const WowSection = () => { const canvasRef = useRef(null); useEffect(() => { const canvas = canvasRef.current; if (!canvas || !window.THREE) return; const T = window.THREE; const scene = new T.Scene(); const w = canvas.clientWidth || window.innerWidth; const h = canvas.clientHeight || window.innerHeight; const camera = new T.PerspectiveCamera(70, w / h, 0.1, 100); camera.position.z = 5; const renderer = new T.WebGLRenderer({ canvas, alpha: true, antialias: true }); renderer.setSize(w, h); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // Fibonacci sphere distribution const count = 1200; const pos = new Float32Array(count * 3); const orig = new Float32Array(count * 3); const golden = Math.PI * (3 - Math.sqrt(5)); for (let i = 0; i < count; i++) { const y = 1 - (i / (count - 1)) * 2; const r = Math.sqrt(1 - y * y) * 2.2; const theta = golden * i; pos[i*3] = r * Math.cos(theta); pos[i*3+1] = y * 2.2; pos[i*3+2] = r * Math.sin(theta); orig[i*3] = pos[i*3]; orig[i*3+1] = pos[i*3+1]; orig[i*3+2] = pos[i*3+2]; } const geo = new T.BufferGeometry(); geo.setAttribute('position', new T.BufferAttribute(pos, 3)); const mat = new T.PointsMaterial({ color: 0xE25822, size: 0.038, transparent: true, opacity: 0.85 }); const pts = new T.Points(geo, mat); scene.add(pts); // Gold accent pts const gCount = 300; const gPos = new Float32Array(gCount * 3); for (let i = 0; i < gCount; i++) { const phi = Math.acos(-1 + (2 * i) / gCount); const th = Math.sqrt(gCount * Math.PI) * phi; gPos[i*3] = 2.8 * Math.sin(phi) * Math.cos(th); gPos[i*3+1] = 2.8 * Math.sin(phi) * Math.sin(th); gPos[i*3+2] = 2.8 * Math.cos(phi); } const gGeo = new T.BufferGeometry(); gGeo.setAttribute('position', new T.BufferAttribute(gPos, 3)); const gMat = new T.PointsMaterial({ color: 0xD4AF37, size: 0.022, transparent: true, opacity: 0.4 }); const gPts = new T.Points(gGeo, gMat); scene.add(gPts); let mxW = 0, myW = 0; const onMouse = e => { mxW = (e.clientX / window.innerWidth - 0.5) * 2; myW = (e.clientY / window.innerHeight - 0.5) * 2; }; window.addEventListener('mousemove', onMouse); const clock = new T.Clock(); let raf; const tick = () => { raf = requestAnimationFrame(tick); const t = clock.getElapsedTime(); const breathe = 1 + Math.sin(t * 0.7) * 0.07; const pArr = geo.attributes.position.array; for (let i = 0; i < count; i++) { const d = 1 + mxW * 0.08 * Math.sin(i * 0.07 + t) + myW * 0.08 * Math.cos(i * 0.07 + t); pArr[i*3] = orig[i*3] * breathe * d; pArr[i*3+1] = orig[i*3+1] * breathe; pArr[i*3+2] = orig[i*3+2] * breathe; } geo.attributes.position.needsUpdate = true; pts.rotation.y = t * 0.10; pts.rotation.x = t * 0.04; gPts.rotation.y = -t * 0.06; renderer.render(scene, camera); }; tick(); const onResize = () => { const nw = window.innerWidth, nh = window.innerHeight; camera.aspect = nw / nh; camera.updateProjectionMatrix(); renderer.setSize(nw, nh); }; window.addEventListener('resize', onResize); return () => { cancelAnimationFrame(raf); window.removeEventListener('mousemove', onMouse); window.removeEventListener('resize', onResize); geo.dispose(); mat.dispose(); gGeo.dispose(); gMat.dispose(); renderer.dispose(); }; }, []); return (

"The web is no longer
a brochure.
It's an experience."

); }; Object.assign(window, { HeroSection, WowSection });