/* BioReveal — Main funnel app. (v0.9 — body removed, vitals hidden, voice transcript) * * Flow: Goals → Full BioScan (vitals+skin+voice+transcript) → Feel/Training/Vices * → Basics → Medical → Email → AI Reveal */ const { StepIntro, StepGoals, StepBioscan, StepFeel, StepTraining, StepVices, StepBasics, StepMedical, StepEmail, } = window.BR_STEPS; function App() { const [stage, setStage] = React.useState("intro"); const [step, setStep] = React.useState(0); const [aiStatus, setAiStatus] = React.useState({ checked: false, mode: "rules" }); const [state, setState] = React.useState({ bioScan: null, goals: [], feel: {}, training: null, vices: [], basics: { age: 32, sex: null }, medical: {}, email: "", }); const [profile, setProfile] = React.useState(null); const [synthError, setSynthError] = React.useState(null); React.useEffect(() => { fetch("/api/health").then(r => r.json()).then(d => setAiStatus({ checked: true, mode: d.auth_mode || "rules" })).catch(() => setAiStatus({ checked: true, mode: "rules" })); }, []); const update = (key, val) => setState(s => ({ ...s, [key]: val })); const stepDef = [ { key: "goals", render: () => update("goals", v)} />, ready: () => state.goals.length > 0 }, { key: "bioscan", render: () => update("bioScan", v)} />, ready: () => true }, { key: "feel", render: () => update("feel", v)} />, ready: () => true }, { key: "training", render: () => update("training", v)} />, ready: () => !!state.training }, { key: "vices", render: () => update("vices", v)} />, ready: () => true }, { key: "basics", render: () => update("basics", v)} />, ready: () => !!state.basics?.sex }, { key: "medical", render: () => update("medical", v)} />, ready: () => state.medical?.pregnant !== null && state.medical?.pregnant !== undefined }, { key: "email", render: () => update("email", v)} />, ready: () => /\S+@\S+\.\S+/.test(state.email || "") }, ]; const cur = stepDef[step]; const totalSteps = stepDef.length; const progress = Math.round(((step + 1) / totalSteps) * 100); const next = () => { if (step >= totalSteps - 1) { setStage("analysis"); } else setStep(step + 1); }; const back = () => { if (step === 0) setStage("intro"); else setStep(step - 1); }; const startSynthesis = async () => { setSynthError(null); const intake = { goals: state.goals, feel: state.feel, training: state.training, vices: state.vices, basics: state.basics, medical: state.medical, vitals: state.bioScan?.vitals || {}, skin: state.bioScan?.skin || {}, voice: state.bioScan?.voice || {}, wearable: {}, email: state.email, }; try { const res = await fetch("/api/synthesize", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(intake), }); const data = await res.json(); if (data.error) { setSynthError(data.message || data.error); return; } setProfile({ protocol: data, intake }); setStage("reveal"); window.scrollTo(0, 0); } catch (e) { setSynthError(e.message); } }; const AIBanner = () => ( aiStatus.checked && aiStatus.mode === "rules" && (
⚙ AI MODE: RULES ENGINE · Set ANTHROPIC_API_KEY to enable Claude Opus 4.7 reasoning.
) ); if (stage === "reveal" && profile) { return { setStage("intro"); setStep(0); setProfile(null); window.scrollTo(0, 0); }} />; } return (
{stage === "intro" && { setStage("quiz"); setStep(0); window.scrollTo(0, 0); }}/>} {stage === "quiz" && (
BIOREVEAL · LIVE STEP {step + 1} / {totalSteps}
{cur.render()}
)} {stage === "analysis" && (
)}
); } ReactDOM.createRoot(document.getElementById("root")).render();