// 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')}