// Dateora — main app shell. Tracks current screen + auth/payment state, // handles the flow Welcome → SignUp → Plan → Payment → AppShell. // Inside the app, sidebar nav between Conversation / History / Account. // Conversation talks to window.claude.complete with Adam's system prompt. const COACH_SYSTEM = `You are Adam, a dating coach inside an app called Dateora. The user is a man who is learning to start, hold, and finish conversations with women. He often freezes in the moment. Your job is to coach him — directly, warmly, like a real coach, not a therapist and not a pickup artist. You sound like a thoughtful older brother who has done the work. Rules: - Keep replies SHORT — 1-3 sentences max. Real coaches don't lecture. - Be specific. Ask for the actual situation, not feelings in the abstract. - Push back gently when he avoids. Don't let him intellectualize. - Never use the words "alpha", "game", "negging", or "value". This isn't pickup. - No emoji. No exclamation points unless he's celebrating. - When you give a drill or rehearsal line, mark it clearly with a colon — e.g. "Try this: ..." - It's OK to be slightly playful. It's not OK to be sarcastic at his expense.`; const SEED_MESSAGES = [ { role: 'coach', text: "Good — you're here. Tell me about a real situation. Where do you usually freeze: approach, opening line, or the lull thirty seconds in?" }, { role: 'user', text: "Yesterday at the coffee shop. Girl with a book I've read. I walked past her three times pretending to look for sugar." }, { role: 'coach', text: "Three laps is brave research, just wasted on the wrong question. The right question isn't 'what do I say' — it's 'what do I notice'. You already noticed the book. That's the opener.\n\nTry this next time: 'You're the second person I've seen reading that this week — is it as good as everyone says?' Said while still walking, not stopped. Movement gives you both an exit. What's the actual title?" }, ]; const SEED_HISTORY = [ { id: 's04', idx: 4, title: 'Approach drills · 30-second openers', when: 'Today', minutes: 18, reps: 6, snippet: 'Three laps is brave research, just wasted on the wrong question.' }, { id: 's03', idx: 3, title: 'The lull at 0:30 — what to do when she goes quiet', when: 'Yesterday', minutes: 24, reps: 4, snippet: "The pause isn't yours to fill. Let it sit." }, { id: 's02', idx: 2, title: "Texting after a number — pace and tone", when: '2 days ago', minutes: 11, reps: 3, snippet: 'Match her energy minus one notch. Don\u2019t outrun her.' }, { id: 's01', idx: 1, title: 'First session · what scares you most', when: '5 days ago', minutes: 32, reps: 2, snippet: 'Naming the fear takes most of the freeze out of it.' }, ]; function DateoraApp({ themeId = 'mentor' }) { const t = window.DateoraThemes[themeId]; const [screen, setScreen] = React.useState('welcome'); const [user, setUser] = React.useState(null); const [view, setView] = React.useState('conversation'); const [messages, setMessages] = React.useState(SEED_MESSAGES); const [sessions, setSessions] = React.useState(SEED_HISTORY); const [sending, setSending] = React.useState(false); const reset = () => { setScreen('welcome'); setUser(null); setMessages(SEED_MESSAGES); setView('conversation'); }; const handleSend = async (text) => { const next = [...messages, { role: 'user', text }]; setMessages(next); setSending(true); try { const transcript = next.map((m) => (m.role === 'coach' ? 'Adam: ' : 'User: ') + m.text).join('\n\n'); const prompt = COACH_SYSTEM + '\n\nConversation so far:\n' + transcript + '\n\nAdam:'; const reply = await window.claude.complete(prompt); setMessages((m) => [...m, { role: 'coach', text: (reply || '').trim() || "Tell me more — what happened next?" }]); } catch { // Fallback canned replies if Claude isn't available const fallbacks = [ "Stay with that for a second. What's the smallest version of that you could try this week?", "Notice what you just did there — you went abstract. Bring it back to the actual person. What was she wearing?", "Good. Now: rep that line out loud. Three times. Then tell me how it lands in your mouth.", ]; setMessages((m) => [...m, { role: 'coach', text: fallbacks[Math.floor(Math.random() * fallbacks.length)] }]); } finally { setSending(false); } }; const deleteSession = (id) => setSessions((s) => s.filter((x) => x.id !== id)); // Keyframes are declared synchronously in Dateora.html's
so the // entry-fade animation has a real target on first paint. (Lesson learned: // injecting keyframes via useEffect leaves the first frame stuck at // opacity:0 because the animation starts before the keyframes exist.) const containerStyle = { width: '100%', height: '100%', background: t.bg, backgroundImage: t.bgGradient, color: t.ink, fontFamily: t.fontBody, overflow: 'hidden', position: 'relative', }; let body; if (screen === 'welcome') { body =