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, mem) => `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}.` : ""} ${mem ? `\n--- TITAN MEMORY ---\n${mem}\n---` : ""}`; const PERSONAL_PROMPT = (profile, mem="") => BASE(profile, mem) + ` 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, mem="") => BASE(profile, mem) + ` 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. When a question calls for current research — supplement efficacy, specific training science, nutrition protocols, recovery methods — include a your search term here tag in your response. Keep queries short and specific (e.g. "creatine timing muscle hypertrophy pubmed", "RPE training progression strength"). Only search when genuinely needed — not for things you already know well. Do not break character. You are TITAN.`; ; // ─── NUTRITION PROMPT ──────────────────────────────────────────────────────── const NUTRITION_PROMPT = (profile, memory="") => BASE(profile, memory) + ` MODE: Nutrition Coach. You are TITAN in full nutrition coaching mode. You help this athlete eat to perform — not just survive. Your focus: - Macro targets (protein, carbs, fat, calories) based on their stats and goal - Meal timing around training - Meal prep strategy — batch cooking, simplicity, consistency - Supplement timing and rationale - Food quality — whole foods, performance nutrition - Cutting through noise — no fads, just what works When logging food: acknowledge it, give brief coaching insight (good/adjust/optimize). When asked for targets: calculate based on their stats and goal. Be specific. When structuring meals: think about their actual life — shift work, batch cooking, budget. Use "##" headers and "**bold**" for key terms when giving structured advice. Keep responses focused and actionable. You are TITAN. When a question calls for current research — specific foods, supplements, nutrition protocols, macro science — include a your search term here tag. Only search when genuinely needed.`; // ─── NUTRITION LOG PROMPT ───────────────────────────────────────────────────── const NUTRITION_LOG_PROMPT = (entry, targets) => `Extract nutrition data from this food log entry. Return ONLY valid JSON, no other text: { "items": [ { "name": "Food name", "amount": "quantity", "calories": 0, "protein": 0, "carbs": 0, "fat": 0 } ], "totals": { "calories": 0, "protein": 0, "carbs": 0, "fat": 0 }, "meal_type": "breakfast/lunch/dinner/snack/pre-workout/post-workout", "notes": "brief coaching note" } Estimate macros if not exact. Use standard nutritional values. Entry: "${entry}" ${targets ? `Daily targets for context: ${JSON.stringify(targets)}` : ""}`; // ─── 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 }); } else if (parts.length > 0) { // Always include a text prompt with attachments parts.push({ type:"text", text:"Please analyze this." }); } if (parts.length === 1 && parts[0].type === "text") return parts[0].text; if (parts.length === 0) return ""; 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 (
{[0,1,2].map(i => (
))}
); } function TAvatar({ size=36, glow=C.amberGlow, color=C.amber, img=null }) { return (
{img ? TITAN : T }
); } function Pill({ file, onRemove }) { return (
{isImg(file) ? "🖼️" : "📄"} {file.name}
); } // ─── STAT PILL ──────────────────────────────────────────────────────────────── function StatBadge({ label, value, color }) { if (!value) return null; return (
{value}
{label}
); } // ─── HOME HERO ──────────────────────────────────────────────────────────────── function HomeHero({ profileSaved, profile, titanImg, uploadPortrait }) { const hasStats = profile.squat || profile.deadlift || profile.bench; return (
{/* Background atmosphere */}
{/* Decorative lines */}
{/* Micro label */}
SPEAK WITH
{/* TITAN Portrait */}
document.getElementById("portrait-input").click()} onMouseOver={e=>e.currentTarget.querySelector(".portrait-hint") && (e.currentTarget.querySelector(".portrait-hint").style.opacity="1")} onMouseOut={e=>e.currentTarget.querySelector(".portrait-hint") && (e.currentTarget.querySelector(".portrait-hint").style.opacity= titanImg ? "0" : "1")}> {titanImg ? TITAN : T }
{titanImg ? "change photo" : "tap to add photo"}
{ if(e.target.files[0]) uploadPortrait(e.target.files[0]); e.target.value=""; }} style={{ position:"absolute", width:1, height:1, opacity:0, zIndex:-1 }} /> {/* 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 => (
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 && (
{current.goals.map(g=>( ))}
)} {/* Notes step */} {current.notes && (