const { useState, useEffect, useRef } = React; // ─── API ──────────────────────────────────────────────────────────────────── const API = { token: localStorage.getItem('fayztoo.jwt') || '', name: localStorage.getItem('fayztoo.name') || '', setAuth(t, n) { this.token=t; this.name=n; localStorage.setItem('fayztoo.jwt',t); localStorage.setItem('fayztoo.name',n); }, clear() { this.token=''; this.name=''; localStorage.removeItem('fayztoo.jwt'); localStorage.removeItem('fayztoo.name'); }, async req(path, opts={}) { const h={'Content-Type':'application/json',...(opts.headers||{})}; if(this.token) h['Authorization']=`Bearer ${this.token}`; const r=await fetch(path,{...opts,headers:h}); if(!r.ok){ let err=`HTTP ${r.status}`; try{const j=await r.json();err=j.detail||err;}catch{} throw new Error(err); } return r.json(); } }; const md = t => !t ? '' : t.replace(/\*\*(.+?)\*\*/g,'$1').replace(/(^|[^*])\*([^*]+?)\*/g,'$1$2'); const initials = n => (n||'').split(/\s+/).filter(Boolean).slice(0,2).map(s=>s[0]).join('').toUpperCase() || 'F'; const fmtDate = s => { if(!s) return ''; try{ return new Date(s).toLocaleDateString('en-GB',{day:'numeric',month:'short',year:'numeric'}); } catch{ return s; } }; const fmtTime = s => { if(!s) return ''; try{ return new Date(s).toLocaleTimeString('en-GB',{hour:'2-digit',minute:'2-digit'}); } catch{ return s; } }; // ─── Login ────────────────────────────────────────────────────────────────── function Login({ onLoggedIn, synthetics }) { const [name,setName]=useState(''); const [email,setEmail]=useState(''); const [agreed,setAgreed]=useState(false); const [err,setErr]=useState(''); const [busy,setBusy]=useState(false); async function submit(e) { e.preventDefault(); setErr(''); setBusy(true); try { const r=await API.req('/api/session/start',{method:'POST',body:JSON.stringify({name,email})}); API.setAuth(r.token,r.name); onLoggedIn(r); } catch(e){ setErr(e.message); } finally { setBusy(false); } } async function loadSynthetic(key) { if(!agreed){ setErr('Please tick the acknowledgement first.'); return; } setBusy(true); setErr(''); try { const r=await API.req(`/api/synthetic_members/${key}/load`,{method:'POST'}); API.setAuth(r.token,r.name); onLoggedIn(r); } catch(e){ setErr(e.message); } finally { setBusy(false); } } return (
f
Fayztoo Wellbeing Companion

Your Wellbeing Compass for the second half of life

A premium, private wellbeing companion. Begin with the Wellbeing Compass across four dimensions — Purpose, Physical, Mental, Connections. Continue with the AI Wellbeing Companion grounded in a knowledge library written by qualified domain experts. Always on, always remembering, always private.

About this service
  • Fayztoo is a reflective wellbeing companion, not therapy and not medical advice.
  • It does not diagnose, prescribe, or replace a qualified clinician.
  • For any medical concern, please speak to your GP.
  • If you are in distress: Samaritans 116 123 (UK, free, 24/7). If life is at risk: 999.
  • Your conversations are private. Data is never sold or used to train any foundation model.
setName(e.target.value)} required /> setEmail(e.target.value)} required /> {err &&
{err}
}
{synthetics && synthetics.length > 0 && (
Or step into a seeded member journey
{synthetics.map(m => (
loadSynthetic(m.key)}>
{m.archetype || 'demo journey'}
{m.name}
{m.role}
))}

Each shows the Wellbeing Companion responding to a different pattern. Tick the acknowledgement first.

)}
); } // ─── Sidebar ──────────────────────────────────────────────────────────────── function Sidebar({ view, setView, hasResults, archetype }) { const nav = [ { id: 'dashboard', label: 'Dashboard', icon: '◆' }, { id: 'baseline', label: 'Baseline assessment', icon: 'B' }, ...(hasResults ? [{ id: 'baseline-results', label: 'Your results', icon: '▣' }] : []), { id: 'chat', label: 'AI Wellbeing Companion', icon: '◉' }, { id: 'timeline', label: 'Memory timeline', icon: '◈' }, { id: 'privacy', label: 'Privacy settings', icon: '🔒' }, { id: 'admin', label: 'Library & escalations', icon: '☰' }, ]; return ( ); } // ─── Dashboard ────────────────────────────────────────────────────────────── function Dashboard({ profile, setView }) { const baseline = profile?.assessments?.find(a=>a.kind==='baseline'); const pi = profile?.assessments?.find(a=>a.kind==='pi'); return (

Welcome{profile?.name ? `, ${profile.name.split(' ')[0]}` : ''}.

Your private space for the second half of life. Begin where you are.

Total Wellbeing Assessment

Five minutes, four pillars. Your baseline starts here.

Purpose & Identity, deeper

About 15–20 minutes. Goes beyond the surface to your phase, pattern, and pathway.

Open the Wellbeing Companion

Reflective conversation, grounded in a knowledge library written by qualified domain experts. Held within the six Constitutional Safety rules. Always private.

); } // ─── Wellbeing Compass survey ──────────────────────────────────────────────── function WellbeingCompass({ onDone }) { const [compass,setCompass]=useState(null); const [sectionIdx,setSectionIdx]=useState(0); const [answers,setAnswers]=useState({}); const [busy,setBusy]=useState(false); const [err,setErr]=useState(''); useEffect(()=>{ API.req('/api/assessment/baseline').then(d=>setCompass(d.compass)).catch(e=>setErr(e.message)); },[]); if(!compass) return
{err||'Loading…'}
; const sections = compass.sections; const closing = compass.closing; const isClosing = sectionIdx === sections.length; const section = !isClosing ? sections[sectionIdx] : null; const allScoreableIds = sections.flatMap(s => s.items.filter(i=>i.type==='likert').map(i=>i.id)); const answeredCount = allScoreableIds.filter(id=>answers[id]).length; const progress = allScoreableIds.length > 0 ? (answeredCount / allScoreableIds.length) * 100 : 0; function setAnswer(id, val) { setAnswers(prev=>({...prev,[id]:val})); } function toggleCheckbox(id, opt) { const cur = answers[id] || []; const next = cur.includes(opt) ? cur.filter(o=>o!==opt) : [...cur, opt]; setAnswers(prev=>({...prev,[id]:next})); } function sectionComplete() { if(isClosing) return true; return section.items.every(item => { if(item.type==='likert') return answers[item.id] !== undefined; if(item.type==='reflection') return (answers[item.id]||'').trim().length > 0; return true; }); } async function submit() { setBusy(true); setErr(''); try { const r=await API.req('/api/assessment/baseline',{method:'POST',body:JSON.stringify({answers})}); onDone(r); } catch(e){ setErr(e.message); } finally { setBusy(false); } } function renderItem(item) { const score = answers[item.id]; const showFollowup = item.followup && score !== undefined && score <= 3; return (
{item.type==='reflection' && ( <> {item.prompt} {item.hint && {item.hint}}