/* Mindful Balance — Baseline Quiz Funnel (IG ad conversion test)
 * Hybrid voice: clinical authority on the reveal, warm voice everywhere else.
 * All variants exposed as Tweaks for live A/B testing.
 */

const COLORS = {
  bg: '#FBF9F6',
  surface: '#EFECE6',
  raised: '#FFFFFF',
  ink: '#2D2B2A',
  ink500: '#5B5754',
  ink300: '#8E8A85',
  ink200: '#BFB9B1',
  hair: 'rgba(45,43,42,0.08)',
  border: 'rgba(45,43,42,0.14)',
  sage: '#A3B19B',
  sageDeep: '#6F8067',
  sageTint: '#E6EBE2',
  terra: '#D9A080',
  terraDeep: '#B5704F',
  terraTint: '#F4DFD0',
  ochre: '#E3C16F',
  ochreDeep: '#A88638',
  ochreTint: '#F4E6BD',
};

// ─── Brand mark (Bisect) — small inline lockup used in headers ─
function Bisect({ size = 24 }) {
  const id = React.useId();
  return (
    <svg width={size} height={size} viewBox="0 0 48 48" fill="none">
      <defs>
        <clipPath id={id + '-t'}><rect x="0" y="0" width="48" height="22"/></clipPath>
        <clipPath id={id + '-b'}><rect x="0" y="22" width="48" height="26"/></clipPath>
      </defs>
      <circle cx="24" cy="24" r="18" fill={COLORS.terra} clipPath={`url(#${id}-t)`}/>
      <circle cx="24" cy="24" r="18" fill={COLORS.sage} clipPath={`url(#${id}-b)`}/>
      <line x1="2" y1="22" x2="46" y2="22" stroke={COLORS.ink} strokeWidth="2.4" strokeLinecap="round"/>
    </svg>
  );
}

function HeaderLockup({ markSize = 22, wordSize = 16 }) {
  return (
    <div style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
      <Bisect size={markSize}/>
      <span style={{
        fontFamily: "'Roboto', system-ui, sans-serif",
        fontWeight: 600, fontSize: wordSize, letterSpacing: '-0.025em',
        color: COLORS.ink, lineHeight: 1,
      }}>baseline</span>
    </div>
  );
}

// ─── Tracking ──────────────────────────────────────────────
// Wire real IDs into <head> in the index.html. Falls back to console.
// Replace XX / X.X with real numbers once beta data exists. Don't ship
// fabricated stats — they're the fastest way to lose trust.
function track(event, props = {}) {
  const variantLabel = window.__VARIANT ? (window.__VARIANT.label || JSON.stringify(window.__VARIANT)) : undefined;
  // utm_content captures which hook angle drove the click (set on the IG ad URL)
  let hook;
  try {
    const u = new URLSearchParams(location.search);
    hook = u.get('utm_content') || u.get('hook') || undefined;
  } catch (e) {}
  const enriched = { ...props };
  if (variantLabel) enriched.variant = variantLabel;
  if (hook) enriched.hook = hook;
  // eslint-disable-next-line no-console
  console.log('%c[track]', 'color:#6F8067;font-weight:600', event, enriched);
  try { if (window.fbq) window.fbq('trackCustom', event, enriched); } catch (e) {}
  try { if (window.gtag) window.gtag('event', event, enriched); } catch (e) {}

  // ── Standard Meta events for ad optimisation ──────────────
  // Meta's algorithm optimises delivery based on these standard event types.
  // These fire IN ADDITION to the custom events above (Meta deduplicates by eventID).
  try {
    if (window.fbq) {
      const p = enriched;
      if (event === 'ViewReveal')    fbq('track', 'ViewContent',      { content_name: p.band || 'Baseline Score' });
      if (event === 'ViewPaywall')   fbq('track', 'InitiateCheckout', { value: p.price, currency: 'USD', num_items: 1 });
      if (event === 'ClickPurchase') fbq('track', 'AddToCart',        { value: p.price, currency: 'USD' });
      if (event === 'BetaReserved')  fbq('track', 'Lead');
    }
  } catch (e) {}

  window.__eventLog ||= [];
  window.__eventLog.push({ event, props: enriched, t: Date.now() });
  window.dispatchEvent(new CustomEvent('quiz-track', { detail: { event, props: enriched } }));
}

// ─── Quiz data ─────────────────────────────────────────────
const QUESTIONS_WARM = [
  {
    title: "How soon after waking do you reach for your phone?",
    options: [
      { text: "Under 5 minutes",    score: 0 },
      { text: "5 to 30 minutes",    score: 10 },
      { text: "Over 30 minutes",    score: 20 },
    ],
    caption: "Anonymous. Your answers stay on this device.",
  },
  {
    title: "When you're trying to focus, how often do you switch tabs or check notifications?",
    options: [
      { text: "Every few minutes",         score: 0 },
      { text: "Only on planned breaks",    score: 10 },
      { text: "I can focus for hours",     score: 20 },
    ],
    caption: "Frequent task-switching quietly drains your daily cognitive reserve.",
  },
  {
    title: "When a task gets hard or boring, what do you usually do?",
    options: [
      { text: "Drop it entirely",          score: 0 },
      { text: "Open another app or tab",   score: 10 },
      { text: "Push through the friction", score: 20 },
    ],
    caption: "Low tolerance for boredom is a sign your brain is hunting for cheap rewards.",
  },
  {
    title: "What does your last hour before sleep look like?",
    options: [
      { text: "Scrolling or streaming",      score: 0 },
      { text: "Reading or journaling",       score: 10 },
      { text: "Screen-free wind-down",       score: 20 },
    ],
    caption: "Late-night stimulation suppresses melatonin and disrupts overnight recovery.",
  },
  {
    title: "What's the one thing you most want to change?",
    options: [
      { text: "Stop compulsive phone-checking",  score: 10, type: "Dopamine Budgeting" },
      { text: "Stay focused for longer stretches", score: 10, type: "Context-Switching Shields" },
      { text: "Clear the brain fog and fatigue",   score: 10, type: "Circadian Realignment" },
    ],
    caption: "Your protocol is tuned to your primary friction point.",
  },
];

const QUESTIONS_CLINICAL = [
  { title: "How long after waking do you perform your first digital check-in?",
    options: [
      { text: "Under 5 minutes", score: 0 },
      { text: "5 to 30 minutes", score: 10 },
      { text: "Over 30 minutes", score: 20 },
    ],
    caption: "*Anonymous and encrypted. Your data remains recognizable only to you." },
  { title: "When working or studying, how often do you switch tabs or check notifications?",
    options: [
      { text: "Every few minutes", score: 0 },
      { text: "Only during planned breaks", score: 10 },
      { text: "Hyper-focused for hours", score: 20 },
    ],
    caption: "*Frequent task-switching induces context-switching tax, rapidly draining your daily cognitive reserve." },
  { title: "When encountering a complex problem or boring task, what is your immediate response?",
    options: [
      { text: "Abandon the task entirely", score: 0 },
      { text: "Open a secondary screen or app", score: 10 },
      { text: "Push through the friction", score: 20 },
    ],
    caption: "*Low boredom tolerance indicates low baseline dopamine, causing the brain to seek cheap rewards." },
  { title: "What does your final hour before sleep look like?",
    options: [
      { text: "Active scrolling or streaming", score: 0 },
      { text: "Passive reading or journaling", score: 10 },
      { text: "Screen-free rest", score: 20 },
    ],
    caption: "*Late-night high-stimulation content suppresses melatonin and disrupts neurological recovery." },
  { title: "What is your primary optimization objective?",
    options: [
      { text: "Eliminate compulsive phone usage", score: 10, type: "Dopamine Budgeting" },
      { text: "Extend deep focus intervals", score: 10, type: "Context-Switching Shields" },
      { text: "Clear brain fog and constant fatigue", score: 10, type: "Circadian Realignment" },
    ],
    caption: "*Targeted behavioral interventions are calibrated to your primary friction point." },
];

const HOOK_HEADLINES = {
  curiosity: { eyebrow: "60-second assessment", h: "What's your attention baseline?",        sub: "A quick read of where your focus actually stands." },
  pain:      { eyebrow: "If you can relate…",   h: "Can't focus for more than 10 minutes?", sub: "Find out what's draining your attention budget." },
  identity:  { eyebrow: "For people who",       h: "used to be able to read a book.",       sub: "60 seconds to see what's changed and what to do about it." },
  antitech:  { eyebrow: "It's not willpower.",  h: "It's a dopamine problem.",              sub: "See your baseline, then the protocol to recalibrate it." },
};

const LOADING_LINES = {
  clinical: [
    "Analyzing neurochemical baseline…",
    "Isolating cognitive friction points…",
    "Generating your 12-week protocol…",
  ],
  warm: [
    "Reading your answers…",
    "Looking for patterns…",
    "Building your starting plan…",
  ],
};

function getProfile(score) {
  if (score <= 40) return {
    title: "Attention Baseline: Saturated",
    color: COLORS.terraDeep,
    band: 'low',
    analysis: "Your profile shows high adaptation to micro-stimuli. Your brain has started expecting a hit every few minutes. Capacity is intact; the budget is just mismanaged. Start here.",
    intervention: "Dopamine Budgeting",
  };
  if (score <= 70) return {
    title: "Attention Baseline: Miscalibrated",
    color: COLORS.ochreDeep,
    band: 'mid',
    analysis: "You're capable of deep work, but the friction points in your day are quietly burning through your reserve. The fix is structural, not motivational.",
    intervention: "Context-Switching Shields",
  };
  return {
    title: "Attention Baseline: Regulated",
    color: COLORS.sageDeep,
    band: 'high',
    analysis: "Your defenses against micro-stimuli are strong. To close the final gap, we focus on autonomic recovery and sleep architecture.",
    intervention: "Circadian Realignment",
  };
}

// ─── Shared UI ─────────────────────────────────────────────
function Eyebrow({ children, color = COLORS.ink300 }) {
  return <div style={{
    fontSize: 11, letterSpacing: '0.12em', textTransform: 'uppercase',
    fontWeight: 600, color, marginBottom: 12,
  }}>{children}</div>;
}

function PrimaryButton({ children, onClick, disabled, full = true }) {
  return (
    <button onClick={onClick} disabled={disabled} style={{
      background: disabled ? COLORS.ink200 : COLORS.sageDeep,
      color: '#FBF9F6',
      border: 'none', borderRadius: 12,
      padding: '15px 20px',
      fontFamily: 'inherit', fontSize: 15, fontWeight: 500,
      cursor: disabled ? 'not-allowed' : 'pointer',
      width: full ? '100%' : 'auto',
      transition: 'transform 80ms cubic-bezier(0.22,0.61,0.36,1), opacity 200ms',
      opacity: disabled ? 0.6 : 1,
    }}
    onMouseDown={e => !disabled && (e.currentTarget.style.transform = 'scale(0.98)')}
    onMouseUp={e => (e.currentTarget.style.transform = 'scale(1)')}
    onMouseLeave={e => (e.currentTarget.style.transform = 'scale(1)')}>
      {children}
    </button>
  );
}

function ProgressDots({ step, total }) {
  return (
    <div style={{ display: 'flex', gap: 6, padding: '0 4px' }}>
      {Array.from({ length: total }, (_, i) => (
        <div key={i} style={{
          flex: 1, height: 3, borderRadius: 999,
          background: i <= step ? COLORS.sageDeep : COLORS.ink100 || '#DDD8D0',
          transition: 'background 220ms cubic-bezier(0.22,0.61,0.36,1)',
        }}/>
      ))}
    </div>
  );
}

function BackButton({ onClick }) {
  return (
    <button onClick={onClick} aria-label="Back" style={{
      background: 'transparent', border: 'none', padding: 8, cursor: 'pointer',
      color: COLORS.ink500, display: 'inline-flex',
    }}>
      <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round">
        <path d="m15 6-6 6 6 6"/>
      </svg>
    </button>
  );
}

// ─── Screens ───────────────────────────────────────────────
function IntroScreen({ tweaks, onStart }) {
  const hook = HOOK_HEADLINES[tweaks.hookAngle] || HOOK_HEADLINES.curiosity;
  React.useEffect(() => { track('ViewIntro', { angle: tweaks.hookAngle }); }, [tweaks.hookAngle]);

  return (
    <div style={{ padding: '24px 24px 24px', display: 'flex', flexDirection: 'column', height: '100%', boxSizing: 'border-box' }}>
      <div style={{ paddingTop: 4, paddingBottom: 24 }}>
        <HeaderLockup/>
      </div>
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
        <Eyebrow>{hook.eyebrow}</Eyebrow>
        <h1 style={{
          fontSize: 32, lineHeight: 1.15, fontWeight: 700, letterSpacing: '-0.015em',
          color: COLORS.ink, margin: '0 0 14px',
        }}>{hook.h}</h1>
        <p style={{ fontSize: 16, lineHeight: 1.5, color: COLORS.ink500, margin: 0, maxWidth: 320 }}>{hook.sub}</p>

        <div style={{ marginTop: 36, display: 'flex', flexDirection: 'column', gap: 10 }}>
          {[
            ['5 questions', '60 seconds'],
            ['Get a score', '0–100 attention baseline'],
            ['See your weak point', 'and one thing to do about it'],
          ].map(([a,b]) => (
            <div key={a} style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <div style={{
                width: 22, height: 22, borderRadius: 999, background: COLORS.sageTint,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                color: COLORS.sageDeep, fontSize: 13, fontWeight: 700, flexShrink: 0,
              }}>✓</div>
              <div style={{ fontSize: 14, color: COLORS.ink }}>
                <span style={{ fontWeight: 500 }}>{a}</span>
                <span style={{ color: COLORS.ink500 }}> · {b}</span>
              </div>
            </div>
          ))}
        </div>
      </div>

      <PrimaryButton onClick={() => { track('ClickStart'); onStart(); }}>Start the assessment</PrimaryButton>
      <div style={{ textAlign: 'center', fontSize: 12, color: COLORS.ink300, marginTop: 10 }}>Anonymous. No account needed.</div>
    </div>
  );
}

function QuizScreen({ tweaks, step, total, data, prev, onAnswer, onBack }) {
  const [locked, setLocked] = React.useState(false);
  const [picked, setPicked] = React.useState(null);

  React.useEffect(() => {
    track('ViewQuestion', { step: step + 1 });
    setLocked(false);
    setPicked(prev != null ? prev : null);
  }, [step]);

  function pick(opt, i) {
    if (locked) return;
    setLocked(true);
    setPicked(i);
    track('AnswerQuestion', { step: step + 1, score: opt.score, type: opt.type || null });
    setTimeout(() => onAnswer(opt), 380);
  }

  return (
    <div style={{ padding: '24px 24px', display: 'flex', flexDirection: 'column', height: '100%', boxSizing: 'border-box' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 28 }}>
        <BackButton onClick={onBack}/>
        <div style={{ flex: 1 }}><ProgressDots step={step} total={total}/></div>
        <div style={{ minWidth: 36, textAlign: 'right', fontSize: 12, color: COLORS.ink300, fontVariantNumeric: 'tabular-nums' }}>{step + 1}/{total}</div>
      </div>

      <h1 style={{
        fontSize: 22, lineHeight: 1.3, fontWeight: 600, letterSpacing: '-0.005em',
        color: COLORS.ink, margin: '0 0 24px',
      }}>{data.title}</h1>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 10, flex: 1 }}>
        {data.options.map((opt, i) => {
          const selected = picked === i;
          return (
            <button key={i} onClick={() => pick(opt, i)} style={{
              textAlign: 'left',
              background: selected ? COLORS.sageTint : COLORS.surface,
              border: '2px solid', borderColor: selected ? COLORS.sageDeep : 'transparent',
              borderRadius: 12, padding: '15px 16px',
              fontFamily: 'inherit', fontSize: 15, color: COLORS.ink,
              cursor: 'pointer', transition: 'all 180ms cubic-bezier(0.22,0.61,0.36,1)',
            }}>{opt.text}</button>
          );
        })}
      </div>

      <div style={{ fontSize: 12, lineHeight: 1.4, color: COLORS.ink300, textAlign: 'center', marginTop: 18, padding: '0 8px' }}>
        {data.caption}
      </div>
    </div>
  );
}

function EmailGate({ tweaks, onSubmit, onSkip }) {
  const [email, setEmail] = React.useState('');
  const [touched, setTouched] = React.useState(false);
  React.useEffect(() => { track('ViewEmailGate'); }, []);
  const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim());

  return (
    <div style={{ padding: '64px 24px 24px', display: 'flex', flexDirection: 'column', height: '100%', boxSizing: 'border-box' }}>
      <div style={{ flex: 1 }}>
        <Eyebrow>One more thing</Eyebrow>
        <h1 style={{ fontSize: 28, lineHeight: 1.2, fontWeight: 700, letterSpacing: '-0.01em', color: COLORS.ink, margin: '0 0 12px' }}>
          Where should we send your baseline?
        </h1>
        <p style={{ fontSize: 15, lineHeight: 1.5, color: COLORS.ink500, margin: '0 0 24px' }}>
          You'll get the full report plus the first week of your protocol, free. Unsubscribe anytime.
        </p>

        <label style={{ display: 'block' }}>
          <div style={{ fontSize: 12, color: COLORS.ink500, marginBottom: 6 }}>Email</div>
          <input
            type="email" placeholder="you@example.com"
            value={email} onChange={e => setEmail(e.target.value)} onBlur={() => setTouched(true)}
            style={{
              width: '100%', boxSizing: 'border-box',
              padding: '14px 14px', borderRadius: 12,
              border: '1px solid ' + (touched && !valid ? COLORS.terraDeep : COLORS.border),
              background: COLORS.raised, fontFamily: 'inherit', fontSize: 16, color: COLORS.ink,
              outline: 'none',
            }}/>
          {touched && !valid && (
            <div style={{ fontSize: 12, color: COLORS.terraDeep, marginTop: 6 }}>That doesn't look like a valid email.</div>
          )}
        </label>
      </div>

      <PrimaryButton disabled={!valid} onClick={() => { track('SubmitEmail'); onSubmit(email.trim()); }}>
        Show me my baseline
      </PrimaryButton>
      <button onClick={() => { track('SkipEmail'); onSkip(); }} style={{
        marginTop: 6, background: 'transparent', border: 'none', padding: 12,
        color: COLORS.ink500, fontSize: 13, fontFamily: 'inherit', cursor: 'pointer',
      }}>Skip, show me anyway</button>
    </div>
  );
}

function BetaSignup({ tweaks, score, onSubmit, onBack }) {
  const [email, setEmail] = React.useState('');
  const [touched, setTouched] = React.useState(false);
  React.useEffect(() => { track('ViewBetaSignup', { price: tweaks.price }); }, []);
  const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim());
  const price = Number(tweaks.price).toFixed(2);

  return (
    <div style={{ padding: '24px 24px', display: 'flex', flexDirection: 'column', height: '100%', boxSizing: 'border-box' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 28 }}>
        <BackButton onClick={onBack}/>
        <HeaderLockup/>
      </div>
      <div style={{ flex: 1 }}>
        <Eyebrow color={COLORS.terraDeep}>Hold on, one detail</Eyebrow>
        <h1 style={{ fontSize: 28, lineHeight: 1.18, fontWeight: 700, letterSpacing: '-0.01em', color: COLORS.ink, margin: '0 0 14px' }}>
          Baseline is in private beta.
        </h1>
        <p style={{ fontSize: 15, lineHeight: 1.55, color: COLORS.ink500, margin: '0 0 8px' }}>
          The full app ships <b style={{ color: COLORS.ink }}>late summer 2026</b>. We're not charging yet. Leave your email and we'll send your personalized protocol the moment it's live.
        </p>
        <p style={{ fontSize: 15, lineHeight: 1.55, color: COLORS.ink500, margin: '0 0 22px' }}>
          Your spot is locked at <b style={{ color: COLORS.ink }}>${price}</b> when the app launches.
        </p>

        <label style={{ display: 'block' }}>
          <div style={{ fontSize: 12, color: COLORS.ink500, marginBottom: 6 }}>Email</div>
          <input
            type="email" placeholder="you@example.com" autoFocus
            value={email} onChange={e => setEmail(e.target.value)} onBlur={() => setTouched(true)}
            style={{
              width: '100%', boxSizing: 'border-box',
              padding: '14px 14px', borderRadius: 12,
              border: '1px solid ' + (touched && !valid ? COLORS.terraDeep : COLORS.border),
              background: COLORS.raised, fontFamily: 'inherit', fontSize: 16, color: COLORS.ink,
              outline: 'none',
            }}/>
          {touched && !valid && (
            <div style={{ fontSize: 12, color: COLORS.terraDeep, marginTop: 6 }}>That doesn't look right.</div>
          )}
        </label>

        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10, marginTop: 18, padding: '12px 14px', background: COLORS.sageTint, borderRadius: 10 }}>
          <div style={{ width: 18, height: 18, borderRadius: 999, background: COLORS.sageDeep, color: COLORS.chalk || '#FBF9F6', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 11, fontWeight: 700, flexShrink: 0, marginTop: 2 }}>✓</div>
          <div style={{ fontSize: 13, lineHeight: 1.5, color: COLORS.ink }}>
            <b>No charge today.</b> We'll only collect payment once the app is in your hands and you've confirmed.
          </div>
        </div>
      </div>

      <PrimaryButton disabled={!valid} onClick={() => { track('SubmitBetaEmail', { price: tweaks.price, score }); onSubmit(email.trim()); }}>
        Reserve my spot
      </PrimaryButton>
      <div style={{ textAlign: 'center', fontSize: 12, color: COLORS.ink300, marginTop: 10 }}>
        We'll email you once, when the app ships. Unsubscribe anytime.
      </div>
    </div>
  );
}

function LoadingScreen({ tweaks, onDone }) {
  const lines = LOADING_LINES[tweaks.loadingCopy] || LOADING_LINES.warm;
  const [idx, setIdx] = React.useState(0);
  React.useEffect(() => {
    track('ViewLoading');
    const t1 = setTimeout(() => setIdx(1), 1400);
    const t2 = setTimeout(() => setIdx(2), 2800);
    const t3 = setTimeout(() => onDone(), 4400);
    return () => { clearTimeout(t1); clearTimeout(t2); clearTimeout(t3); };
  }, []);

  return (
    <div style={{ padding: '24px', display: 'flex', flexDirection: 'column', height: '100%', boxSizing: 'border-box', alignItems: 'center', justifyContent: 'center' }}>
      <div style={{ width: 120, height: 120, filter: 'url(#organic-fusion)', position: 'relative', marginBottom: 36 }}>
        <div className="drop drop-1" style={{ position: 'absolute', width: 40, height: 40, top: 40, left: 40, background: COLORS.sageDeep, borderRadius: '50%' }}/>
        <div className="drop drop-2" style={{ position: 'absolute', width: 40, height: 40, top: 40, left: 40, background: COLORS.sageDeep, borderRadius: '50%' }}/>
      </div>
      <div style={{ minHeight: 28, textAlign: 'center', position: 'relative', width: '100%' }}>
        {lines.map((line, i) => (
          <div key={i} style={{
            position: 'absolute', left: 0, right: 0,
            fontSize: 15, fontWeight: 500, color: COLORS.ink,
            opacity: i === idx ? 1 : 0,
            transition: 'opacity 600ms cubic-bezier(0.22,0.61,0.36,1)',
          }}>{line}</div>
        ))}
      </div>
    </div>
  );
}

function RevealScreen({ tweaks, score, onContinue }) {
  const profile = getProfile(score);
  React.useEffect(() => { track('ViewReveal', { score, band: profile.band }); }, []);

  // animate score count-up
  const [shown, setShown] = React.useState(0);
  React.useEffect(() => {
    const start = performance.now();
    const dur = 900;
    let raf;
    function step(t) {
      const p = Math.min(1, (t - start) / dur);
      const eased = 1 - Math.pow(1 - p, 3);
      setShown(Math.round(score * eased));
      if (p < 1) raf = requestAnimationFrame(step);
    }
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [score]);

  return (
    <div style={{ padding: '56px 24px 24px', display: 'flex', flexDirection: 'column', height: '100%', boxSizing: 'border-box' }}>
      <div style={{ flex: 1 }}>
        <div style={{ textAlign: 'center', marginBottom: 28 }}>
          <Eyebrow color={profile.color}>Your result</Eyebrow>
          <div style={{ fontSize: 18, fontWeight: 500, color: COLORS.ink }}>{profile.title}</div>
          <div style={{
            fontSize: 72, lineHeight: 1, fontWeight: 700, letterSpacing: '-0.025em',
            color: profile.color, marginTop: 10, fontVariantNumeric: 'tabular-nums',
          }}>{shown}<span style={{ color: COLORS.ink300, fontWeight: 500, fontSize: 28 }}> / 100</span></div>
        </div>

        {/* Spectrum bar */}
        <div style={{ background: COLORS.surface, borderRadius: 999, height: 8, position: 'relative', overflow: 'hidden', marginBottom: 6 }}>
          <div style={{
            position: 'absolute', left: 0, top: 0, bottom: 0,
            width: `${Math.max(4, Math.min(100, score))}%`,
            background: profile.color,
            transition: 'width 900ms cubic-bezier(0.22,0.61,0.36,1)',
          }}/>
        </div>
        <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11, color: COLORS.ink300, marginBottom: 24 }}>
          <span>Saturated</span><span>Miscalibrated</span><span>Regulated</span>
        </div>

        <div style={{ background: COLORS.surface, borderRadius: 12, padding: '16px 18px', fontSize: 15, lineHeight: 1.5, color: COLORS.ink }}>
          {profile.analysis}
        </div>

        <div style={{
          marginTop: 14, padding: '14px 16px', borderRadius: 12,
          background: COLORS.raised, border: `1px solid ${COLORS.hair}`,
          display: 'flex', alignItems: 'center', gap: 12,
        }}>
          <div style={{
            width: 36, height: 36, borderRadius: 10, background: COLORS.sageTint, color: COLORS.sageDeep,
            display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, fontWeight: 700,
          }}>→</div>
          <div>
            <div style={{ fontSize: 12, color: COLORS.ink500 }}>Recommended starting point</div>
            <div style={{ fontSize: 15, fontWeight: 600 }}>{profile.intervention}</div>
          </div>
        </div>
      </div>

      <PrimaryButton onClick={() => { track('ClickViewProtocol'); onContinue(profile); }}>
        {tweaks.ctaText}
      </PrimaryButton>
      <div style={{ textAlign: 'center', fontSize: 12, color: COLORS.ink300, marginTop: 10 }}>
        Targeted protocols tuned to your primary friction point.
      </div>
    </div>
  );
}

function PaywallScreen({ tweaks, profile, score, onPurchase }) {
  React.useEffect(() => { track('ViewPaywall', { price: tweaks.price }); }, []);
  const price = Number(tweaks.price).toFixed(2);

  return (
    <div style={{ padding: '56px 24px 24px', display: 'flex', flexDirection: 'column', height: '100%', boxSizing: 'border-box' }}>
      <div style={{ flex: 1 }}>
        <Eyebrow>For your baseline of {score}/100</Eyebrow>
        <h1 style={{ fontSize: 24, lineHeight: 1.2, fontWeight: 700, letterSpacing: '-0.01em', color: COLORS.ink, margin: '0 0 20px' }}>
          Your 12-week recalibration protocol
        </h1>

        <div style={{ background: COLORS.surface, borderRadius: 16, padding: 16, marginBottom: 14 }}>
          {[
            [profile.intervention, "A targeted protocol for your primary baseline deficit."],
            ["Friction mapping",   "Custom constraints to eliminate compulsive checking."],
            ["Cognitive pacing",   "Science-backed intervals that defend your deep-focus blocks."],
          ].map(([t, d]) => (
            <div key={t} style={{ display: 'flex', gap: 12, marginBottom: 12 }}>
              <div style={{ width: 22, height: 22, borderRadius: 999, background: COLORS.sageDeep, color: '#FBF9F6', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 13, fontWeight: 700, flexShrink: 0 }}>✓</div>
              <div>
                <div style={{ fontSize: 15, fontWeight: 600, color: COLORS.ink }}>{t}</div>
                <div style={{ fontSize: 13, lineHeight: 1.45, color: COLORS.ink500, marginTop: 2 }}>{d}</div>
              </div>
            </div>
          ))}
        </div>

        {tweaks.socialProof === 'rating' && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 14px', background: COLORS.ochreTint, borderRadius: 10, marginBottom: 14 }}>
            <div style={{ color: COLORS.ochreDeep, fontSize: 14, letterSpacing: 1 }}>★★★★★</div>
            <div style={{ fontSize: 13, color: COLORS.ink }}>X.X from XXX beta testers</div>
          </div>
        )}
        {tweaks.socialProof === 'stat' && (
          <div style={{ padding: '12px 14px', background: COLORS.sageTint, borderRadius: 10, marginBottom: 14, fontSize: 13, color: COLORS.ink }}>
            <b>91%</b> report focus gains by week 4.
          </div>
        )}

        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '4px 4px 0' }}>
          <div style={{ fontSize: 15, color: COLORS.ink }}>Full 12-week access</div>
          <div style={{ fontSize: 22, fontWeight: 700, color: COLORS.ink }}>${price}</div>
        </div>
      </div>

      <PrimaryButton onClick={() => { track('ClickPurchase', { price: tweaks.price }); onPurchase(); }}>
        Activate protocol
      </PrimaryButton>
      <div style={{ textAlign: 'center', fontSize: 12, color: COLORS.ink300, marginTop: 10 }}>7-day money-back guarantee. Cancel anytime.</div>
    </div>
  );
}

function ConfirmScreen({ email }) {
  React.useEffect(() => { track('BetaReserved'); }, []);
  return (
    <div style={{ padding: '24px', height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', textAlign: 'center', position: 'relative' }}>
      <div style={{ position: 'absolute', top: 24, left: 24 }}>
        <HeaderLockup/>
      </div>
      <div style={{ width: 80, height: 80, borderRadius: 999, background: COLORS.sageTint, color: COLORS.sageDeep, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 36, marginBottom: 20 }}>✓</div>
      <h1 style={{ fontSize: 26, fontWeight: 700, margin: '0 0 10px', color: COLORS.ink, letterSpacing: '-0.01em' }}>You're on the list.</h1>
      <p style={{ fontSize: 15, lineHeight: 1.5, color: COLORS.ink500, margin: 0, maxWidth: 300 }}>
        We'll email <b style={{ color: COLORS.ink }}>{email || 'you'}</b> the moment Baseline ships. No charge until then.
      </p>
    </div>
  );
}

// ─── App shell ─────────────────────────────────────────────
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "hookAngle": "curiosity",
  "voice": "warm",
  "price": 29.99,
  "ctaText": "View recalibration protocol",
  "loadingCopy": "warm",
  "socialProof": "none"
}/*EDITMODE-END*/;

// Variant override hook — a deployable variant HTML can set window.__VARIANT
// BEFORE this script loads to pre-seed the defaults. Tracking calls then carry
// the variant label so analytics can split A/B results.
const EFFECTIVE_DEFAULTS = { ...TWEAK_DEFAULTS, ...(window.__VARIANT?.tweaks || {}) };

function App() {
  const [tweaks, setTweak] = useTweaks(EFFECTIVE_DEFAULTS);

  const questions = tweaks.voice === 'clinical' ? QUESTIONS_CLINICAL : QUESTIONS_WARM;

  // funnel state
  const [stage, setStage] = React.useState('intro'); // intro | quiz | loading | reveal | paywall | beta | confirm
  const [step, setStep] = React.useState(0);
  const [scores, setScores] = React.useState([]);
  const [profile, setProfile] = React.useState(null);
  const [email, setEmail] = React.useState('');
  const totalScore = scores.reduce((a, b) => a + b, 0);

  // Capture UTM params once from the landing URL (variants set utm_content on the ad link)
  const [utmParams] = React.useState(() => {
    try {
      const u = new URLSearchParams(location.search);
      return {
        utm_source:   u.get('utm_source')   || undefined,
        utm_medium:   u.get('utm_medium')   || undefined,
        utm_campaign: u.get('utm_campaign') || undefined,
        utm_content:  u.get('utm_content')  || u.get('hook') || undefined,
        utm_term:     u.get('utm_term')     || undefined,
      };
    } catch { return {}; }
  });

  // expose dev log
  const [showLog, setShowLog] = React.useState(false);
  React.useEffect(() => {
    if (location.search.includes('debug')) setShowLog(true);
    function onKey(e) { if (e.key === 'L' && e.shiftKey) setShowLog(s => !s); }
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, []);

  function reset() {
    setStage('intro'); setStep(0); setScores([]); setProfile(null); setEmail('');
    track('Reset');
  }

  function startQuiz() { setStage('quiz'); setStep(0); setScores([]); }

  function onAnswer(opt) {
    const next = [...scores];
    next[step] = opt.score;
    setScores(next);
    if (step + 1 < questions.length) {
      setStep(step + 1);
    } else {
      setStage('loading');
    }
  }

  function onBack() {
    if (step === 0) setStage('intro');
    else setStep(step - 1);
  }

  // Fire-and-forget: move to confirm immediately, POST in background.
  // Network errors are swallowed so the user never sees a broken confirm screen.
  async function submitEmail(emailAddr) {
    setEmail(emailAddr);
    setStage('confirm');
    try {
      await fetch('/api/subscribe', {
        method:  'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          email:    emailAddr,
          score:    totalScore,
          band:     profile?.band,
          price:    tweaks.price,
          variant:  window.__VARIANT?.label || null,
          ...utmParams,
        }),
      });
    } catch (e) {
      console.warn('[baseline] subscribe failed (continuing anyway):', e);
    }
  }

  let screen;
  if (stage === 'intro')      screen = <IntroScreen tweaks={tweaks} onStart={startQuiz}/>;
  else if (stage === 'quiz')  screen = <QuizScreen tweaks={tweaks} step={step} total={questions.length} data={questions[step]} prev={scores[step]} onAnswer={onAnswer} onBack={onBack}/>;
  else if (stage === 'loading') screen = <LoadingScreen tweaks={tweaks} onDone={() => setStage('reveal')}/>;
  else if (stage === 'reveal') screen = <RevealScreen tweaks={tweaks} score={totalScore} onContinue={(p) => { setProfile(p); setStage('paywall'); }}/>;
  else if (stage === 'paywall') screen = <PaywallScreen tweaks={tweaks} profile={profile} score={totalScore} onPurchase={() => setStage('beta')}/>;
  else if (stage === 'beta')  screen = <BetaSignup tweaks={tweaks} score={totalScore} onSubmit={submitEmail} onBack={() => setStage('paywall')}/>;
  else if (stage === 'confirm') screen = <ConfirmScreen email={email}/>;

  return (
    <>
      <div className="app-frame">{screen}</div>

      {/* Debug event log */}
      {showLog && <EventLog onClose={() => setShowLog(false)} onReset={reset}/>}

      {/* Tweaks panel */}
      <TweaksPanel title="Tweaks">
        <TweakSection label="Hook (entry screen)">
          <TweakSelect label="Headline angle" value={tweaks.hookAngle} onChange={(v) => setTweak('hookAngle', v)} options={[
            { value: 'curiosity', label: 'Curiosity' },
            { value: 'pain',      label: 'Pain' },
            { value: 'identity',  label: 'Identity' },
            { value: 'antitech',  label: 'Anti-tech' },
          ]}/>
        </TweakSection>

        <TweakSection label="Voice">
          <TweakRadio label="Quiz copy" value={tweaks.voice} onChange={(v) => setTweak('voice', v)} options={[
            { value: 'warm',     label: 'Warm' },
            { value: 'clinical', label: 'Clinical' },
          ]}/>
          <TweakRadio label="Loading" value={tweaks.loadingCopy} onChange={(v) => setTweak('loadingCopy', v)} options={[
            { value: 'warm',     label: 'Warm' },
            { value: 'clinical', label: 'Clinical' },
          ]}/>
        </TweakSection>

        <TweakSection label="Funnel">
          <TweakText label="Reveal CTA text" value={tweaks.ctaText} onChange={(v) => setTweak('ctaText', v)}/>
        </TweakSection>

        <TweakSection label="Paywall">
          <TweakRadio label="Price" value={tweaks.price} onChange={(v) => setTweak('price', v)} options={[
            { value: 9.99,  label: '$9.99' },
            { value: 19.99, label: '$19.99' },
            { value: 29.99, label: '$29.99' },
          ]}/>
          <TweakSelect label="Social proof" value={tweaks.socialProof} onChange={(v) => setTweak('socialProof', v)} options={[
            { value: 'none',   label: 'None' },
            { value: 'rating', label: '★ X.X from XXX beta testers' },
            { value: 'stat',   label: '91% report focus gains' },
          ]}/>
        </TweakSection>

        <TweakSection label="Test">
          <TweakButton label="Reset funnel to intro" onClick={reset}/>
          <TweakButton label={(showLog ? 'Hide' : 'Show') + ' event log'} onClick={() => setShowLog(s => !s)} secondary/>
        </TweakSection>
      </TweaksPanel>
    </>
  );
}

function EventLog({ onClose, onReset }) {
  const [events, setEvents] = React.useState(window.__eventLog || []);
  React.useEffect(() => {
    function onEvt() { setEvents([...(window.__eventLog || [])]); }
    window.addEventListener('quiz-track', onEvt);
    return () => window.removeEventListener('quiz-track', onEvt);
  }, []);
  return (
    <div style={{
      position: 'fixed', left: 16, bottom: 16, width: 320, maxHeight: 280,
      background: 'rgba(45,43,42,0.96)', color: '#FBF9F6',
      borderRadius: 12, padding: 12, fontSize: 11, fontFamily: 'ui-monospace, Menlo, monospace',
      boxShadow: '0 12px 32px rgba(0,0,0,0.3)', zIndex: 100, overflow: 'auto',
    }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
        <strong>Event log</strong>
        <span style={{ opacity: 0.6 }}>shift+L to toggle · {events.length}</span>
      </div>
      {events.slice(-20).map((e, i) => (
        <div key={i} style={{ marginBottom: 3, opacity: 0.9 }}>
          <span style={{ color: '#A3B19B' }}>{e.event}</span>
          {Object.keys(e.props).length > 0 && <span style={{ color: '#BFB9B1' }}> {JSON.stringify(e.props)}</span>}
        </div>
      ))}
      <button onClick={onReset} style={{ marginTop: 8, marginRight: 6, padding: '4px 8px', background: '#6F8067', color: '#FBF9F6', border: 'none', borderRadius: 6, cursor: 'pointer', fontFamily: 'inherit', fontSize: 11 }}>Reset funnel</button>
      <button onClick={onClose} style={{ marginTop: 8, padding: '4px 8px', background: 'transparent', color: '#FBF9F6', border: '1px solid rgba(255,255,255,0.2)', borderRadius: 6, cursor: 'pointer', fontFamily: 'inherit', fontSize: 11 }}>Hide</button>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
