// Verticals hub — tabbed domain selector const DOMAIN_ORDER = ['legal', 'realestate', 'accounting', 'assistant', 'restauration', 'transport', 'security', 'forensics']; function VerticalsHub({ t }) { const [active, setActive] = useState('legal'); const d = t.domains[active]; return ( {/* Domain selector — horizontal scroll pills */} {/* Fade edges */} {DOMAIN_ORDER.map(k => { const dom = t.domains[k]; const on = active === k; return ( { setActive(k); e.currentTarget.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' }); }} style={{ appearance: 'none', whiteSpace: 'nowrap', flexShrink: 0, border: '1px solid ' + (on ? 'var(--brand)' : 'var(--border)'), background: on ? 'var(--brand)' : 'var(--bg-elev)', color: on ? '#fff' : 'var(--ink)', padding: '10px 20px', borderRadius: 999, cursor: 'pointer', transition: 'all 200ms cubic-bezier(0.16, 1, 0.3, 1)', display: 'flex', alignItems: 'center', gap: 8, boxShadow: on ? '0 4px 16px color-mix(in oklch, var(--brand) 35%, transparent)' : '0 1px 3px rgba(0,0,0,0.06)', }} onMouseEnter={e => { if (!on) { e.currentTarget.style.borderColor = 'var(--brand)'; e.currentTarget.style.background = 'color-mix(in oklch, var(--brand) 6%, var(--bg-elev))'; } }} onMouseLeave={e => { if (!on) { e.currentTarget.style.borderColor = 'var(--border)'; e.currentTarget.style.background = 'var(--bg-elev)'; } }} > {dom.label} ); })} {/* Content panel */} {d.label} {d.title} {d.desc} « {d.quote} » {/* Features grid */} {d.features.map((f, i) => ( ))} {/* Compliance bar */} {t.lang === 'fr' ? 'Conformité' : 'Compliance'} {d.compliance} }>{t.cta_demo_v} ); } // Per-domain visual — right column of the active panel function DomainVisual({ kind, t }) { if (kind === 'legal') return ; if (kind === 'realestate') return ; if (kind === 'accounting') return ; if (kind === 'assistant') return ; if (kind === 'restauration') return ; if (kind === 'transport') return ; if (kind === 'security') return ; if (kind === 'forensics') return ; return null; } // --- Domain visuals --------------------------------------------------------- function VisualFrame({ children, label }) { return ( {label} {children} ); } function AccountingVisual({ t }) { const rows = t.lang === 'fr' ? [ ['Loyer reçu · Unité 204', '+1 450,00', 'Revenus location', 'classé'], ['Rona · matériaux', '−328,47', 'Entretien immeuble', 'classé'], ['Facture Hydro · 2340 St-Denis', '−612,30', 'Services publics', 'à valider'], ['Dépôt Desjardins', '+3 400,00', 'Revenus location', 'classé'], ['Repas client · Europea', '−147,82', 'Représentation (50%)', 'classé'], ] : [ ['Rent received · Unit 204', '+1,450.00', 'Rental income', 'posted'], ['Rona · materials', '−328.47', 'Building maintenance', 'posted'], ['Hydro bill · 2340 St-Denis', '−612.30', 'Utilities', 'review'], ['Desjardins deposit', '+3,400.00', 'Rental income', 'posted'], ['Client meal · Europea', '−147.82', 'Meals (50%)', 'posted'], ]; return ( {rows.map((r, i) => ( {r[0]} {r[1]} {r[2]} {r[3]} ))} {t.lang === 'fr' ? '142 transactions · 3 à valider' : '142 transactions · 3 to review'} {t.lang === 'fr' ? 'TPS/TVQ prêt' : 'GST/QST ready'} ); } function AssistantVisual({ t }) { const briefing = t.lang === 'fr' ? { label: 'briefing · mardi 14 avril', weather: 'Montréal · 7°C · ciel dégagé', items: [ { time: '09:00', title: 'Revue stratégie Q2', who: 'Salle Rosemont · 45 min', status: 'confirmé' }, { time: '11:30', title: 'Appel J.-F. Bouchard', who: 'relance 3e · priorité', status: 'à préparer' }, { time: '12:30', title: 'Lunch · Europea', who: 'rés. pour 3 · préféré 2023', status: 'réservé' }, { time: '15:00', title: 'Signature bail commercial', who: 'Me Tremblay · Zoom', status: 'docs prêts' }, ], followups: '3 suivis aujourd’hui · 1 urgent', } : { label: 'briefing · tuesday, april 14', weather: 'Montréal · 7°C · clear', items: [ { time: '09:00', title: 'Q2 strategy review', who: 'Rosemont room · 45 min', status: 'confirmed' }, { time: '11:30', title: 'Call J.-F. Bouchard', who: '3rd follow-up · priority', status: 'prep needed' }, { time: '12:30', title: 'Lunch · Europea', who: 'table for 3 · favorite 2023', status: 'booked' }, { time: '15:00', title: 'Sign commercial lease', who: 'Me Tremblay · Zoom', status: 'docs ready' }, ], followups: '3 follow-ups today · 1 urgent', }; return ( {briefing.weather} {briefing.items.map((it, i) => ( {it.time} {it.title} {it.who} {it.status} ))} {briefing.followups} ); } function RestoVisual({ t }) { const fr = t.lang === 'fr'; const bookings = fr ? [ { t: '18:30', p: 2, n: 'Leblanc', tag: 'régulier' }, { t: '19:00', p: 4, n: 'Gagnon', tag: 'anniv.' }, { t: '19:15', p: 6, n: 'Desjardins corp.', tag: 'VIP' }, { t: '19:45', p: 2, n: 'Tremblay', tag: 'nouveau' }, { t: '20:00', p: 3, n: 'Martin', tag: 'rappelé' }, ] : [ { t: '18:30', p: 2, n: 'Leblanc', tag: 'regular' }, { t: '19:00', p: 4, n: 'Gagnon', tag: 'birthday' }, { t: '19:15', p: 6, n: 'Desjardins corp.', tag: 'VIP' }, { t: '19:45', p: 2, n: 'Tremblay', tag: 'new' }, { t: '20:00', p: 3, n: 'Martin', tag: 'confirmed' }, ]; return ( {bookings.map((b, i) => ( {b.t} {b.p} {b.n} {b.tag} ))} {fr ? '42 couverts · 5 relances envoyées' : '42 covers · 5 reminders sent'} {fr ? '2 no-show rebookés' : '2 no-shows rebooked'} ); } function TransportVisual({ t }) { const fr = t.lang === 'fr'; const trips = fr ? [ { id: 'TR-1182', from: 'Laval', to: 'Québec', truck: 'T-07', status: 'en route', eta: '14:42' }, { id: 'TR-1183', from: 'Montréal', to: 'Sherbrooke', truck: 'T-03', status: 'chargement', eta: '—' }, { id: 'TR-1184', from: 'Longueuil', to: 'Trois-Rivières', truck: 'T-11', status: 'assigné', eta: '15:10' }, { id: 'TR-1185', from: 'Mirabel', to: 'Gatineau', truck: 'T-05', status: 'livré', eta: '13:58' }, ] : [ { id: 'TR-1182', from: 'Laval', to: 'Québec', truck: 'T-07', status: 'en route', eta: '2:42 pm' }, { id: 'TR-1183', from: 'Montréal', to: 'Sherbrooke', truck: 'T-03', status: 'loading', eta: '—' }, { id: 'TR-1184', from: 'Longueuil', to: 'Trois-Rivières', truck: 'T-11', status: 'assigned', eta: '3:10 pm' }, { id: 'TR-1185', from: 'Mirabel', to: 'Gatineau', truck: 'T-05', status: 'delivered', eta: '1:58 pm' }, ]; return ( {trips.map((tr, i) => ( {tr.id} {tr.from} → {tr.to} {tr.truck} {tr.status} {tr.eta} ))} {fr ? '12 camions actifs · 4 en attente' : '12 trucks active · 4 idle'} {fr ? 'SAAQ · à jour' : 'SAAQ · current'} ); } function SecurityVisual({ t }) { const fr = t.lang === 'fr'; const events = fr ? [ { time: '14:02:03', type: 'scan', desc: 'Scan adversariel lancé · modèle GPT-4o', status: 'en cours' }, { time: '14:02:11', type: 'alert', desc: 'Injection de prompt détectée · bloquée', status: 'bloqué' }, { time: '14:02:18', type: 'scan', desc: 'Test jailbreak #47 · résistance confirmée', status: 'ok' }, { time: '14:02:24', type: 'alert', desc: 'Fuite de données PII · garde-fou actif', status: 'bloqué' }, { time: '14:02:31', type: 'scan', desc: 'Audit guardrails terminé · 12/12 passés', status: 'ok' }, ] : [ { time: '2:02:03pm', type: 'scan', desc: 'Adversarial scan started · GPT-4o model', status: 'running' }, { time: '2:02:11pm', type: 'alert', desc: 'Prompt injection detected · blocked', status: 'blocked' }, { time: '2:02:18pm', type: 'scan', desc: 'Jailbreak test #47 · resistance confirmed', status: 'ok' }, { time: '2:02:24pm', type: 'alert', desc: 'PII data leak attempt · guardrail active', status: 'blocked' }, { time: '2:02:31pm', type: 'scan', desc: 'Guardrail audit complete · 12/12 passed', status: 'ok' }, ]; return ( {events.map((ev, i) => ( {ev.time} {ev.desc} {ev.status} ))} {fr ? '47 tests · 2 menaces bloquées' : '47 tests · 2 threats blocked'} {fr ? 'score: 96/100' : 'score: 96/100'} ); } function ForensicsVisual({ t }) { const fr = t.lang === 'fr'; const items = fr ? [ { id: 'DOC-0014', type: 'Courriel', source: 'Exchange', entities: 12, status: 'analysé' }, { id: 'DOC-0015', type: 'PDF contrat', source: 'SharePoint', entities: 34, status: 'analysé' }, { id: 'DOC-0016', type: 'Relevé bancaire', source: 'OSINT', entities: 8, status: 'en cours' }, { id: 'DOC-0017', type: 'Image EXIF', source: 'Forensique', entities: 3, status: 'analysé' }, { id: 'DOC-0018', type: 'Chat Slack', source: 'Export', entities: 21, status: 'en file' }, ] : [ { id: 'DOC-0014', type: 'Email', source: 'Exchange', entities: 12, status: 'analyzed' }, { id: 'DOC-0015', type: 'Contract PDF', source: 'SharePoint', entities: 34, status: 'analyzed' }, { id: 'DOC-0016', type: 'Bank statement', source: 'OSINT', entities: 8, status: 'running' }, { id: 'DOC-0017', type: 'Image EXIF', source: 'Forensic', entities: 3, status: 'analyzed' }, { id: 'DOC-0018', type: 'Slack chat', source: 'Export', entities: 21, status: 'queued' }, ]; return ( {items.map((it, i) => ( {it.id} {it.type} {it.source} {it.entities} {fr ? 'entités' : 'entities'} {it.status} ))} {fr ? '1 204 documents · 78 entités extraites' : '1,204 documents · 78 entities extracted'} {fr ? 'custody: vérifié' : 'custody: verified'} ); } function FeatureBlock({ tag, title, desc, first }) { const [hov, setHov] = useState(false); return ( setHov(true)} onMouseLeave={() => setHov(false)} style={{ padding: '30px 26px', borderRight: '1px solid var(--border)', borderBottom: '1px solid var(--border)', background: hov ? 'color-mix(in oklch, var(--brand) 2.5%, var(--bg-elev))' : 'var(--bg-elev)', transition: 'background 150ms', }}> {tag} {title} {desc} ); } function CompareTable({ t }) { return ( {t.compare_them} CASTOR {t.compare_rows.map((row, i) => ( {row[0]} {row[1]} ))} ); } function ConciergeMockup({ t }) { return ( Concierge Castor BON DE TRAVAIL · #4821 Fuite · Unité 304 Plombier Tremblay · demain 9h ); } function PhoneFrame({ children }) { return ( {children} ); } function CTA({ t }) { return ( }>{t.cta_pill} {t.cta_title_1} {t.cta_title_2} {t.cta_sub} }>{t.cta_btn} {t.cta_demo} {t.cta_pills.map((p, i) => ( {p} ))} ); } function Footer({ t }) { return ( ); } Object.assign(window, { VerticalsHub, CTA, Footer, PhoneFrame, FeatureBlock, CompareTable, ConciergeMockup, DomainVisual, AccountingVisual, AssistantVisual, RestoVisual, TransportVisual, SecurityVisual, ForensicsVisual });
{d.desc}
{t.cta_sub}