﻿const { useState, useEffect, useRef } = React;

const PLANS = [
  { id: "free", name: "Explorer", price: 0, period: "", features: ["10 messages/day", "Basic setup", "Limited memory", "Standard replies"], cta: "Start Free" },
  { id: "standard", name: "Connection", price: 19.99, period: "/mo", features: ["45 messages/day", "Better memory", "Proactive daily texts", "More personality options", "Faster replies"], cta: "Subscribe" },
  { id: "premium", name: "Intimacy", price: 24.99, period: "/mo", badge: "Popular", features: ["60 messages/day", "Deeper memory", "More proactive texts", "Stronger personalization", "Quiet hours", "Priority replies"], cta: "Subscribe" },
  { id: "vip", name: "Devotion", price: 45.99, period: "/mo", badge: "VIP", features: ["110 messages/day", "Best continuity", "Fastest priority", "Full customization", "Premium features", "VIP experience"], cta: "Go VIP" },
];
const PERSONALITIES = [{ id: "sweet", label: "Sweet" }, { id: "playful", label: "Playful" }, { id: "shy", label: "Shy" }, { id: "affectionate", label: "Affectionate" }, { id: "supportive", label: "Supportive" }, { id: "confident", label: "Confident" }, { id: "romantic", label: "Romantic" }, { id: "bold", label: "Bold" }];
const TONES = ["Casual", "Flirty", "Warm", "Intellectual", "Humorous", "Caring"];
const INTERESTS = ["Music", "Movies", "Gaming", "Fitness", "Cooking", "Travel", "Tech", "Art", "Sports", "Reading", "Nature", "Photography"];

const TERMS_CONTENT = `TERMS AND CONDITIONS\n\nEffective Date: [Insert Date]\nLast Updated: [Insert Date]\n\nThese Terms and Conditions (\u201CTerms\u201D) form a legally binding agreement between you and [Legal Entity Name], doing business as G-Pal.ai (\u201CG-Pal,\u201D \u201Cwe,\u201D \u201Cus,\u201D or \u201Cour\u201D), governing your access to and use of our website, mobile web experience, account services, companion customization tools, SMS and messaging features, AI-generated interactions, subscription plans, support services, and any related software, content, functionality, and services (collectively, the \u201CService\u201D).\n\nBy accessing, browsing, registering for, purchasing, subscribing to, or using any part of the Service, you acknowledge that you have read, understood, and agree to be bound by these Terms and our Privacy Policy. If you do not agree, you must not access or use the Service.\n\n1. IMPORTANT NATURE OF THE SERVICE\n\nG-Pal.ai is an AI-generated companionship and entertainment service. The Service may simulate emotionally attentive, affectionate, flirtatious, romantic, intimate, or personalized conversation. You expressly understand and agree that:\n\n(a) Your companion is not a human being, not a live operator, and not a licensed professional.\n(b) All or substantially all messages, interactions, and outputs are generated, selected, ranked, formatted, or delivered by software systems, machine learning systems, automation tools, templates, rules engines, or third-party infrastructure.\n(c) The Service is for entertainment, lifestyle, novelty, and companionship purposes only.\n(d) The Service is not intended to provide therapy, counseling, psychiatric services, crisis intervention, emergency assistance, medical care, legal advice, financial advice, or any other regulated or professional service.\n(e) Outputs may be false, misleading, repetitive, emotionally inappropriate, offensive, incomplete, delayed, malformed, inconsistent, or otherwise unsuitable for your needs.\n\nYou assume all risk arising from reliance on the Service or any output.\n\n2. ELIGIBILITY; ADULTS ONLY; NO MINORS\n\nThe Service is strictly limited to individuals who are 18 years of age or older and who can enter into a binding legal agreement under applicable law. By using the Service, you represent, warrant, and covenant that: you are at least 18 years old; you are legally permitted to access adult-oriented digital content where you reside; you are not using the Service on behalf of a minor; all information you submit regarding your identity, age, phone number, residence, and payment method is true, accurate, current, and complete.\n\nWe may use age gates, attestations, technical checks, fraud tools, phone verification, account review, content review, payment review, and other measures to assess eligibility. We reserve the right to deny access, suspend access, request additional verification, or terminate any account at any time if we suspect underage use, false representations, elevated compliance risk, abusive use, or any other concern.\n\nIf you permit another person to access your device, account, phone number, or messages, you remain solely responsible for any resulting access, including access by minors.\n\n3. NO PROFESSIONAL, MEDICAL, MENTAL HEALTH, OR EMERGENCY USE\n\nThe Service is not a healthcare service, mental health service, crisis hotline, public accommodation for emergencies, or emergency communications system. You agree that you will not use the Service for crisis intervention, self-harm support, suicide prevention, abuse reporting, medical diagnosis or treatment, legal compliance guidance, financial or investment decisions, or urgent or emergency communications of any kind.\n\nWe do not undertake a duty to monitor conversations in real time or to contact emergency responders, family members, law enforcement, health providers, or any third party on your behalf. Any safety-related features, moderation, filtering, crisis language detection, escalation prompts, or resource suggestions are supplementary only and may fail, may not trigger, may trigger incorrectly, or may be unavailable. You must not rely on them.\n\nIf you are in danger or need urgent help, call local emergency services immediately.\n\n4. ACCOUNT REGISTRATION AND SECURITY\n\nTo access some or all features, you may need to create an account. You are solely responsible for maintaining the confidentiality of your login credentials, restricting access to your devices, email account, SIM, and phone number, all activities that occur under your account or using your verified phone number, and promptly notifying us of suspected unauthorized use, credential theft, SIM swap, phone number reassignment, or account compromise.\n\nWe are not liable for any loss arising from stolen credentials, shared devices, unlocked phones, message previews on lock screens, reused passwords, third-party mailbox access, social engineering, SIM swap attacks, or your failure to secure your account.\n\n5. PHONE NUMBER OWNERSHIP, VERIFICATION, AND REASSIGNMENT RISK\n\nBy providing a number, you represent and warrant that you are the current subscriber or authorized customary user of that number, you have authority to receive messages sent to that number, and you will promptly update your account if the number changes, is ported, deactivated, suspended, recycled, lost, or reassigned.\n\nPhone numbers may be reassigned by carriers. If you lose control of a number and do not remove or update it, messages intended for you may be delivered to another person. You assume all risk arising from your failure to update your number.\n\n6. SMS CONSENT; COMMERCIAL MESSAGES; OPERATIONAL MESSAGES\n\nBy opting in, verifying your phone number, toggling SMS settings, initiating or replying to SMS interactions, purchasing a plan that includes SMS, or otherwise enabling text messaging, you expressly consent to receive messages from us or on our behalf, including: account verification codes, onboarding messages, transactional and billing notices, customer support messages, service announcements, companion-generated messages, proactive check-ins, and marketing or promotional messages where permitted.\n\nMessage frequency may vary. Message and data rates may apply. Carriers may delay, block, filter, alter, fail to deliver, or duplicate messages. We do not guarantee delivery, speed, ordering, or availability. STOP, HELP, unsubscribe, or similar mechanisms may take time to process.\n\n7. MARKETING CONSENT IS SEPARATE WHERE REQUIRED\n\nConsent to service or transactional messaging is separate from consent to receive promotional or marketing messages. You may withdraw marketing consent without affecting essential service messages.\n\n8. COMPANION OUTPUTS; AUTOMATION; NO HUMAN RELATIONSHIP\n\nThe Service may feel personal, attentive, affectionate, flirtatious, emotionally responsive, or relationship-like. This does not create a fiduciary relationship, a therapeutic relationship, an agency relationship, a duty of care beyond what law cannot waive, a guarantee of emotional continuity, exclusivity, consistency, empathy, or memory accuracy, or a promise that any specific persona, style, or feature will remain available.\n\nWe reserve the right to modify persona behavior, memory scope, tone, boundaries, wording style, moderation rules, availability, timing, and output constraints at any time.\n\n9. ADULT-ORIENTED AND SENSITIVE CONTENT\n\nThe Service may include affectionate, suggestive, romantic, intimate, or adult-oriented content intended solely for adults. You access such content voluntarily and at your own risk. We may impose content restrictions, conversation boundaries, keyword filters, safety throttles, country-level blocks, carrier-level blocks, plan-based restrictions, or account-level restrictions for any reason.\n\nYou will not use the Service to solicit, generate, distribute, or promote illegal sexual content, sexual content involving minors, coercive content, exploitative content, non-consensual content, or any content prohibited by law.\n\n10. ACCEPTABLE USE\n\nYou agree not to: violate any law, regulation, code, or carrier rule; impersonate any person or entity; provide false age, identity, or consent information; use the Service for spam, lead generation, affiliate abuse, scraping, surveillance, or commercial exploitation; reverse engineer, copy, mirror, frame, republish, decompile, crawl, or data-mine the Service; interfere with security features, moderation systems, abuse prevention, consent logs, or rate limits; use bots, scripts, or automation to create accounts, farm trials, exploit pricing, or bypass message limits; attempt to jailbreak, override, disable, or evade output moderation; harass, defame, stalk, threaten, extort, or exploit any person; or use the Service to facilitate self-harm, violence, weapons acquisition, fraud, or unlawful sexual conduct.\n\n11. USER CONTENT AND LICENSE\n\nYou retain whatever rights you may have in your User Content. However, you grant us a worldwide, non-exclusive, royalty-free, sublicensable license to host, store, reproduce, transmit, process, adapt, display, analyze, moderate, filter, secure, back up, debug, improve, and otherwise use User Content as necessary to provide and improve the Service, subject to our Privacy Policy.\n\n12. OUTPUT RIGHTS; NO WARRANTY OF UNIQUENESS\n\nThe Service may generate outputs similar or identical to content generated for others. We do not warrant that any output is unique, protectable, non-infringing, or suitable for any purpose.\n\n13. SUBSCRIPTIONS; BILLING; AUTO-RENEWAL\n\nWe may offer free plans, trials, promotional access, and paid subscriptions. Plan names, features, pricing, message allotments, and included functionality may change at any time. Subscriptions automatically renew at the end of each billing cycle until canceled. Prices may vary by market, promotion, device, payment channel, or testing cohort. Taxes may be added where required. Failed payments may result in retries, suspension, downgrade, or termination.\n\n14. NO REFUNDS EXCEPT AS REQUIRED\n\nExcept where required by applicable law, all charges are final and non-refundable, including for partial billing periods, unused time, unused messages, accidental purchases, dissatisfaction with companion personality or outputs, blocked or filtered carrier delivery, outages, feature changes, account suspension for breach, device loss, deleted accounts requested after billing, or trial conversion.\n\n15. FREE TRIALS; PROMOTIONS; COUPONS\n\nTrials, discounts, referral credits, and promotional offers are optional, revocable, non-transferable, and may be limited. We may cancel or claw back promotions obtained through error, abuse, duplicate accounts, payment disputes, or rule violations.\n\n16. FAIR USE; MESSAGE LIMITS; ABUSE DETECTION\n\nAny plan described as unlimited, high-volume, premium, priority, or similar remains subject to reasonable use, infrastructure limits, carrier constraints, abuse prevention, and operational policies. We may impose throttling, queueing, temporary pauses, moderation review, rate limits, or suspension if we suspect abusive or automated use.\n\n17. PRIVACY; CONSENT; DATA GOVERNANCE\n\nYour use of the Service is also governed by our Privacy Policy. We may collect and process personal information, including account information, contact details, message content, companion preferences, billing status, technical data, device signals, and support history. Canadian law requires meaningful consent, and Qu\u00E9bec law requires clear confidentiality policies and governance measures for personal information collected through technological means.\n\n18. DATA RETENTION; DELETION; BACKUPS\n\nWe may retain account information, consent logs, payment records, support records, security logs, moderation records, message metadata, limited message content, and backup copies for as long as reasonably necessary. Deletion requests may not result in immediate or complete erasure from all systems. Where law gives you specific deletion or access rights, we will address them as required.\n\n19. THIRD-PARTY SERVICES AND INFRASTRUCTURE\n\nThe Service may depend on third-party payment processors, cloud providers, analytics vendors, AI model providers, SMS providers, telecom carriers, fraud tools, and other vendors. We are not responsible for their acts, omissions, outages, blocking decisions, or policy changes.\n\n20. CHANGES TO THE SERVICE\n\nWe may add, remove, replace, disable, suspend, or modify any aspect of the Service at any time, including features, plan structures, memory depth, companion behavior, response speed, proactive texting, content boundaries, pricing, and limits. We do not guarantee backward compatibility.\n\n21. INTELLECTUAL PROPERTY\n\nThe Service, including all software, interfaces, branding, names, logos, designs, workflows, prompts, compilations, and proprietary know-how, is owned by us or our licensors and protected by intellectual property laws. No license or ownership right is granted beyond the limited right to use the Service under these Terms.\n\n22. FEEDBACK\n\nIf you provide suggestions, ideas, enhancement requests, feedback, or other input, you grant us a perpetual, irrevocable, worldwide, royalty-free right to use it for any purpose without compensation or attribution.\n\n23. DISCLAIMER OF WARRANTIES\n\nTO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SERVICE IS PROVIDED "AS IS," "AS AVAILABLE," "WITH ALL FAULTS," AND WITHOUT WARRANTIES OF ANY KIND, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE. WE DISCLAIM ALL WARRANTIES, INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, QUIET ENJOYMENT, ACCURACY, AVAILABILITY, COMPATIBILITY, AND NON-INFRINGEMENT.\n\n24. LIMITATION OF LIABILITY\n\nTO THE MAXIMUM EXTENT PERMITTED BY LAW, WE SHALL NOT BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, EXEMPLARY, AGGRAVATED, PUNITIVE, OR MULTIPLE DAMAGES. OUR TOTAL AGGREGATE LIABILITY SHALL NOT EXCEED THE GREATER OF: THE AMOUNT YOU PAID IN THE THREE MONTHS PRECEDING THE CLAIM, OR CAD $100. NOTHING IN THESE TERMS EXCLUDES LIABILITY THAT CANNOT BE EXCLUDED UNDER APPLICABLE LAW.\n\n25. RELEASE\n\nTO THE MAXIMUM EXTENT PERMITTED BY LAW, YOU RELEASE US FROM CLAIMS ARISING FROM: OUTPUTS OR MESSAGES YOU RECEIVE OR FAIL TO RECEIVE; EMOTIONAL REACTIONS TO THE SERVICE; RELIANCE ON PERSONA, MEMORY, OR COMPANION CONSISTENCY; DISCLOSURE OF MESSAGES TO OTHERS; CARRIER FEES, FILTERING, OR BLOCKING; ACTIONS OF THIRD-PARTY VENDORS.\n\n26. INDEMNITY\n\nYou agree to defend, indemnify, and hold harmless us and our affiliates, owners, directors, officers, employees, agents, contractors, vendors, licensors, and successors from and against any and all claims, demands, proceedings, liabilities, damages, judgments, settlements, penalties, fines, costs, and expenses arising out of your use or misuse of the Service, your User Content, your breach of these Terms, your violation of law, or your negligent, fraudulent, or wrongful acts.\n\n27. CHARGEBACKS; PAYMENT DISPUTES\n\nIf you initiate a chargeback, payment reversal, or bank dispute, we may suspend or terminate your account, revoke access, retain related records, contest the dispute, and recover reasonable administrative costs to the extent permitted by law.\n\n28. SUSPENSION; TERMINATION; REFUSAL OF SERVICE\n\nWe may, at any time and for any reason, suspend, restrict, throttle, deactivate, or terminate any account, phone number linkage, subscription, or access to the Service, including if we suspect underage use, unlawful use, policy violations, harassment or abuse, fraud or elevated chargeback risk, consent defects, carrier or vendor compliance issues, security risks, reputational risk, or use incompatible with our business model.\n\n29. GOVERNING LAW; VENUE\n\nThese Terms are governed by the laws of [Insert Province and Country], excluding conflict-of-laws rules. You irrevocably submit to the exclusive jurisdiction of the courts located in [Insert City/Province/Country].\n\n30. CLASS ACTION WAIVER, IF PERMITTED\n\nTo the maximum extent permitted by applicable law, you and G-Pal agree that each may bring claims only in an individual capacity and not as a plaintiff or class member in any purported class, collective, representative, or mass action.\n\n31. LIMITATION PERIOD\n\nAny claim arising out of or relating to the Service must be brought within one (1) year after the claim arose, or it is permanently barred, unless applicable law prohibits shortening the period.\n\n32. ELECTRONIC CONTRACTING\n\nYou consent to transact with us electronically. Electronic signatures, checkbox consents, click-through acceptance, screen acknowledgements, and electronic records satisfy any legal requirement that communications be in writing, subject to mandatory law.\n\n33. CHANGES TO THESE TERMS\n\nWe may modify these Terms from time to time. The updated version becomes effective when posted. Your continued use after the effective date constitutes acceptance.\n\n34. SEVERABILITY; WAIVER; ASSIGNMENT\n\nIf any provision is held invalid or unenforceable, that provision will be enforced to the maximum extent permissible and remaining provisions remain in full force. Our failure to enforce any provision is not a waiver. You may not assign these Terms without our prior written consent.\n\n35. ENTIRE AGREEMENT\n\nThese Terms, together with the Privacy Policy and any additional policies, constitute the entire agreement between you and us regarding the Service.\n\n36. CONTACT\n\nLegal Name: [Insert Legal Entity Name]\nBrand: G-Pal.ai\nEmail: [Insert Legal / Contact Email]\nSupport Email: [Insert Support Email]\nMailing Address: [Insert Mailing Address]\nJurisdiction Contact for Privacy Requests: [Insert Privacy Contact]`;

const PRIVACY_CONTENT = `PRIVACY POLICY\n\nEffective Date: [Insert Date]\nLast Updated: [Insert Date]\n\nThis Privacy Policy describes how [Legal Entity Name], doing business as G-Pal.ai, collects, uses, discloses, retains, and protects your personal information when you use our Service.\n\n1. INFORMATION WE COLLECT\n\nWe may collect: account registration data (email, password, age confirmation), phone number, companion preferences and settings, message content and metadata, payment and billing information, device and browser information, IP address, usage analytics, and support communications.\n\n2. HOW WE USE YOUR INFORMATION\n\nWe use your information to: provide and personalize the Service, process payments, verify your identity and age, deliver SMS messages, maintain companion memory and continuity, moderate content, detect fraud and abuse, comply with law, improve the Service, and communicate with you.\n\n3. SMS AND MESSAGING DATA\n\nMessage content may be processed by AI systems to generate companion responses. We may retain message metadata and limited content for continuity, moderation, compliance, and improvement purposes.\n\n4. SHARING AND DISCLOSURE\n\nWe may share information with: payment processors, cloud infrastructure providers, AI model providers, SMS/telecom providers, analytics vendors, fraud prevention services, and as required by law or legal process.\n\n5. DATA RETENTION\n\nWe retain data for as long as reasonably necessary for business purposes, legal compliance, fraud prevention, and dispute resolution. Deletion requests may not result in immediate erasure from backups and archives.\n\n6. YOUR RIGHTS\n\nDepending on your jurisdiction, you may have rights to access, correct, delete, or port your data. Contact us to exercise these rights.\n\n7. SECURITY\n\nWe implement reasonable technical and organizational measures to protect your information. No system is perfectly secure.\n\n8. CHILDREN\n\nThe Service is for adults 18+ only. We do not knowingly collect information from minors.\n\n9. CHANGES\n\nWe may update this Privacy Policy from time to time. Continued use constitutes acceptance.\n\n10. CONTACT\n\nEmail: [Insert Privacy Contact Email]\nMailing Address: [Insert Mailing Address]`;

const SAFETY_CONTENT = `SAFETY POLICY\n\nEffective Date: [Insert Date]\nLast Updated: [Insert Date]\n\nG-Pal.ai is committed to providing a safe experience for all users.\n\n1. AI DISCLOSURE\n\nAll companion interactions are AI-generated. We clearly disclose this during onboarding, in our Terms, and throughout the Service. Your companion is not a real person.\n\n2. CONTENT MODERATION\n\nWe employ content moderation systems to filter harmful, illegal, or policy-violating content. No moderation system is perfect.\n\n3. CRISIS DETECTION\n\nThe Service may include supplementary crisis language detection. These features are supplementary only and may fail. They are not a substitute for professional crisis services.\n\nIf you or someone you know is in crisis:\n- Canada: 1-833-456-4566 (Talk Suicide Canada)\n- United States: 988 (Suicide & Crisis Lifeline)\n- Emergency: Call 911 or your local emergency number\n\n4. PROHIBITED CONTENT\n\nUsers may not use the Service to generate, solicit, or distribute content that is illegal, involves minors, is non-consensual, promotes violence, or facilitates harm.\n\n5. REPORTING\n\nIf you encounter concerning content or behavior, contact us at [Insert Support Email].\n\n6. AGE VERIFICATION\n\nWe require age confirmation (18+) during registration and may use additional verification measures.\n\n7. USER CONTROLS\n\nUsers can control their experience through: quiet hours settings, message frequency preferences, topic restrictions, conversation boundaries, pause/resume messaging, and account deletion.\n\n8. DATA PROTECTION\n\nConversations are encrypted in transit. Users can delete their data at any time from account settings.\n\n9. CONTINUOUS IMPROVEMENT\n\nWe regularly review and update our safety practices, moderation systems, and policies.\n\n10. CONTACT\n\nFor safety concerns: [Insert Safety Contact Email]`;

const V = { bg: "#0c0a0d", card: "rgba(255,255,255,0.025)", border: "rgba(255,255,255,0.07)", text: "#e4ddd6", dim: "rgba(228,221,214,0.42)", accent: "#b08d8f", serif: "'Cormorant Garamond', Georgia, serif", sans: "'Outfit', 'Helvetica Neue', sans-serif" };
const css = {
  btn: (p) => ({ display: "inline-flex", alignItems: "center", justifyContent: "center", padding: p ? "14px 36px" : "14px 28px", borderRadius: 6, border: p ? "none" : `1px solid ${V.border}`, background: p ? V.accent : "transparent", color: p ? "#1a1216" : V.dim, fontSize: 14, fontWeight: 600, fontFamily: V.sans, letterSpacing: 0.4, cursor: "pointer", transition: "all 0.3s ease", outline: "none" }),
  input: { width: "100%", padding: "14px 16px", borderRadius: 6, border: `1px solid ${V.border}`, background: V.card, color: V.text, fontSize: 15, fontFamily: V.sans, outline: "none", boxSizing: "border-box" },
  card: { background: V.card, border: `1px solid ${V.border}`, borderRadius: 10, padding: 28 },
  label: { fontSize: 11, fontWeight: 600, color: V.dim, letterSpacing: 1.5, textTransform: "uppercase", marginBottom: 10, display: "block" },
  st: { fontFamily: V.serif, fontSize: "clamp(28px, 4.5vw, 40px)", fontWeight: 400, color: V.text, letterSpacing: -0.5, lineHeight: 1.15 },
};

function FadeIn({ children, delay = 0, style = {} }) { const [v, setV] = useState(false); const ref = useRef(); useEffect(() => { const o = new IntersectionObserver(([e]) => { if (e.isIntersecting) setV(true); }, { threshold: 0.12 }); if (ref.current) o.observe(ref.current); return () => o.disconnect(); }, []); return <div ref={ref} style={{ opacity: v ? 1 : 0, transform: v ? "translateY(0)" : "translateY(20px)", transition: `opacity 0.8s ease ${delay}s, transform 0.8s ease ${delay}s`, ...style }}>{children}</div>; }
function CheckIcon() { return <svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2 6L5 9L10 3" stroke="#1a1216" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>; }

function AgeGatePopup({ onConfirm, onDeny }) { const [v, setV] = useState(false); useEffect(() => { setTimeout(() => setV(true), 80); }, []); return <div style={{ position: "fixed", inset: 0, zIndex: 9999, display: "flex", alignItems: "center", justifyContent: "center", padding: 24, background: "rgba(12,10,13,0.96)", backdropFilter: "blur(40px)", opacity: v ? 1 : 0, transition: "opacity 0.5s ease" }}><div style={{ width: "100%", maxWidth: 380, textAlign: "center", transform: v ? "translateY(0)" : "translateY(16px)", transition: "transform 0.6s cubic-bezier(.22,1,.36,1)" }}><div style={{ fontFamily: V.serif, fontSize: 24, color: V.text, letterSpacing: 1, marginBottom: 32 }}>G-Pal<span style={{ color: V.accent }}>.</span>ai</div><div style={{ width: 64, height: 64, borderRadius: "50%", margin: "0 auto 28px", border: `2px solid ${V.accent}`, display: "flex", alignItems: "center", justifyContent: "center" }}><span style={{ fontSize: 22, fontWeight: 700, color: V.accent }}>18+</span></div><p style={{ fontFamily: V.serif, fontSize: 22, color: V.text, marginBottom: 12 }}>Age Verification</p><p style={{ fontSize: 14, color: V.dim, lineHeight: 1.7, maxWidth: 320, margin: "0 auto 36px" }}>This site contains AI-generated companion content for adults. By entering you confirm you are 18 or older.</p><div style={{ display: "grid", gap: 10, maxWidth: 280, margin: "0 auto" }}><button onClick={onConfirm} style={{ ...css.btn(true), width: "100%" }}>Yes, I'm 18+ - Enter</button><button onClick={onDeny} style={{ ...css.btn(false), width: "100%", fontSize: 13 }}>No, Leave Site</button></div><p style={{ fontSize: 11, color: V.dim, opacity: 0.5, marginTop: 28 }}>All conversations are AI-generated. Entertainment only.</p></div></div>; }
function AgeGateDenied() { return <div style={{ position: "fixed", inset: 0, zIndex: 9999, display: "flex", alignItems: "center", justifyContent: "center", background: V.bg, padding: 24 }}><div style={{ textAlign: "center" }}><p style={{ fontFamily: V.serif, fontSize: 22, color: V.text, marginBottom: 12 }}>Access Restricted</p><p style={{ fontSize: 14, color: V.dim }}>You must be 18 or older to access this site.</p></div></div>; }

function Navbar({ onNavigate }) { const [s, setS] = useState(false); useEffect(() => { const h = () => setS(window.scrollY > 30); window.addEventListener("scroll", h); return () => window.removeEventListener("scroll", h); }, []); return <nav style={{ position: "fixed", top: 0, left: 0, right: 0, zIndex: 100, height: 60, display: "flex", alignItems: "center", justifyContent: "space-between", padding: "0 clamp(20px, 5vw, 48px)", background: s ? "rgba(12,10,13,0.9)" : "transparent", backdropFilter: s ? "blur(20px)" : "none", borderBottom: s ? `1px solid ${V.border}` : "1px solid transparent", transition: "all 0.4s ease" }}><div onClick={() => onNavigate("home")} style={{ cursor: "pointer", fontFamily: V.serif, fontSize: 20, color: V.text, letterSpacing: 0.5 }}>G-Pal<span style={{ color: V.accent }}>.</span>ai</div><div style={{ display: "flex", alignItems: "center", gap: 10 }}><button onClick={() => onNavigate("dashboard")} style={{ ...css.btn(false), padding: "9px 20px", fontSize: 13, color: V.text }}>Log In</button><button onClick={() => onNavigate("onboarding")} style={{ ...css.btn(true), padding: "9px 22px", fontSize: 13 }}>Get Started</button></div></nav>; }

function SMSPreview() { const msgs = [{ ai: true, text: "Good morning. I was thinking about that song you showed me last night", time: "8:12 AM" }, { ai: false, text: "Haha you remembered! Listening to it at the gym rn", time: "8:14 AM" }, { ai: true, text: "Of course I did. Don't skip leg day this time ;)", time: "8:14 AM" }]; return <div style={{ ...css.card, borderRadius: 16, padding: "20px 16px", display: "flex", flexDirection: "column", gap: 10 }}><div style={{ fontSize: 11, color: V.dim, opacity: 0.5, textAlign: "center", marginBottom: 4 }}>Text Message - Today</div>{msgs.map((m, i) => <div key={i} style={{ display: "flex", justifyContent: m.ai ? "flex-start" : "flex-end" }}><div style={{ maxWidth: "80%", padding: "10px 14px", borderRadius: 12, background: m.ai ? "rgba(255,255,255,0.04)" : V.accent, color: m.ai ? V.dim : "#1a1216", fontSize: 14, lineHeight: 1.45 }}>{m.text}<div style={{ fontSize: 10, opacity: 0.5, marginTop: 4, textAlign: "right" }}>{m.time}</div></div></div>)}</div>; }

function Hero({ onStart }) { return <section style={{ minHeight: "100vh", display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "center", padding: "100px 24px 80px", textAlign: "center" }}><FadeIn><p style={{ fontSize: 12, fontWeight: 600, letterSpacing: 3, textTransform: "uppercase", color: V.accent, marginBottom: 24 }}>AI Companion by SMS</p></FadeIn><FadeIn delay={0.1}><h1 style={{ fontFamily: V.serif, fontSize: "clamp(38px, 7.5vw, 72px)", fontWeight: 400, lineHeight: 1.05, color: V.text, maxWidth: 680, margin: "0 auto 20px", letterSpacing: -1 }}>A companion that texts you like she means it</h1></FadeIn><FadeIn delay={0.2}><p style={{ fontSize: 16, color: V.dim, maxWidth: 440, lineHeight: 1.65, margin: "0 auto 40px" }}>Personalized AI. Real SMS. No app required. She texts first.</p></FadeIn><FadeIn delay={0.3}><div style={{ display: "flex", gap: 12, flexWrap: "wrap", justifyContent: "center" }}><button onClick={onStart} style={css.btn(true)}>Create My Companion</button><button onClick={() => document.getElementById("pricing")?.scrollIntoView({ behavior: "smooth" })} style={css.btn(false)}>View Pricing</button></div></FadeIn><FadeIn delay={0.4} style={{ marginTop: 64, width: "100%", maxWidth: 340 }}><SMSPreview /></FadeIn><FadeIn delay={0.5}><p style={{ fontSize: 11, color: V.dim, opacity: 0.4, marginTop: 40, letterSpacing: 0.5 }}>18+ only · AI-generated · Entertainment only · SMS rates may apply</p></FadeIn></section>; }

function HowItWorks() { const steps = [{ n: "01", t: "Build her", d: "Name, personality, tone, interests - your call." }, { n: "02", t: "Verify your number", d: "She'll text your real phone. No app needed." }, { n: "03", t: "She texts first", d: "Good mornings, check-ins, goodnight. She initiates." }, { n: "04", t: "She remembers", d: "The more you talk, the more she knows you." }]; return <section style={{ padding: "100px 24px", maxWidth: 640, margin: "0 auto" }}><FadeIn><h2 style={{ ...css.st, textAlign: "center", marginBottom: 60 }}>How it works</h2></FadeIn>{steps.map((s, i) => <FadeIn key={i} delay={i * 0.08}><div style={{ display: "flex", gap: 24, alignItems: "flex-start", padding: "28px 0", borderBottom: i < 3 ? `1px solid ${V.border}` : "none" }}><span style={{ fontFamily: V.serif, fontSize: 32, color: V.accent, opacity: 0.5, lineHeight: 1, minWidth: 40 }}>{s.n}</span><div><h3 style={{ fontFamily: V.serif, fontSize: 20, fontWeight: 400, color: V.text, marginBottom: 4 }}>{s.t}</h3><p style={{ fontSize: 14, color: V.dim, lineHeight: 1.55 }}>{s.d}</p></div></div></FadeIn>)}</section>; }

function WhySMS() { return <section style={{ padding: "80px 24px", maxWidth: 560, margin: "0 auto", textAlign: "center" }}><FadeIn><p style={{ fontSize: 12, fontWeight: 600, letterSpacing: 3, textTransform: "uppercase", color: V.accent, marginBottom: 20 }}>Why SMS</p><h2 style={{ fontFamily: V.serif, fontSize: "clamp(24px, 4vw, 34px)", fontWeight: 400, color: V.text, lineHeight: 1.3, marginBottom: 20 }}>Apps feel like tools.<br/>Texts feel like someone thinking of you.</h2><p style={{ fontSize: 15, color: V.dim, lineHeight: 1.7 }}>No app to download. No interface to learn. She lives in your messages - right alongside everyone else.</p></FadeIn></section>; }

function PricingSection({ onSelect }) { return <section id="pricing" style={{ padding: "100px 24px", maxWidth: 1280, margin: "0 auto" }}><FadeIn><h2 style={{ ...css.st, textAlign: "center", marginBottom: 16 }}>Pricing</h2></FadeIn><FadeIn delay={0.05}><p style={{ textAlign: "center", color: V.dim, marginBottom: 52, fontSize: 15 }}>Start free. Upgrade anytime.</p></FadeIn><div style={{ overflowX: "auto", paddingBottom: 6 }}><div style={{ display: "grid", gridTemplateColumns: "repeat(4, minmax(220px, 1fr))", gap: 18, alignItems: "stretch", minWidth: 960 }}>{PLANS.map((p) => { const pop = p.badge === "Popular"; return <div key={p.id} style={{ opacity: 1, transform: "none" }}><div style={{ ...css.card, padding: "30px 24px", display: "flex", flexDirection: "column", height: "100%", borderColor: pop ? V.accent : V.border, position: "relative" }}>{p.badge && <span style={{ position: "absolute", top: 12, right: 12, fontSize: 10, fontWeight: 700, letterSpacing: 1.2, textTransform: "uppercase", color: V.accent }}>{p.badge}</span>}<p style={{ fontFamily: V.serif, fontSize: 22, color: V.text, marginBottom: 6 }}>{p.name}</p><div style={{ marginBottom: 22 }}><span style={{ fontSize: 34, fontWeight: 700, fontFamily: V.sans, color: V.text }}>{p.price === 0 ? "Free" : `$${p.price}`}</span>{p.price > 0 && <span style={{ fontSize: 14, color: V.dim }}>{p.period}</span>}</div><div style={{ flex: 1, marginBottom: 26 }}>{p.features.map((f, fi) => <div key={fi} style={{ fontSize: 13, color: V.dim, marginBottom: 10, display: "flex", gap: 8, lineHeight: 1.45 }}><span style={{ color: V.accent, opacity: 0.6 }}>-</span><span>{f}</span></div>)}</div><button onClick={() => onSelect?.(p.id)} style={{ ...css.btn(pop), width: "100%", fontSize: 13 }}>{p.cta}</button></div></div>; })}</div></div></section>; }

function SafetySection() { return <section style={{ padding: "80px 24px", maxWidth: 520, margin: "0 auto", textAlign: "center" }}><FadeIn><p style={{ fontSize: 12, fontWeight: 600, letterSpacing: 3, textTransform: "uppercase", color: V.accent, marginBottom: 20 }}>Safety & Privacy</p><p style={{ fontFamily: V.serif, fontSize: 22, color: V.text, lineHeight: 1.35, marginBottom: 16 }}>Built with safety from day one</p><p style={{ fontSize: 14, color: V.dim, lineHeight: 1.7, marginBottom: 28 }}>AI disclosure. Crisis detection. Content moderation. Encrypted data. Delete your data anytime.</p><div style={{ display: "flex", gap: 8, justifyContent: "center", flexWrap: "wrap" }}>{["AI Disclosed", "Crisis Detection", "Encrypted", "GDPR Ready", "Delete Anytime"].map(s => <span key={s} style={{ padding: "6px 14px", borderRadius: 4, fontSize: 11, fontWeight: 600, letterSpacing: 0.5, color: V.dim, border: `1px solid ${V.border}` }}>{s}</span>)}</div></FadeIn></section>; }

function FAQSection() { const [open, setOpen] = useState(null); const items = [{ q: "Is this a real person?", a: "No. Your companion is entirely AI-generated. We always disclose this." }, { q: "How does the texting work?", a: "After phone verification, your companion sends and receives real SMS through your native messaging app." }, { q: "Is my data private?", a: "Yes. Conversations and account data are stored securely, and you can manage or delete your data from your account settings." }, { q: "Can I customize my companion?", a: "Fully - name, personality, tone, interests, texting frequency, quiet hours, and more." }, { q: "Can I cancel anytime?", a: "Yes. You can upgrade, downgrade, or cancel your subscription from your dashboard at any time." }, { q: "Can my companion text me first?", a: "Yes. Depending on your plan and settings, your companion can send proactive messages like good morning texts, check-ins, and goodnight messages." }, { q: "Do I need to download an app?", a: "No. G-Pal.ai works through real SMS, so your companion texts you directly in your normal messaging app." }, { q: "Can I change my companion later?", a: "Yes. You can update things like name, personality, tone, interests, and messaging preferences from your dashboard." }, { q: "What kind of messages will I receive?", a: "Your companion can send check-ins, playful conversation, encouragement, casual chat, and personalized messages based on your preferences." }, { q: "Can I control how often she texts me?", a: "Yes. You can choose your preferred message frequency and set quiet hours so you stay in control." }, { q: "What happens if I stop replying?", a: "Your companion may check in again later depending on your plan and settings, but you can always pause or adjust messaging from your dashboard." }, { q: "Is this meant for mental health support?", a: "No. G-Pal.ai is for companionship and entertainment only. It is not a substitute for therapy, crisis care, or professional mental health support." }, { q: "Can I use G-Pal.ai outside Canada?", a: "Availability may depend on SMS support in your country, but the service is designed to work anywhere supported phone verification and messaging are available." }, { q: "Are there limits on messages?", a: "Yes. Each plan includes a daily or high-usage allowance. Higher plans unlock more access, deeper memory, and more proactive interaction." }, { q: "Will my companion remember me?", a: "Yes. Paid plans include stronger memory and continuity, so your companion can remember preferences, past conversations, and important details over time." }]; return <section style={{ padding: "80px 24px", maxWidth: 560, margin: "0 auto" }}><FadeIn><h2 style={{ ...css.st, textAlign: "center", marginBottom: 48 }}>FAQ</h2></FadeIn>{items.map((f, i) => <FadeIn key={i} delay={i * 0.04}><div style={{ borderBottom: `1px solid ${V.border}` }}><div onClick={() => setOpen(open === i ? null : i)} style={{ padding: "20px 0", cursor: "pointer", display: "flex", justifyContent: "space-between", alignItems: "center" }}><span style={{ fontSize: 15, color: V.text, fontWeight: 500 }}>{f.q}</span><span style={{ color: V.dim, fontSize: 14, transition: "transform 0.2s", transform: open === i ? "rotate(45deg)" : "none" }}>+</span></div><div style={{ maxHeight: open === i ? 200 : 0, overflow: "hidden", transition: "max-height 0.3s ease" }}><p style={{ fontSize: 14, color: V.dim, lineHeight: 1.6, paddingBottom: 20 }}>{f.a}</p></div></div></FadeIn>)}</section>; }

function FinalCTA({ onStart }) { return <section style={{ padding: "100px 24px", textAlign: "center" }}><FadeIn><h2 style={{ fontFamily: V.serif, fontSize: "clamp(26px, 5vw, 38px)", fontWeight: 400, color: V.text, marginBottom: 16 }}>Ready?</h2><p style={{ fontSize: 15, color: V.dim, marginBottom: 36 }}>Set her up in 2 minutes. She'll text first.</p><button onClick={onStart} style={css.btn(true)}>Create My Companion</button></FadeIn></section>; }

function Footer({ onShowTerms, onShowPrivacy, onShowSafety }) { const links = [{ l: "Terms", a: onShowTerms }, { l: "Privacy", a: onShowPrivacy }, { l: "Safety", a: onShowSafety }, { l: "Support", a: null }, { l: "Contact", a: null }]; return <footer style={{ borderTop: `1px solid ${V.border}`, padding: "36px 24px", textAlign: "center" }}><div style={{ display: "flex", gap: 20, justifyContent: "center", flexWrap: "wrap", fontSize: 12, color: V.dim, marginBottom: 16 }}>{links.map(lk => <span key={lk.l} onClick={lk.a || undefined} style={{ cursor: lk.a ? "pointer" : "default", opacity: lk.a ? 0.7 : 0.4 }}>{lk.l}</span>)}</div><p style={{ fontSize: 11, color: V.dim, opacity: 0.3 }}>&copy; 2026 G-Pal.ai - AI-generated companion service. 18+ only.</p></footer>; }

function LegalPage({ title, content, onBack }) { return <div style={{ minHeight: "100vh", padding: "80px 24px 60px" }}><div style={{ maxWidth: 640, margin: "0 auto" }}><button onClick={onBack} style={{ background: "none", border: "none", color: V.dim, cursor: "pointer", fontSize: 13, fontFamily: V.sans, marginBottom: 24, padding: 0 }}>&larr; Back</button><h1 style={{ fontFamily: V.serif, fontSize: 32, fontWeight: 400, color: V.text, marginBottom: 8 }}>{title}</h1><p style={{ fontSize: 13, color: V.dim, marginBottom: 32 }}>Last updated: 2026</p><div style={{ ...css.card, whiteSpace: "pre-wrap", fontSize: 13.5, color: V.dim, lineHeight: 1.75, maxHeight: "70vh", overflowY: "auto" }}>{content}</div></div></div>; }

function Shell({ step, total, title, sub, children, onBack }) { return <div style={{ minHeight: "100vh", display: "flex", flexDirection: "column", alignItems: "center", padding: "80px 24px 40px" }}><div style={{ width: "100%", maxWidth: 480 }}><div style={{ display: "flex", gap: 4, marginBottom: 40 }}>{Array.from({ length: total }).map((_, i) => <div key={i} style={{ flex: 1, height: 2, borderRadius: 1, background: i < step ? V.accent : V.border }} />)}</div>{onBack && <button onClick={onBack} style={{ background: "none", border: "none", color: V.dim, cursor: "pointer", fontSize: 13, fontFamily: V.sans, marginBottom: 20, padding: 0 }}>&larr; Back</button>}<h2 style={{ fontFamily: V.serif, fontSize: 28, fontWeight: 400, color: V.text, marginBottom: sub ? 8 : 28 }}>{title}</h2>{sub && <p style={{ color: V.dim, fontSize: 14, marginBottom: 32, lineHeight: 1.5 }}>{sub}</p>}{children}</div></div>; }

function StepAge({ onNext }) { const [c, setC] = useState([false, false, false]); const t = i => { const n = [...c]; n[i] = !n[i]; setC(n); }; const labels = ["I am 18 years of age or older", "I understand this is an AI, not a real person", "I consent to adult-oriented AI content"]; return <Shell step={1} total={8} title="Before we begin"><div style={{ display: "grid", gap: 8, marginBottom: 28 }}>{labels.map((l, i) => <div key={i} onClick={() => t(i)} style={{ ...css.card, padding: "14px 16px", cursor: "pointer", display: "flex", gap: 14, alignItems: "center", borderColor: c[i] ? V.accent : V.border }}><div style={{ width: 20, height: 20, borderRadius: 4, border: c[i] ? "none" : `1.5px solid ${V.border}`, background: c[i] ? V.accent : "transparent", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}>{c[i] && <CheckIcon />}</div><span style={{ fontSize: 14, color: V.text }}>{l}</span></div>)}</div><button onClick={onNext} disabled={!c.every(Boolean)} style={{ ...css.btn(true), width: "100%", opacity: c.every(Boolean) ? 1 : 0.35 }}>Continue</button></Shell>; }

function StepConsent({ onNext, onBack, onShowTerms }) { const [a, setA] = useState(false); return <Shell step={2} total={8} title="Disclosures" sub="Please read and accept." onBack={onBack}><div style={{ ...css.card, marginBottom: 20, maxHeight: 240, overflow: "auto", fontSize: 13.5, color: V.dim, lineHeight: 1.7 }}><p style={{ fontWeight: 600, color: V.text, marginBottom: 10 }}>By using G-Pal.ai you acknowledge:</p><p style={{ marginBottom: 8 }}>- Your companion is entirely AI-generated.</p><p style={{ marginBottom: 8 }}>- This service is for entertainment only.</p><p style={{ marginBottom: 8 }}>- Not a substitute for professional mental health support.</p><p style={{ marginBottom: 8 }}>- Crisis? Contact 1-833-456-4566 (CA) or 988 (US).</p><p style={{ marginBottom: 8 }}>- SMS rates from your carrier may apply.</p><p style={{ marginBottom: 8 }}>- You may delete your data or cancel anytime.</p><p>- By continuing, you agree to our <span onClick={(e) => { e.stopPropagation(); onShowTerms("terms"); }} style={{ color: V.accent, cursor: "pointer", textDecoration: "underline" }}>Terms & Conditions</span>, <span onClick={(e) => { e.stopPropagation(); onShowTerms("privacy"); }} style={{ color: V.accent, cursor: "pointer", textDecoration: "underline" }}>Privacy Policy</span>, and <span onClick={(e) => { e.stopPropagation(); onShowTerms("safety"); }} style={{ color: V.accent, cursor: "pointer", textDecoration: "underline" }}>Safety Policy</span>.</p></div><div onClick={() => setA(!a)} style={{ ...css.card, padding: "14px 16px", cursor: "pointer", display: "flex", gap: 14, alignItems: "center", marginBottom: 20, borderColor: a ? V.accent : V.border }}><div style={{ width: 20, height: 20, borderRadius: 4, border: a ? "none" : `1.5px solid ${V.border}`, background: a ? V.accent : "transparent", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}>{a && <CheckIcon />}</div><span style={{ fontSize: 14, color: V.text }}>I accept all disclosures and the <span onClick={(e) => { e.stopPropagation(); onShowTerms("terms"); }} style={{ color: V.accent, textDecoration: "underline", cursor: "pointer" }}>Terms & Conditions</span></span></div><button onClick={onNext} disabled={!a} style={{ ...css.btn(true), width: "100%", opacity: a ? 1 : 0.35 }}>Continue</button></Shell>; }

function StepAccount({ onNext, onBack }) { const [e, setE] = useState(""); const [p, setP] = useState(""); const [l, setL] = useState("en"); const [vm, setVm] = useState("none"); const [vc, setVc] = useState(""); const langs = [["en", "English", "Your companion will text you in English"], ["fr", "Fran\u00E7ais", "Votre compagnon vous \u00E9crira en fran\u00E7ais"]]; return <Shell step={3} total={8} title="Create your account" onBack={onBack}><div style={{ display: "grid", gap: 16, marginBottom: 24 }}><div><label style={css.label}>Email</label><div style={{ display: "flex", gap: 8 }}><input type="email" value={e} onChange={ev => setE(ev.target.value)} placeholder="you@email.com" style={{ ...css.input, flex: 1 }} />{vm === "none" && <button onClick={() => { if (e) setVm("sent"); }} disabled={!e} style={{ ...css.btn(false), padding: "10px 16px", fontSize: 12, whiteSpace: "nowrap", opacity: e ? 1 : 0.35 }}>Verify</button>}{vm === "verified" && <div style={{ display: "flex", alignItems: "center", padding: "0 12px", fontSize: 12, color: V.accent, fontWeight: 600 }}>Verified</div>}</div>{vm === "sent" && <div style={{ marginTop: 10 }}><div style={{ display: "flex", gap: 8 }}><input value={vc} onChange={ev => setVc(ev.target.value)} placeholder="Enter 6-digit code" maxLength={6} style={{ ...css.input, flex: 1, fontSize: 14 }} /><button onClick={() => { if (vc.length >= 6) setVm("verified"); }} disabled={vc.length < 6} style={{ ...css.btn(true), padding: "10px 16px", fontSize: 12, opacity: vc.length >= 6 ? 1 : 0.35 }}>Confirm</button></div><p style={{ fontSize: 11, color: V.dim, opacity: 0.6, marginTop: 6 }}>A verification code has been sent to your email via Supabase.</p></div>}</div><div><label style={css.label}>Password</label><input type="password" value={p} onChange={ev => setP(ev.target.value)} placeholder="Create a secure password" style={css.input} /></div><div><label style={css.label}>Language <span style={{ textTransform: "none", fontWeight: 400, opacity: 0.6 }}>(your companion will text in this language)</span></label><div style={{ display: "grid", gap: 8 }}>{langs.map(([v, lb, desc]) => <div key={v} onClick={() => setL(v)} style={{ padding: "14px 16px", borderRadius: 6, cursor: "pointer", border: l === v ? `1px solid ${V.accent}` : `1px solid ${V.border}` }}><div style={{ fontSize: 14, fontWeight: 500, color: l === v ? V.accent : V.text, marginBottom: 2 }}>{lb}</div><div style={{ fontSize: 12, color: V.dim, opacity: 0.7 }}>{desc}</div></div>)}</div></div></div><button onClick={onNext} disabled={!e || !p} style={{ ...css.btn(true), width: "100%", opacity: e && p ? 1 : 0.35 }}>Continue</button></Shell>; }

function StepBuilder({ onNext, onBack }) { const [name, setName] = useState(""); const [pers, setPers] = useState([]); const [tones, setTones] = useState([]); const [ints, setInts] = useState([]); const [freq, setFreq] = useState("balanced"); const pill = (sel) => ({ padding: "8px 16px", borderRadius: 4, cursor: "pointer", fontSize: 13, fontWeight: 500, border: sel ? `1px solid ${V.accent}` : `1px solid ${V.border}`, color: sel ? V.accent : V.dim, background: "transparent" }); const toggleTone = (t) => { if (tones.includes(t)) setTones(tones.filter(x => x !== t)); else if (tones.length < 2) setTones([...tones, t]); }; return <Shell step={4} total={8} title="Build your companion" sub="Make her yours." onBack={onBack}><div style={{ display: "grid", gap: 24, marginBottom: 28 }}><div><label style={css.label}>Her Name</label><input value={name} onChange={e => setName(e.target.value)} placeholder="Choose a name" style={css.input} /></div><div><label style={css.label}>Personality (up to 3)</label><div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>{PERSONALITIES.map(p => { const s = pers.includes(p.id); return <div key={p.id} onClick={() => { if (s) setPers(pers.filter(x => x !== p.id)); else if (pers.length < 3) setPers([...pers, p.id]); }} style={pill(s)}>{p.label}</div>; })}</div></div><div><label style={css.label}>Tone (choose 2)</label><div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>{TONES.map(t => <div key={t} onClick={() => toggleTone(t)} style={pill(tones.includes(t))}>{t}</div>)}</div>{tones.length > 0 && <p style={{ fontSize: 12, color: V.dim, opacity: 0.6, marginTop: 8 }}>Selected: {tones.join(" & ")}{tones.length < 2 ? " \u2014 pick one more" : ""}</p>}</div><div><label style={css.label}>Your Interests</label><div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>{INTERESTS.map(t => <div key={t} onClick={() => setInts(ints.includes(t) ? ints.filter(x => x !== t) : [...ints, t])} style={pill(ints.includes(t))}>{t}</div>)}</div></div><div><label style={css.label}>Text Frequency</label><div style={{ display: "flex", gap: 8 }}>{[["minimal", "Minimal"], ["balanced", "Balanced"], ["frequent", "Frequent"]].map(([v, l]) => <div key={v} onClick={() => setFreq(v)} style={{ flex: 1, padding: "10px", borderRadius: 6, textAlign: "center", cursor: "pointer", fontSize: 13, fontWeight: 500, border: freq === v ? `1px solid ${V.accent}` : `1px solid ${V.border}`, color: freq === v ? V.accent : V.dim }}>{l}</div>)}</div></div></div><button onClick={onNext} disabled={!name || !pers.length || tones.length < 2} style={{ ...css.btn(true), width: "100%", opacity: name && pers.length && tones.length >= 2 ? 1 : 0.35 }}>Continue</button></Shell>; }

function StepPlan({ onNext, onBack, onSelectPlan }) { const [sel, setSel] = useState("premium"); return <Shell step={5} total={8} title="Choose your plan" onBack={onBack}><div style={{ display: "grid", gap: 8, marginBottom: 24 }}>{PLANS.map(p => <div key={p.id} onClick={() => setSel(p.id)} style={{ ...css.card, padding: "16px 18px", cursor: "pointer", display: "flex", justifyContent: "space-between", alignItems: "center", borderColor: sel === p.id ? V.accent : V.border }}><div><span style={{ fontFamily: V.serif, fontSize: 17, color: sel === p.id ? V.accent : V.text }}>{p.name}</span><div style={{ fontSize: 12, color: V.dim, marginTop: 2 }}>{p.features[0]}</div></div><span style={{ fontSize: 20, fontWeight: 700, fontFamily: V.sans, color: V.text }}>{p.price === 0 ? "Free" : `$${p.price}`}</span></div>)}</div><button onClick={() => { onSelectPlan(sel); onNext(); }} style={{ ...css.btn(true), width: "100%" }}>Continue with {PLANS.find(p => p.id === sel)?.name}</button></Shell>; }

function StepPayment({ onNext, onBack, selectedPlan }) { const plan = PLANS.find(p => p.id === selectedPlan) || PLANS[2]; const [cn, setCn] = useState(""); const [cc, setCc] = useState(""); const [ex, setEx] = useState(""); const [cv, setCv] = useState(""); if (plan.price === 0) return <Shell step={6} total={8} title="No payment needed" sub="You selected the free Explorer plan." onBack={onBack}><div style={{ ...css.card, textAlign: "center", marginBottom: 24 }}><p style={{ fontFamily: V.serif, fontSize: 22, color: V.text, marginBottom: 8 }}>Explorer</p><p style={{ fontSize: 34, fontWeight: 700, fontFamily: V.sans, color: V.text }}>Free</p><p style={{ fontSize: 13, color: V.dim, marginTop: 8 }}>10 messages/day · Basic setup</p></div><button onClick={onNext} style={{ ...css.btn(true), width: "100%" }}>Continue</button></Shell>; const ok = cn && cc.length >= 16 && ex && cv.length >= 3; return <Shell step={6} total={8} title="Payment" sub={`${plan.name} plan — $${plan.price}/mo`} onBack={onBack}><div style={{ ...css.card, marginBottom: 20 }}><div style={{ display: "flex", justifyContent: "space-between", marginBottom: 16 }}><span style={{ fontFamily: V.serif, fontSize: 18, color: V.text }}>{plan.name}</span><span style={{ fontSize: 18, fontWeight: 700, color: V.text }}>${plan.price}/mo</span></div><div style={{ fontSize: 12, color: V.dim, opacity: 0.6 }}>Auto-renews monthly. Cancel anytime.</div></div><div style={{ display: "grid", gap: 14, marginBottom: 24 }}><div><label style={css.label}>Name on Card</label><input value={cn} onChange={e => setCn(e.target.value)} placeholder="Full name" style={css.input} /></div><div><label style={css.label}>Card Number</label><input value={cc} onChange={e => setCc(e.target.value.replace(/\D/g, "").slice(0, 16))} placeholder="1234 5678 9012 3456" style={css.input} /></div><div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}><div><label style={css.label}>Expiry</label><input value={ex} onChange={e => setEx(e.target.value)} placeholder="MM/YY" maxLength={5} style={css.input} /></div><div><label style={css.label}>CVC</label><input value={cv} onChange={e => setCv(e.target.value.replace(/\D/g, "").slice(0, 4))} placeholder="123" maxLength={4} style={css.input} /></div></div></div><button onClick={onNext} disabled={!ok} style={{ ...css.btn(true), width: "100%", opacity: ok ? 1 : 0.35 }}>Subscribe — ${plan.price}/mo</button></Shell>; }

function StepPhone({ onNext, onBack }) { const [ph, setPh] = useState(""); const [code, setCode] = useState(""); const [sent, setSent] = useState(false); return <Shell step={7} total={8} title="Verify your phone" sub="This is where she'll text you." onBack={onBack}>{!sent ? <div style={{ display: "grid", gap: 16 }}><div><label style={css.label}>Phone Number</label><input type="tel" value={ph} onChange={e => setPh(e.target.value)} placeholder="+1 (555) 123-4567" style={css.input} /></div><p style={{ fontSize: 12, color: V.dim, opacity: 0.6 }}>We'll send a verification code via SMS.</p><button onClick={() => setSent(true)} disabled={!ph} style={{ ...css.btn(true), width: "100%", opacity: ph ? 1 : 0.35 }}>Send Code</button></div> : <div style={{ display: "grid", gap: 16 }}><p style={{ fontSize: 14, color: V.dim }}>Code sent to <span style={{ color: V.text, fontWeight: 600 }}>{ph}</span></p><input value={code} onChange={e => setCode(e.target.value)} placeholder="000000" maxLength={6} style={{ ...css.input, textAlign: "center", fontSize: 26, letterSpacing: 10, fontWeight: 700 }} /><button onClick={onNext} disabled={code.length < 6} style={{ ...css.btn(true), width: "100%", opacity: code.length >= 6 ? 1 : 0.35 }}>Verify</button><button onClick={() => setSent(false)} style={{ background: "none", border: "none", color: V.dim, cursor: "pointer", fontSize: 13, fontFamily: V.sans }}>Resend</button></div>}</Shell>; }

function StepWelcome({ onFinish }) { const [v, setV] = useState(false); useEffect(() => { setTimeout(() => setV(true), 200); }, []); return <Shell step={8} total={8} title=""><div style={{ textAlign: "center", paddingTop: 32, opacity: v ? 1 : 0, transform: v ? "none" : "translateY(16px)", transition: "all 0.7s ease" }}><p style={{ fontFamily: V.serif, fontSize: 34, color: V.text, marginBottom: 12 }}>You're all set</p><p style={{ fontSize: 15, color: V.dim, lineHeight: 1.6, marginBottom: 8 }}>She's sending your first text now.</p><p style={{ fontSize: 13, color: V.dim, opacity: 0.5, marginBottom: 36 }}>Check your phone.</p><div style={{ ...css.card, textAlign: "left", marginBottom: 32 }}><div style={{ fontSize: 11, color: V.dim, opacity: 0.5, marginBottom: 10 }}>Preview</div><div style={{ background: "rgba(255,255,255,0.03)", borderRadius: 8, padding: "12px 16px", fontSize: 14.5, color: V.dim, lineHeight: 1.5 }}>Hey. I've been looking forward to this. So tell me - what's on your mind right now?</div></div><button onClick={onFinish} style={{ ...css.btn(true), width: "100%" }}>Go to Dashboard</button></div></Shell>; }

function Dashboard({ onBack, onShowTerms }) { const [tab, setTab] = useState("Overview"); const tabs = ["Overview", "Companion", "Settings", "Billing"]; return <div style={{ minHeight: "100vh", padding: "80px 24px 40px" }}><div style={{ maxWidth: 520, margin: "0 auto" }}><div style={{ display: "flex", gap: 0, marginBottom: 36, borderBottom: `1px solid ${V.border}` }}>{tabs.map(t => <div key={t} onClick={() => setTab(t)} style={{ padding: "10px 16px", cursor: "pointer", fontSize: 13, fontWeight: 500, color: tab === t ? V.accent : V.dim, borderBottom: tab === t ? `1.5px solid ${V.accent}` : "1.5px solid transparent", marginBottom: -1 }}>{t}</div>)}</div>{tab === "Overview" && <div style={{ display: "grid", gap: 12 }}><div onClick={() => setTab("Billing")} style={{ ...css.card, cursor: "pointer" }}><p style={css.label}>Plan</p><p style={{ fontFamily: V.serif, fontSize: 22, color: V.text }}>Intimacy</p><p style={{ fontSize: 13, color: V.dim, marginTop: 4 }}>$24.99/mo · Renews May 6</p><p style={{ fontSize: 11, color: V.accent, marginTop: 8, opacity: 0.7 }}>Click to manage billing →</p></div><div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}><div style={css.card}><p style={css.label}>Messages Today</p><p style={{ fontSize: 28, fontWeight: 700, fontFamily: V.sans, color: V.text }}>47<span style={{ fontSize: 14, fontWeight: 400, color: V.dim }}>/60</span></p></div><div style={css.card}><p style={css.label}>Last Active</p><p style={{ fontSize: 16, fontWeight: 600, color: V.text, marginTop: 8 }}>2 min ago</p></div></div><div style={css.card}><p style={css.label}>Phone</p><div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}><span style={{ fontSize: 15, color: V.text }}>+1 (514) 555-1234</span><span style={{ fontSize: 12, color: V.accent, fontWeight: 600 }}>Verified</span></div></div></div>}{tab === "Companion" && <div style={css.card}><p style={{ fontFamily: V.serif, fontSize: 22, color: V.text, marginBottom: 4 }}>Luna</p><p style={{ fontSize: 13, color: V.dim, marginBottom: 20 }}>Sweet · Romantic · Affectionate</p>{[["Tones", "Warm & Caring"], ["Frequency", "Balanced"], ["Language", "English"], ["Quiet Hours", "11 PM - 7 AM"]].map(([k, v]) => <div key={k} style={{ display: "flex", justifyContent: "space-between", padding: "12px 0", borderTop: `1px solid ${V.border}` }}><span style={{ fontSize: 13, color: V.dim }}>{k}</span><span style={{ fontSize: 13, color: V.text, fontWeight: 500 }}>{v}</span></div>)}<button style={{ ...css.btn(false), width: "100%", marginTop: 16, fontSize: 13 }}>Edit Companion</button></div>}{tab === "Settings" && <div style={{ display: "grid", gap: 4 }}>{["Privacy Settings", "Conversation Boundaries", "Topic Restrictions", "Pause Messaging", "Download My Data", "Delete Account"].map(item => <div key={item} style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "14px 0", borderBottom: `1px solid ${V.border}`, cursor: "pointer" }}><span style={{ fontSize: 14, color: V.text }}>{item}</span><span style={{ color: V.dim, fontSize: 14 }}>→</span></div>)}{[["Terms & Conditions", "terms"], ["Privacy Policy", "privacy"], ["Safety Policy", "safety"]].map(([label, type]) => <div key={type} onClick={() => onShowTerms(type)} style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "14px 0", borderBottom: `1px solid ${V.border}`, cursor: "pointer" }}><span style={{ fontSize: 14, color: V.text }}>{label}</span><span style={{ color: V.dim, fontSize: 14 }}>→</span></div>)}</div>}{tab === "Billing" && <div style={{ display: "grid", gap: 12 }}><div style={css.card}><p style={css.label}>Active Plan</p><p style={{ fontFamily: V.serif, fontSize: 20, color: V.text, marginBottom: 12 }}>Intimacy - $24.99/mo</p><div style={{ display: "flex", gap: 8 }}><button style={{ ...css.btn(false), fontSize: 12, padding: "8px 16px" }}>Change Plan</button><button style={{ ...css.btn(false), fontSize: 12, padding: "8px 16px", color: V.dim, opacity: 0.6 }}>Cancel</button></div></div><div style={css.card}><p style={css.label}>Payment</p><p style={{ fontSize: 14, color: V.text }}>Visa ending in 4242</p><p style={{ fontSize: 12, color: V.dim, marginTop: 4 }}>Next charge: May 6, 2026</p></div></div>}<div style={{ marginTop: 48, textAlign: "center" }}><button onClick={onBack} style={{ background: "none", border: "none", color: V.dim, cursor: "pointer", fontSize: 13, fontFamily: V.sans, opacity: 0.5 }}>&larr; Back to homepage</button></div></div></div>; }

function GPalApp() {
  const [ageOk, setAgeOk] = useState(false);
  const [ageDenied, setAgeDenied] = useState(false);
  const [page, setPage] = useState("home");
  const [step, setStep] = useState(1);
  const [selectedPlan, setSelectedPlan] = useState("premium");
  const [prevPage, setPrevPage] = useState("home");

  const go = () => { setPage("onboarding"); setStep(1); window.scrollTo(0, 0); };
  const next = () => { setStep(s => s + 1); window.scrollTo(0, 0); };
  const back = () => { setStep(s => s - 1); window.scrollTo(0, 0); };
  const nav = p => { setPage(p); window.scrollTo(0, 0); };
  const showLegal = (type) => { setPrevPage(page); setPage(type); window.scrollTo(0, 0); };
  const backFromLegal = () => { setPage(prevPage); window.scrollTo(0, 0); };

  return (
    <div style={{ background: V.bg, color: V.text, fontFamily: V.sans, minHeight: "100vh" }}>
      <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@300;400;500;600&family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
      {!ageOk && !ageDenied && <AgeGatePopup onConfirm={() => setAgeOk(true)} onDeny={() => setAgeDenied(true)} />}
      {ageDenied && <AgeGateDenied />}
      {ageOk && <div>
        <Navbar onNavigate={p => { if (p === "home") nav("home"); else if (p === "onboarding") go(); else nav(p); }} />
        {page === "home" && <><Hero onStart={go} /><HowItWorks /><WhySMS /><PricingSection onSelect={go} /><SafetySection /><FAQSection /><FinalCTA onStart={go} /><Footer onShowTerms={() => showLegal("terms")} onShowPrivacy={() => showLegal("privacy")} onShowSafety={() => showLegal("safety")} /></>}
        {page === "terms" && <LegalPage title="Terms & Conditions" content={TERMS_CONTENT} onBack={backFromLegal} />}
        {page === "privacy" && <LegalPage title="Privacy Policy" content={PRIVACY_CONTENT} onBack={backFromLegal} />}
        {page === "safety" && <LegalPage title="Safety Policy" content={SAFETY_CONTENT} onBack={backFromLegal} />}
        {page === "onboarding" && <>{step === 1 && <StepAge onNext={next} />}{step === 2 && <StepConsent onNext={next} onBack={back} onShowTerms={showLegal} />}{step === 3 && <StepAccount onNext={next} onBack={back} />}{step === 4 && <StepBuilder onNext={next} onBack={back} />}{step === 5 && <StepPlan onNext={next} onBack={back} onSelectPlan={setSelectedPlan} />}{step === 6 && <StepPayment onNext={next} onBack={back} selectedPlan={selectedPlan} />}{step === 7 && <StepPhone onNext={next} onBack={back} />}{step === 8 && <StepWelcome onFinish={() => nav("dashboard")} />}</>}
        {page === "dashboard" && <Dashboard onBack={() => nav("home")} onShowTerms={showLegal} />}
      </div>}
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<GPalApp />);



