/* global React */ const { useState, useEffect, useRef } = React; /* ─── Icons (Lucide-style, 1.5 stroke) ──────────────────────── */ function Icon({ name, size = 20, ...props }) { const stroke = props.stroke || 'currentColor'; const sw = 1.5; const common = { width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke, strokeWidth: sw, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': true, ...props }; switch (name) { case 'arrow-right': return ; case 'arrow-up-right':return ; case 'check': return ; case 'sparkle': return ; case 'phone': return ; case 'mic': return ; case 'volume': return ; case 'phone-off': return ; case 'play': return ; case 'star': return ; case 'shield': return ; case 'zap': return ; case 'target': return ; case 'brain': return ; case 'trending': return ; case 'clock': return ; case 'x': return ; case 'instagram': return ; case 'linkedin': return ; case 'menu': return ; default: return null; } } /* ─── Reveal-on-scroll hook ──────────────────────────────────── */ function useReveal() { const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver( (entries) => entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); } }), { threshold: 0.10, rootMargin: '0px 0px -32px 0px' } ); el.querySelectorAll('.reveal').forEach((n) => io.observe(n)); return () => io.disconnect(); }, []); return ref; } /* ─── Logo ───────────────────────────────────────────────────── */ function Logo() { return ( ColdCallable ); } /* ─── EmailCapture ───────────────────────────────────────────── */ function EmailCapture({ dark = false, placeholder = 'your@email.com', cta = 'Join Waitlist' }) { const [email, setEmail] = useState(''); const [state, setState] = useState('idle'); // idle | loading | done | error const validate = (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v); const submit = (e) => { e.preventDefault(); if (!validate(email)) { setState('error'); return; } setState('loading'); fetch('https://submit-form.com/v3DI3knqX', { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, body: JSON.stringify({ email }), }) .then((r) => { if (!r.ok) throw new Error(); setState('done'); }) .catch(() => setState('error')); }; return (
{state === 'done' ? (
You're on the list. We'll be in touch.
) : ( <> { setEmail(e.target.value); if (state === 'error') setState('idle'); }} aria-invalid={state === 'error'} /> )}
); } /* ─── CountUp ────────────────────────────────────────────────── */ function CountUp({ to, suffix = '', duration = 1600 }) { const ref = useRef(null); const [val, setVal] = useState(0); useEffect(() => { const el = ref.current; if (!el) return; let started = false; const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting && !started) { started = true; const start = performance.now(); const tick = (now) => { const t = Math.min(1, (now - start) / duration); const eased = 1 - Math.pow(1 - t, 3); setVal(Math.floor(eased * to)); if (t < 1) requestAnimationFrame(tick); else setVal(to); }; requestAnimationFrame(tick); } }); }, { threshold: 0.4 }); io.observe(el); return () => io.disconnect(); }, [to, duration]); return {val.toLocaleString()}{suffix}; } Object.assign(window, { Icon, useReveal, Logo, EmailCapture, CountUp });