// ============================================================ // LANDING — shared bits (Request Access modal) // ============================================================ // Imported by landing-c.jsx. The modal POSTs to /landing/request-access // which writes to the platform DB so an admin can follow up with an // invite code. const { useState: useStL } = React; function RequestAccessModal({ open, onClose }) { const [submitted, setSubmitted] = useStL(false); const [submitting, setSubmitting] = useStL(false); const [error, setError] = useStL(null); const [email, setEmail] = useStL(''); const [company, setCompany] = useStL(''); const [role, setRole] = useStL('ops'); if (!open) return null; const onSubmit = async (e) => { e.preventDefault(); if (submitting) return; setSubmitting(true); setError(null); try { const resp = await fetch('/landing/request-access', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: email.trim().toLowerCase(), company: company.trim(), role }), }); if (!resp.ok) { const data = await resp.json().catch(() => ({})); throw new Error(data.detail || `Request failed (${resp.status})`); } setSubmitted(true); } catch (err) { setError(err.message); } finally { setSubmitting(false); } }; // Inline colors instead of var() — the landing page doesn't carry a // data-theme attribute, so the portal's themed tokens (--surface, // --text-strong, --border, …) resolve to empty here and the modal // would render transparent. Hardcoding keeps the modal looking like // a real card against the dark page. const C = { ink: '#1A1714', ink2: '#2F2A24', ink3: '#58524A', ink4: '#847C71', paper: '#FBFAF7', paperSunk: '#F5F3EE', rule: '#E6E1D6', accent: '#FF7A59', successSoft:'#D1FAE5', success: '#047857', errSoft: '#FEE2E2', err: '#B91C1C', }; return (
{submitted ? (
You're on the list.

We'll be in touch within two business days with an invite code and a short call to learn about your process.

Already have a code? Sign up →
) : (
Request access

Tell us a little about your team. We'll send you an invite code within two business days.

setEmail(e.target.value)} required disabled={submitting} /> setCompany(e.target.value)} disabled={submitting} /> {error && (
{error}
)}
Already have an invite code? Sign up →
)}
); } function FormField({ label, children }) { return (
{children}
); } // ============================================================ // Video modal — plays the 2-minute Remotion tour on the landing page // ============================================================ function VideoModal({ open, onClose, src }) { const videoRef = React.useRef(null); // Close on Escape; pause + reset when the modal closes. React.useEffect(() => { if (!open) return; const onKey = (e) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [open, onClose]); React.useEffect(() => { const v = videoRef.current; if (!v) return; if (open) { v.currentTime = 0; v.play().catch(() => { /* autoplay may be blocked; user can hit play */ }); } else { v.pause(); } }, [open]); if (!open) return null; return (
e.stopPropagation()} style={{ width: 'min(1200px, 100%)', aspectRatio: '16 / 9', position: 'relative', borderRadius: 14, overflow: 'hidden', boxShadow: '0 40px 120px -40px rgba(0,0,0,0.8), 0 0 0 1px rgba(255,255,255,0.06)', background: '#07080a', }} >
); } Object.assign(window, { RequestAccessModal, VideoModal });