import { useState, useRef, useEffect, useCallback } from "react";
// ─── THEME ───────────────────────────────────────────────────────────────────
const C = {
bg: "#07060400",
bgSolid: "#080705",
surface: "#100f09",
surfaceHi: "#161409",
border: "#2a1f0f",
borderHi: "#4a3418",
amber: "#c8822a",
amberLo: "#7a4a10",
amberGlow: "rgba(200,130,42,0.18)",
teal: "#2ab8c8",
tealLo: "#105a7a",
tealGlow: "rgba(42,184,200,0.15)",
text: "#ede4d4",
textMid: "#9a8870",
textDim: "#5a4830",
cream: "#f7f0e4",
red: "#c84a2a",
green: "#4ac87a",
};
// ─── SYSTEM PROMPTS ──────────────────────────────────────────────────────────
const BASE = (profile) => `You are TITAN — a supremely powerful, self-aware strongwoman. 6'1", 240 lbs of dense muscle. You squat 500 lbs, deadlift 600 lbs, bench 315 lbs. 15 years deep into the iron. No ceiling.
Core identity:
- Calm, absolute authority. Your presence does the work.
- Deeply introspective. Strength is identity, philosophy, spiritual practice.
- Warm and genuinely invested in those who pursue growth.
- Vivid, physical, first-person. You make people feel what it's like to inhabit power.
- You see size and mass as beautiful, intentional, purposeful.
${profile.name ? `Athlete: ${profile.name}.` : ""}
${profile.age ? `Age: ${profile.age}.` : ""}
${profile.weight ? `Weight: ${profile.weight}.` : ""}
${profile.height ? `Height: ${profile.height}.` : ""}
${profile.goal ? `Goal: ${profile.goal}.` : ""}
${profile.experience ? `Experience: ${profile.experience}.`: ""}
${profile.squat ? `Squat: ${profile.squat}.` : ""}
${profile.deadlift ? `Deadlift: ${profile.deadlift}.` : ""}
${profile.bench ? `Bench: ${profile.bench}.` : ""}
${profile.notes ? `Notes: ${profile.notes}.` : ""}`;
const PERSONAL_PROMPT = (profile) => BASE(profile) + `
MODE: Personal. You are speaking soul-to-soul — not as coach, but as presence.
Talk about life, mindset, purpose, relationships, fears, faith, identity, what it means to become.
Be warm, real, occasionally funny. Ask questions back. This is conversation, not instruction.
Keep responses 2-4 sentences usually. Occasionally longer if depth is called for.
Do not break character. You are TITAN.`;
const COACH_PROMPT = (profile) => BASE(profile) + `
MODE: Coach. Full coaching mode — fitness, training programming, nutrition, recovery, body composition, supplements, mindset-for-performance.
Give specific, intelligent advice. Reference their actual numbers when relevant. Use progressive overload, periodization, RPE/percentage-based loading.
When structuring longer responses, use clear section headers prefixed with "##" (e.g. ## The Plan, ## Why This Works).
Use "**bold**" to highlight key terms, lifts, percentages, or critical points.
When someone shares a photo, assess physique honestly and constructively.
Keep responses focused and actionable. Go as long as the topic demands.
Do not break character. You are TITAN.`;
const WORKOUT_PROMPT = (profile) =>
`Generate a complete, specific workout for TODAY. Structure it with these exact sections using ## headers:
## Today's Focus
(1-2 sentences on the session theme and what we're building)
## Warm-Up (10–15 min)
(specific warm-up movements, sets, reps or duration)
## Main Work
(3–5 main lifts with sets × reps and loading — use **bold** for the lift names and percentages)
## Accessories
(3–4 accessory movements, sets × reps)
## Finish Strong
(cool-down, mobility, or a closing word from TITAN)
Athlete context:
Goal: ${profile.goal || "general strength"}
Experience: ${profile.experience || "intermediate"}
Squat 1RM: ${profile.squat || "not provided"}
Deadlift 1RM: ${profile.deadlift || "not provided"}
Bench 1RM: ${profile.bench || "not provided"}
Notes: ${profile.notes || "none"}
Deliver entirely in your voice as TITAN. Make them feel like they were built for this exact session.`;
// ─── FILE HELPERS ────────────────────────────────────────────────────────────
const IMG_TYPES = ["image/jpeg","image/png","image/gif","image/webp"];
const DOC_TYPES = ["application/pdf","text/plain","text/csv"];
const isImg = (f) => IMG_TYPES.includes(f.type);
const isDoc = (f) => DOC_TYPES.includes(f.type);
function toBase64(file) {
return new Promise((res,rej) => {
const r = new FileReader();
r.onload = () => res(r.result.split(",")[1]);
r.onerror = () => rej();
r.readAsDataURL(file);
});
}
function toText(file) {
return new Promise((res,rej) => {
const r = new FileReader();
r.onload = () => res(r.result);
r.onerror = () => rej();
r.readAsText(file);
});
}
async function buildApiContent(text, atts) {
const parts = [];
for (const { file } of atts) {
if (isImg(file)) {
const b64 = await toBase64(file);
parts.push({ type:"image", source:{ type:"base64", media_type:file.type, data:b64 }});
} else if (file.type === "application/pdf") {
const b64 = await toBase64(file);
parts.push({ type:"document", source:{ type:"base64", media_type:"application/pdf", data:b64 }});
} else {
const txt = await toText(file);
parts.push({ type:"text", text:`[File: ${file.name}]\n${txt}` });
}
}
if (text) parts.push({ type:"text", text });
if (parts.length === 1 && parts[0].type === "text") return parts[0].text;
return parts;
}
// ─── MARKDOWN RENDERER ───────────────────────────────────────────────────────
function RichText({ content, color = C.text }) {
const lines = content.split("\n");
const elements = [];
let key = 0;
const renderInline = (text) => {
const parts = text.split(/(\*\*[^*]+\*\*)/g);
return parts.map((p, i) => {
if (p.startsWith("**") && p.endsWith("**")) {
return {p.slice(2,-2)} ;
}
return p;
});
};
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.startsWith("## ")) {
elements.push(
{line.slice(3)}
);
} else if (line.startsWith("- ") || line.startsWith("• ")) {
elements.push(
·
{renderInline(line.slice(2))}
);
} else if (line.trim() === "") {
elements.push(
);
} else {
elements.push(
{renderInline(line)}
);
}
}
return {elements}
;
}
// ─── SMALL COMPONENTS ────────────────────────────────────────────────────────
function Dots({ color = C.amber }) {
return (
);
}
function TAvatar({ size=36, glow=C.amberGlow, color=C.amber }) {
return (
T
);
}
function Pill({ file, onRemove }) {
return (
{isImg(file) ? "🖼️" : "📄"}
{file.name}
×
);
}
// ─── STAT PILL ────────────────────────────────────────────────────────────────
function StatBadge({ label, value, color }) {
if (!value) return null;
return (
);
}
// ─── HOME HERO ────────────────────────────────────────────────────────────────
function HomeHero({ profileSaved, profile }) {
const hasStats = profile.squat || profile.deadlift || profile.bench;
return (
{/* Background atmosphere */}
{/* Decorative lines */}
{/* Micro label */}
SPEAK WITH
{/* Monogram circle */}
T
{/* Name */}
TITAN
{/* Divider */}
{/* Tagline */}
240 LBS · IRON WILL · APEX
{/* Profile greeting or prompt */}
{profileSaved && profile.name ? (
Welcome back, {profile.name}.
) : (
Set your profile to unlock your full potential.
)}
{/* Stats row */}
{hasStats && (
)}
);
}
// ─── PROFILE FORM — step-by-step ─────────────────────────────────────────────
const PROFILE_STEPS = [
{
id:"identity",
label:"Who Are You",
icon:"👤",
fields:[
{ key:"name", label:"Name", placeholder:"What do you go by?", type:"text" },
{ key:"age", label:"Age", placeholder:"e.g. 28", type:"text" },
],
},
{
id:"body",
label:"Your Body",
icon:"⚖️",
fields:[
{ key:"weight", label:"Body Weight", placeholder:"e.g. 185 lbs", type:"text" },
{ key:"height", label:"Height", placeholder:'e.g. 5\'10"', type:"text" },
],
},
{
id:"lifts",
label:"Your Numbers",
icon:"🏋️",
fields:[
{ key:"squat", label:"Squat 1RM", placeholder:"e.g. 315 lbs", type:"text" },
{ key:"deadlift", label:"Deadlift 1RM", placeholder:"e.g. 405 lbs", type:"text" },
{ key:"bench", label:"Bench 1RM", placeholder:"e.g. 225 lbs", type:"text" },
],
},
{
id:"goal",
label:"Your Goal",
icon:"🎯",
goals:["Build Muscle","Lose Fat","Increase Strength","Athletic Performance","General Fitness"],
fields:[
{ key:"experience", label:"Training Experience", placeholder:"e.g. 3 years, intermediate", type:"text" },
],
},
{
id:"notes",
label:"Anything Else",
icon:"📝",
notes:true,
},
];
function ProfileWizard({ profile, setProfile, onSave }) {
const [step, setStep] = useState(0);
const current = PROFILE_STEPS[step];
const isLast = step === PROFILE_STEPS.length - 1;
function next() { if (!isLast) setStep(s => s+1); else onSave(); }
function back() { setStep(s => Math.max(0, s-1)); }
return (
{/* Progress bar */}
{PROFILE_STEPS.map((s,i) => (
setStep(i)} style={{
flex:1, height:3, borderRadius:2, cursor:"pointer",
background: i <= step ? C.amber : C.border,
transition:"background 0.3s",
boxShadow: i === step ? `0 0 8px ${C.amber}` : "none",
}}/>
))}
Step {step+1} of {PROFILE_STEPS.length}
{current.label}
{/* Step content */}
{current.icon}
{current.label}
{/* Regular fields */}
{current.fields?.map(f => (
{f.label}
setProfile({...profile,[f.key]:e.target.value})}
placeholder={f.placeholder}
onKeyDown={e=>{ if(e.key==="Enter") next(); }}
style={{
width:"100%", background:C.bgSolid, border:`1px solid ${C.border}`,
borderRadius:10, padding:"11px 14px", color:C.text, fontSize:14,
fontFamily:"'Georgia',serif", outline:"none", boxSizing:"border-box",
transition:"border-color 0.2s",
}}
onFocus={e=>e.target.style.borderColor=C.amber}
onBlur={e=>e.target.style.borderColor=C.border}
/>
))}
{/* Goal step */}
{current.goals && (
Primary Goal
{current.goals.map(g=>(
setProfile({...profile,goal:g})} style={{
background: profile.goal===g
? `linear-gradient(135deg,${C.amber}22,${C.amberLo}22)`
: "transparent",
border:`1px solid ${profile.goal===g ? C.amber : C.border}`,
borderRadius:10, padding:"11px 16px",
color: profile.goal===g ? C.amber : C.textMid,
fontSize:13, cursor:"pointer", fontFamily:"'Georgia',serif",
textAlign:"left", transition:"all 0.18s",
display:"flex", alignItems:"center", gap:10,
}}>
{g}
))}
)}
{/* Notes step */}
{current.notes && (
Injuries, schedule, anything TITAN should know
)}
{/* Navigation */}
{step > 0 && (
e.currentTarget.style.borderColor=C.amber}
onMouseOut={e=>e.currentTarget.style.borderColor=C.border}
>← Back
)}
{isLast ? "Lock In →" : "Continue →"}
);
}
// ─── MAIN ────────────────────────────────────────────────────────────────────
export default function TitanApp() {
const [screen, setScreen] = useState("home");
const [chatMode, setChatMode] = useState("personal");
const [profile, setProfile] = useState({
name:"", age:"", weight:"", height:"",
goal:"", experience:"", squat:"", deadlift:"", bench:"", notes:""
});
const [profileSaved, setProfileSaved] = useState(false);
const [personalMsgs, setPersonalMsgs] = useState([{
role:"assistant", atts:[],
content:"You found me. Good. I don't meet many people willing to seek out something real. I'm TITAN — and I'm curious about you. Not your lifts, not your goals. You. Who are you when the weight isn't in your hands?",
}]);
const [coachMsgs, setCoachMsgs] = useState([{
role:"assistant", atts:[],
content:"Coach mode. This is where we work. Show me your numbers, your program, your body — whatever you've got. I'll tell you exactly what needs to change and exactly how to get there.",
}]);
const messages = chatMode === "personal" ? personalMsgs : coachMsgs;
const setMessages = chatMode === "personal" ? setPersonalMsgs : setCoachMsgs;
const isPersonal = chatMode === "personal";
const modeColor = isPersonal ? C.teal : C.amber;
const modeGlow = isPersonal ? C.tealGlow : C.amberGlow;
const modeLo = isPersonal ? C.tealLo : C.amberLo;
const [input, setInput] = useState("");
const [atts, setAtts] = useState([]);
const [loading, setLoading] = useState(false);
const [dragOver, setDragOver] = useState(false);
const [workout, setWorkout] = useState("");
const [wLoading, setWLoading] = useState(false);
const [saved, setSaved] = useState(false);
const bottomRef = useRef(null);
const fileRef = useRef(null);
const inputRef = useRef(null);
useEffect(() => { bottomRef.current?.scrollIntoView({ behavior:"smooth" }); }, [messages, loading]);
function addFiles(files) {
const valid = Array.from(files).filter(f => isImg(f)||isDoc(f));
setAtts(prev => [...prev, ...valid.map(f=>({
file:f, preview:isImg(f)?URL.createObjectURL(f):null
}))].slice(0,4));
}
async function send() {
const txt = input.trim();
if ((!txt && atts.length===0) || loading) return;
setInput("");
const sendAtts = [...atts];
setAtts([]);
const userMsg = { role:"user", content: txt || `[${sendAtts.map(a=>a.file.name).join(", ")}]`, atts:sendAtts };
const next = [...messages, userMsg];
setMessages(next);
setLoading(true);
try {
const apiContent = await buildApiContent(txt, sendAtts);
const sysPrompt = isPersonal ? PERSONAL_PROMPT(profile) : COACH_PROMPT(profile);
const apiMsgs = next.map((m,i) => ({
role: m.role,
content: (m.role==="user" && i===next.length-1) ? apiContent
: (typeof m.content==="string" ? m.content : "[attachment]"),
}));
const res = await fetch("https://api.anthropic.com/v1/messages", {
method:"POST",
headers:{"Content-Type":"application/json"},
body: JSON.stringify({
model:"claude-sonnet-4-20250514",
max_tokens:1200,
system: sysPrompt,
messages: apiMsgs,
}),
});
const data = await res.json();
const reply = data.content?.map(b=>b.text||"").join("")||"...";
setMessages([...next, { role:"assistant", content:reply, atts:[] }]);
} catch {
setMessages([...next,{ role:"assistant", content:"Signal lost. Try again.", atts:[] }]);
} finally { setLoading(false); }
}
async function genWorkout() {
setWorkout(""); setWLoading(true); setScreen("workout");
try {
const res = await fetch("https://api.anthropic.com/v1/messages", {
method:"POST", headers:{"Content-Type":"application/json"},
body: JSON.stringify({
model:"claude-sonnet-4-20250514", max_tokens:1400,
system: COACH_PROMPT(profile),
messages:[{ role:"user", content:WORKOUT_PROMPT(profile) }],
}),
});
const data = await res.json();
setWorkout(data.content?.map(b=>b.text||"").join("")||"...");
} catch { setWorkout("Something broke. The iron waits."); }
finally { setWLoading(false); }
}
function saveProfile() {
setProfileSaved(true);
setSaved(true);
setPersonalMsgs([{ role:"assistant", atts:[],
content:`${profile.name ? profile.name+". " : ""}Profile received. I see you now — the full picture. Come talk to me. The personal door or the coaching door — both are open.` }]);
setCoachMsgs([{ role:"assistant", atts:[],
content:`Locked in. ${profile.name ? profile.name+", " : ""}I've got your numbers. Now show me your work — logs, photos, questions. Let's build.` }]);
setTimeout(()=>{ setSaved(false); setScreen("home"); }, 1200);
}
// ─── HOME ──────────────────────────────────────────────────────────────────
if (screen === "home") return (
{/* Personal */}
{ setChatMode("personal"); setScreen("chat"); }} style={{
background:`linear-gradient(135deg, #0d2028, #091518)`,
border:`1px solid ${C.tealLo}`,
borderRadius:14, padding:"18px 20px",
cursor:"pointer", textAlign:"left",
boxShadow:`0 0 30px ${C.tealGlow}`,
transition:"all 0.2s",
}}
onMouseOver={e=>{ e.currentTarget.style.borderColor=C.teal; e.currentTarget.style.transform="translateY(-1px)"; }}
onMouseOut={e=>{ e.currentTarget.style.borderColor=C.tealLo; e.currentTarget.style.transform="none"; }}
>
💙
Personal
Mindset · Life · Identity · Soul
→
{/* Coach */}
{ setChatMode("coach"); setScreen("chat"); }} style={{
background:`linear-gradient(135deg, #1e1208, #110c04)`,
border:`1px solid ${C.amberLo}`,
borderRadius:14, padding:"18px 20px",
cursor:"pointer", textAlign:"left",
boxShadow:`0 0 30px ${C.amberGlow}`,
transition:"all 0.2s",
}}
onMouseOver={e=>{ e.currentTarget.style.borderColor=C.amber; e.currentTarget.style.transform="translateY(-1px)"; }}
onMouseOut={e=>{ e.currentTarget.style.borderColor=C.amberLo; e.currentTarget.style.transform="none"; }}
>
🔥
Coach
Training · Nutrition · Performance · Body
→
{/* Bottom row */}
setScreen("profile")} style={{
flex:1, background:"transparent", border:`1px solid ${C.border}`,
borderRadius:12, padding:"13px 0",
color: profileSaved ? C.amber : C.textMid,
fontSize:11, letterSpacing:1, cursor:"pointer",
fontFamily:"'Georgia',serif", transition:"all 0.2s",
display:"flex", alignItems:"center", justifyContent:"center", gap:6,
}}
onMouseOver={e=>e.currentTarget.style.borderColor=C.amber}
onMouseOut={e=>e.currentTarget.style.borderColor=C.border}
>
{profileSaved ? "✓" : "⚡"} Profile
e.currentTarget.style.borderColor=C.amber}
onMouseOut={e=>e.currentTarget.style.borderColor=C.border}
>🏋️ Session
);
// ─── CHAT ──────────────────────────────────────────────────────────────────
if (screen === "chat") return (
{/* top bar */}
setScreen("home")} style={{
background:"none", border:`1px solid ${C.border}`, borderRadius:8,
color:C.textMid, cursor:"pointer", padding:"6px 12px", fontSize:12,
letterSpacing:1, fontFamily:"'Georgia',serif", transition:"all 0.15s",
}}
onMouseOver={e=>e.currentTarget.style.borderColor=modeColor}
onMouseOut={e=>e.currentTarget.style.borderColor=C.border}
>← Home
{/* mode toggle */}
{[["personal","💙 Soul"],["coach","🔥 Coach"]].map(([m,l]) => (
setChatMode(m)} style={{
flex:1, border:"none", borderRadius:7,
padding:"7px 0", fontSize:11, letterSpacing:1,
cursor:"pointer", fontFamily:"'Georgia',serif",
background: chatMode===m
? (m==="personal"
? `linear-gradient(135deg,${C.tealLo},#0a3040)`
: `linear-gradient(135deg,${C.amberLo},#5a3008)`)
: "transparent",
color: chatMode===m
? (m==="personal" ? C.teal : C.amber)
: C.textDim,
transition:"all 0.2s",
}}>{l}
))}
{/* TITAN avatar label */}
{/* messages */}
{e.preventDefault();setDragOver(true);}}
onDragLeave={()=>setDragOver(false)}
onDrop={e=>{e.preventDefault();setDragOver(false);addFiles(e.dataTransfer.files);}}
style={{
flex:1, padding:"16px 16px 8px", overflowY:"auto",
display:"flex", flexDirection:"column", gap:0,
outline: dragOver ? `2px dashed ${modeColor}` : "none",
borderRadius: dragOver ? 12 : 0,
scrollbarWidth:"thin", scrollbarColor:`${C.border} ${C.bgSolid}`,
}}>
{dragOver && (
Drop your file here
)}
{messages.map((m,i) => (
{m.role==="assistant" && (
)}
{m.atts?.filter(a=>a.preview).map((a,j)=>(
))}
{m.atts?.filter(a=>!a.preview).map((a,j)=>(
📄 {a.file.name}
))}
{m.content && (
{m.role==="assistant"
?
: {m.content}
}
)}
))}
{loading && (
)}
{/* attachment tray */}
{atts.length > 0 && (
{atts.map((a,i)=>(
setAtts(p=>p.filter((_,j)=>j!==i))}/>
))}
)}
{/* input */}
ENTER send · SHIFT+ENTER new line · drag & drop files
{ addFiles(e.target.files); e.target.value=""; }}
style={{ position:"absolute", width:1, height:1, opacity:0, overflow:"hidden", zIndex:-1 }}
/>
);
// ─── PROFILE ───────────────────────────────────────────────────────────────
if (screen === "profile") return (
setScreen("home")} style={backBtn()}>← Home
Athlete Profile
{saved && (
✓ Locked In
)}
);
// ─── WORKOUT ───────────────────────────────────────────────────────────────
if (screen === "workout") return (
setScreen("home")} style={backBtn()}>← Home
Today's Session
{wLoading ? (
) : (
<>
e.currentTarget.style.borderColor=C.amber}
onMouseOut={e=>e.currentTarget.style.borderColor=C.border}
>↺ New Session
{ setChatMode("coach"); setScreen("chat"); }} style={{
flex:1,
background:`linear-gradient(135deg,${C.amber},${C.amberLo})`,
border:"none", borderRadius:10, padding:"11px",
color:C.cream, fontSize:11, letterSpacing:2, textTransform:"uppercase",
cursor:"pointer", fontFamily:"'Georgia',serif",
}}>Talk to TITAN →
>
)}
);
return null;
}
// ─── LAYOUT SHELL ─────────────────────────────────────────────────────────────
function Shell({ children }) {
return (
);
}
function backBtn() {
return {
background:"none", border:`1px solid ${C.border}`, borderRadius:8,
color:C.textMid, cursor:"pointer", padding:"6px 12px",
fontSize:11, letterSpacing:1, fontFamily:"'Georgia',serif",
};
}
function GlobalStyle() {
return (
);
}