// demo-modal.jsx — "the 90-second tour": a guided, auto-advancing walkthrough // of the real Werkd app screens, opened from the hero's "See 90s demo" button. // Reuses the actual phone-screen components (no fake UI). // Listens for window CustomEvent('werkd:open-demo'). Loads after phone-mock.jsx, // shared-bits.jsx, direction-a.jsx, direction-a-extras2.jsx. const DEMO_STEPS = [ { key: 'today', label: 'Today', title: 'Your whole day, one line.', body: 'Next job, route, what you’ve earned, what needs attention — the moment you open the app.', render: () => }, { key: 'composer', label: '⊕ Capture', title: 'Say it. Werkd builds it.', body: 'Tap ⊕ and talk — “replaced the water heater at Patel’s, $1,240.” Werkd builds the job, quote, or expense; you approve.', render: () => }, { key: 'field', label: 'Field Mode', title: 'On the job, hands-free.', body: 'The timer runs while you work. Tap the mic and tell werkd what you used or found — it logs the materials and writes the invoice while your hands stay on the job.', render: () => }, { key: 'job', label: 'Job record', title: 'One job, one screen.', body: 'Schedule, customer, scope, materials, photos — and one button for the job’s next step.', render: () => }, { key: 'desk', label: 'Desk', title: 'Decisions, not paperwork.', body: 'Werkd drafts the invoice, the quote follow-up — you approve. Payment reminders run on their own schedule. The day’s admin, cleared in taps.', render: () => }, { key: 'ledger', label: 'Ledger', title: 'Look it up, then leave.', body: 'Money pulse up top. One search across customers, invoices, quotes, and jobs — price book and reports live here too.', render: () => }, { key: 'sched', label: 'Scheduling', title: 'Customers book when it suits you.', body: 'Werkd offers only the slots that fit your route and your day — not raw calendar gaps.', render: () => }, { key: 'crm', label: 'Customers', title: 'Every customer, remembered.', body: 'Jobs, quotes, payments, photos — threaded per customer. Werkd nudges the follow-ups you’d forget.', render: () => }, ]; const STEP_MS = 6000; function DemoModal() { const DA = window.DA; const m = window.useIsMobile ? window.useIsMobile() : false; const [open, setOpen] = React.useState(false); const [i, setI] = React.useState(0); const [paused, setPaused] = React.useState(false); const [typed, setTyped] = React.useState(''); const timerRef = React.useRef(null); // open via global event React.useEffect(() => { const onOpen = () => { setI(0); setOpen(true); setPaused(false); }; window.addEventListener('werkd:open-demo', onOpen); return () => window.removeEventListener('werkd:open-demo', onOpen); }, []); // esc to close, lock body scroll while open React.useEffect(() => { if (!open) return; const onKey = (e) => { if (e.key === 'Escape') setOpen(false); if (e.key === 'ArrowRight') { setI((p) => (p + 1) % DEMO_STEPS.length); setPaused(true); } if (e.key === 'ArrowLeft') { setI((p) => (p - 1 + DEMO_STEPS.length) % DEMO_STEPS.length); setPaused(true); } }; window.addEventListener('keydown', onKey); const prevOverflow = document.body.style.overflow; document.body.style.overflow = 'hidden'; return () => { window.removeEventListener('keydown', onKey); document.body.style.overflow = prevOverflow; }; }, [open]); // Type the caption out like Werkd narrating each screen, then dwell ~5s and // advance — turns the slideshow into a guided, alive walkthrough. React.useEffect(() => { if (!open) return; const full = DEMO_STEPS[i].body; if (paused) { setTyped(full); return; } setTyped(''); let idx = 0; const SPEED = 18; const typeId = setInterval(() => { idx += 1; setTyped(full.slice(0, idx)); if (idx >= full.length) clearInterval(typeId); }, SPEED); timerRef.current = setTimeout(() => setI((p) => (p + 1) % DEMO_STEPS.length), full.length * SPEED + 5000); return () => { clearInterval(typeId); clearTimeout(timerRef.current); }; }, [open, paused, i]); if (!open) return null; const step = DEMO_STEPS[i]; const go = (n) => { setI((n + DEMO_STEPS.length) % DEMO_STEPS.length); setPaused(true); }; const modal = (
{ if (e.target === e.currentTarget) setOpen(false); }} style={{ position: 'fixed', inset: 0, zIndex: 9999, background: 'rgba(8,8,9,0.86)', backdropFilter: 'blur(10px)', WebkitBackdropFilter: 'blur(10px)', display: 'flex', alignItems: m ? 'flex-start' : 'center', justifyContent: 'center', overflowY: m ? 'auto' : 'visible', fontFamily: DA.font, padding: m ? '60px 12px 28px' : 24, }}> {/* close */}
{/* phone */}
{step.render()}
{/* caption + controls */}
THE 90-SECOND TOUR · {String(i + 1).padStart(2, '0')} / {String(DEMO_STEPS.length).padStart(2, '0')}
{step.label}

{step.title}

{typed}

{/* progress segments */}
{DEMO_STEPS.map((s, idx) => (
); return ReactDOM.createPortal(modal, document.body); } function demoNavBtn(DA) { return { padding: '10px 20px', borderRadius: 6, cursor: 'pointer', background: 'transparent', border: '1px solid rgba(255,255,255,0.22)', color: '#fff', fontSize: 14, fontWeight: 700, fontFamily: DA.font, }; } window.DemoModal = DemoModal;