// onboarding.jsx — First-run customer onboarding: survey → tailoring splash → guided tutorial.
//
// Scope (broadened 2026-06-05): Chrono now serves school, solo-dev,
// project management, personal growth, anti-procrastination, fitness/gym,
// and general time management. The survey captures the user's life-domain
// mix; the tutorial uses those answers to surface domain-specific tips.

// ═══════════════════════════════════════════════════════════════════════
// Root orchestrator — sequences survey → splash → tutorial → done.
// ═══════════════════════════════════════════════════════════════════════
function OnboardingApp({ theme, onComplete }) {
  // Auth is the FIRST step of onboarding so the login/signup screen never
  // overlaps the survey/tutorial. Already-signed-in users (or builds without
  // the accounts backend) skip straight to the survey.
  const authed = !!(window.chronoAuth && window.chronoAuth.isAuthed());
  const startPhase = (window.AuthGate && !authed) ? 'auth' : 'survey';
  const [phase, setPhase] = React.useState(startPhase); // 'auth' | 'survey' | 'splash' | 'tutorial'
  const [answers, setAnswers] = React.useState({});

  if (phase === 'auth') {
    return <window.AuthGate embedded onDone={() => setPhase('survey')} />;
  }
  if (phase === 'survey') {
    return <SurveyFlow theme={theme}
      onComplete={(a) => { setAnswers(a); setPhase('splash'); }}/>;
  }
  if (phase === 'splash') {
    return <TailoringSplash theme={theme} answers={answers}
      onDone={() => setPhase('tutorial')}/>;
  }
  return <GuidedTutorial theme={theme} answers={answers}
    onDone={() => {
      setOnboarding({ done: true, tutorialDone: true, answers });
      onComplete();
    }}/>;
}

// ═══════════════════════════════════════════════════════════════════════
// SURVEY  — multi-step, Arc-style.
// ═══════════════════════════════════════════════════════════════════════
function SurveyFlow({ theme, onComplete }) {
  const [step, setStep] = React.useState(0);
  const [answers, setAnswers] = React.useState({});
  const update = (patch) => setAnswers(a => ({ ...a, ...patch }));

  // Some steps are conditional on prior answers (gym goal only shows if the
  // user picked "fitness" as a domain; school detail only if student).
  const steps = React.useMemo(() => SURVEY_STEPS.filter(s =>
    typeof s.when === 'function' ? s.when(answers) : true
  ), [answers]);

  const next = () => step < steps.length - 1 ? setStep(s => s + 1) : onComplete(answers);
  const back = () => step > 0 ? setStep(s => s - 1) : null;
  const skip = () => next();

  const s = steps[step] || steps[steps.length - 1];
  const progress = (step + 1) / steps.length;
  const isFinal = step === steps.length - 1;

  return (
    <div style={{
      position: 'fixed', inset: 0, overflow: 'hidden',
      zIndex: 2147483647,
      background: theme.bg0, color: theme.text,
      display: 'flex', flexDirection: 'column',
      fontFamily: ChronoTokens.font.ui,
    }}>
      {/* Ambient backdrop */}
      <div style={{
        position: 'absolute', top: -150, left: '15%', width: 520, height: 520,
        borderRadius: '50%',
        background: `radial-gradient(circle, ${theme.accentColor}24 0%, transparent 65%)`,
        filter: 'blur(80px)', pointerEvents: 'none',
        animation: 'chrono-orb-1 24s ease-in-out infinite',
      }}/>
      <div style={{
        position: 'absolute', bottom: -120, right: '12%', width: 480, height: 480,
        borderRadius: '50%',
        background: `radial-gradient(circle, ${theme.signal || theme.accentColor}20 0%, transparent 65%)`,
        filter: 'blur(90px)', pointerEvents: 'none',
        animation: 'chrono-orb-2 28s ease-in-out infinite',
      }}/>

      {/* Top bar — logo + progress */}
      <div style={{ position: 'relative', zIndex: 2,
        padding: '20px 32px 12px',
        display: 'flex', alignItems: 'center', gap: 16,
      }}>
        <ChronoLogo size={20} color={theme.accentColor}/>
        <div style={{
          fontFamily: ChronoTokens.font.display, fontSize: 15, fontWeight: 700,
          letterSpacing: '-0.01em',
        }}>Chrono</div>
        <div style={{ flex: 1 }}/>
        <div style={{
          fontFamily: ChronoTokens.font.mono, fontSize: 11, fontWeight: 600,
          color: theme.textFaint, letterSpacing: '0.14em', textTransform: 'uppercase',
        }}>{step + 1} / {steps.length}</div>
      </div>
      <div style={{ position: 'relative', zIndex: 2, height: 3, background: theme.isDark ? 'rgba(255,255,255,0.06)' : 'rgba(10,12,20,0.06)' }}>
        <div style={{
          height: '100%', width: `${progress * 100}%`,
          background: `linear-gradient(90deg, ${theme.accentColor}, ${theme.signal || theme.accentColor})`,
          boxShadow: `0 0 12px ${theme.accentColor}88`,
          transition: 'width 360ms cubic-bezier(0.22, 1, 0.36, 1)',
        }}/>
      </div>

      {/* Step body */}
      <div className="chrono-scrollbar" style={{
        position: 'relative', zIndex: 2,
        flex: 1, overflow: 'auto',
        display: 'flex', flexDirection: 'column', alignItems: 'center',
        padding: '40px 24px 80px',
      }}>
        <div key={`${step}-${s.id}`} style={{
          width: '100%', maxWidth: 640,
          animation: `chrono-fade-up 360ms ${Spring.std} both`,
        }}>
          {s.eyebrow && (
            <div style={{
              fontFamily: ChronoTokens.font.mono, fontSize: 11.5, fontWeight: 700,
              color: theme.accentColor, letterSpacing: '0.18em', textTransform: 'uppercase',
              marginBottom: 12,
              textShadow: theme.isDark ? `0 0 12px ${theme.accentColor}66` : 'none',
            }}>{s.eyebrow}</div>
          )}
          {s.title && (
            <h1 style={{
              fontFamily: ChronoTokens.font.display, fontSize: 38, fontWeight: 800,
              letterSpacing: '-0.035em', lineHeight: 1.1,
              margin: '0 0 12px',
            }}>{s.title}</h1>
          )}
          {s.subtitle && (
            <p style={{
              fontSize: 15, lineHeight: 1.55, color: theme.textDim,
              margin: '0 0 28px', maxWidth: 520,
            }}>{s.subtitle}</p>
          )}
          <div style={{ marginTop: s.subtitle ? 4 : 24 }}>
            {s.render({ answers, update, theme, next })}
          </div>
        </div>
      </div>

      {/* Bottom action bar */}
      <div style={{
        position: 'relative', zIndex: 3,
        padding: '16px 32px 24px',
        borderTop: `0.5px solid ${theme.hairline}`,
        background: theme.isDark ? 'rgba(10,12,20,0.6)' : 'rgba(255,255,255,0.7)',
        backdropFilter: 'blur(20px)',
        display: 'flex', alignItems: 'center', gap: 12,
      }}>
        {step > 0 && (
          <button onClick={back} className="chrono-tap" style={{
            appearance: 'none', cursor: 'pointer', border: 'none', background: 'transparent',
            color: theme.textDim,
            fontFamily: ChronoTokens.font.ui, fontSize: 13, fontWeight: 600,
            padding: '8px 12px', display: 'inline-flex', alignItems: 'center', gap: 6,
          }}>
            <Icon.back s={12}/> Back
          </button>
        )}
        <div style={{ flex: 1 }}/>
        {s.skippable !== false && step > 0 && !isFinal && (
          <button onClick={skip} className="chrono-tap" style={{
            appearance: 'none', cursor: 'pointer', border: 'none', background: 'transparent',
            color: theme.textFaint,
            fontFamily: ChronoTokens.font.ui, fontSize: 13, fontWeight: 600,
            padding: '8px 12px',
          }}>Skip</button>
        )}
        <button onClick={next} className="chrono-tap chrono-btn" style={{
          appearance: 'none', cursor: 'pointer', border: 'none',
          padding: '10px 22px', borderRadius: 999,
          background: `linear-gradient(135deg, ${theme.accentColor}, ${theme.signal || theme.accentColor})`,
          color: '#fff',
          fontFamily: ChronoTokens.font.ui, fontSize: 13.5, fontWeight: 700,
          letterSpacing: '-0.005em',
          boxShadow: `0 6px 20px ${theme.accentColor}55, 0 0 0 1px ${theme.accentColor}33`,
          display: 'inline-flex', alignItems: 'center', gap: 8,
        }}>
          {isFinal ? 'Finish setup' : (step === 0 ? 'Get started' : 'Continue')}
          <Icon.fwd s={12}/>
        </button>
      </div>
    </div>
  );
}

// ─── Shared survey primitives ───────────────────────────────────────────
function Field({ theme, label, value, onChange, placeholder, type = 'text' }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginBottom: 14 }}>
      <label style={{
        fontFamily: ChronoTokens.font.mono, fontSize: 10.5, fontWeight: 700,
        color: theme.textFaint, letterSpacing: '0.14em', textTransform: 'uppercase',
      }}>{label}</label>
      <input type={type} value={value || ''} onChange={e => onChange(e.target.value)}
        placeholder={placeholder}
        style={{
          appearance: 'none', outline: 'none',
          padding: '12px 14px', borderRadius: 12,
          background: theme.isDark ? 'rgba(255,255,255,0.04)' : 'rgba(10,12,20,0.04)',
          border: `0.5px solid ${theme.hairline}`,
          color: theme.text,
          fontFamily: ChronoTokens.font.ui, fontSize: 15, fontWeight: 500,
        }}/>
    </div>
  );
}

function CardSelect({ theme, options, value, onChange, columns = 2 }) {
  return (
    <div style={{
      display: 'grid', gridTemplateColumns: `repeat(${columns}, 1fr)`, gap: 10,
    }}>
      {options.map(o => {
        const sel = value === o.v;
        return (
          <button key={o.v} onClick={() => onChange(o.v)} className="chrono-tap" style={{
            appearance: 'none', cursor: 'pointer', textAlign: 'left',
            padding: 18, borderRadius: 14,
            border: `1px solid ${sel ? theme.accentColor : theme.hairline}`,
            background: sel
              ? (theme.isDark ? `linear-gradient(135deg, ${theme.accentColor}1F, transparent 70%), rgba(255,255,255,0.02)` : `${theme.accentColor}10`)
              : (theme.isDark ? 'rgba(255,255,255,0.025)' : 'rgba(255,255,255,0.6)'),
            color: theme.text,
            boxShadow: sel ? `0 0 0 1px ${theme.accentColor}, 0 8px 28px ${theme.accentColor}33` : 'none',
            transition: `all 220ms ${Spring.std}`,
          }}>
            {o.icon && (
              <div style={{
                fontSize: 22, marginBottom: 8,
                color: sel ? theme.accentColor : theme.textDim,
              }}>{o.icon}</div>
            )}
            <div style={{
              fontFamily: ChronoTokens.font.display, fontSize: 16, fontWeight: 700,
              letterSpacing: '-0.01em', marginBottom: 4,
            }}>{o.label}</div>
            {o.desc && (
              <div style={{
                fontSize: 12.5, color: theme.textDim, lineHeight: 1.4,
              }}>{o.desc}</div>
            )}
          </button>
        );
      })}
    </div>
  );
}

function PillSelect({ theme, options, value, onChange }) {
  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
      {options.map(o => {
        const sel = value === o.v;
        return (
          <button key={o.v} onClick={() => onChange(o.v)} className="chrono-tap" style={{
            appearance: 'none', cursor: 'pointer',
            padding: '9px 16px', borderRadius: 999,
            border: `1px solid ${sel ? theme.accentColor : theme.hairline}`,
            background: sel
              ? `linear-gradient(135deg, ${theme.accentColor}, ${theme.signal || theme.accentColor})`
              : (theme.isDark ? 'rgba(255,255,255,0.04)' : 'rgba(10,12,20,0.04)'),
            color: sel ? '#fff' : theme.text,
            fontFamily: ChronoTokens.font.ui, fontSize: 13, fontWeight: 600,
            letterSpacing: '-0.005em',
            boxShadow: sel ? `0 4px 16px ${theme.accentColor}55` : 'none',
          }}>{o.label}</button>
        );
      })}
    </div>
  );
}

function ChipMulti({ theme, options, value = [], onChange }) {
  const toggle = (v) => {
    onChange(value.includes(v) ? value.filter(x => x !== v) : [...value, v]);
  };
  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
      {options.map(o => {
        const sel = value.includes(o.v);
        return (
          <button key={o.v} onClick={() => toggle(o.v)} className="chrono-tap" style={{
            appearance: 'none', cursor: 'pointer',
            padding: '9px 14px', borderRadius: 999,
            border: `1px solid ${sel ? theme.accentColor : theme.hairline}`,
            background: sel
              ? `${theme.accentColor}1F`
              : (theme.isDark ? 'rgba(255,255,255,0.04)' : 'rgba(10,12,20,0.04)'),
            color: sel ? theme.accentColor : theme.text,
            fontFamily: ChronoTokens.font.ui, fontSize: 13, fontWeight: 600,
            display: 'inline-flex', alignItems: 'center', gap: 6,
          }}>
            {sel && <span style={{ fontSize: 11 }}>✓</span>}
            {o.label}
          </button>
        );
      })}
    </div>
  );
}

function ToggleRow({ theme, label, sub, value, onChange }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 14,
      padding: '14px 16px', borderRadius: 12,
      border: `0.5px solid ${theme.hairline}`,
      background: theme.isDark ? 'rgba(255,255,255,0.025)' : 'rgba(255,255,255,0.6)',
    }}>
      <div style={{ flex: 1 }}>
        <div style={{ fontSize: 14, fontWeight: 600 }}>{label}</div>
        {sub && <div style={{ fontSize: 12, color: theme.textDim, marginTop: 2 }}>{sub}</div>}
      </div>
      <button onClick={() => onChange(!value)} style={{
        appearance: 'none', cursor: 'pointer', border: 'none',
        width: 44, height: 26, borderRadius: 999,
        background: value ? theme.accentColor : (theme.isDark ? 'rgba(255,255,255,0.1)' : 'rgba(10,12,20,0.12)'),
        padding: 2, position: 'relative',
        boxShadow: value ? theme.accentGlow : 'none',
        transition: `all 220ms ${Spring.snap}`,
      }}>
        <div style={{
          width: 22, height: 22, borderRadius: 999, background: '#fff',
          transform: value ? 'translateX(18px)' : 'translateX(0)',
          transition: `transform 220ms ${Spring.std}`,
          boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
        }}/>
      </button>
    </div>
  );
}

// ─── The survey steps ───────────────────────────────────────────────────
const isStudent = (a) => a.role === 'undergrad' || a.role === 'grad' || a.role === 'hs';
const hasDomain = (a, k) => Array.isArray(a.domains) && a.domains.includes(k);

const SURVEY_STEPS = [
  // 1. Welcome
  {
    id: 'welcome',
    skippable: false,
    render: ({ theme }) => (
      <div style={{ textAlign: 'center', padding: '40px 0 20px' }}>
        <div style={{
          width: 96, height: 96, margin: '0 auto 28px',
          borderRadius: 28,
          background: `linear-gradient(135deg, ${theme.accentColor}, ${theme.signal || theme.accentColor})`,
          boxShadow: `0 20px 60px ${theme.accentColor}66, 0 0 40px ${theme.accentColor}44`,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          animation: `chrono-fade-up 600ms ${Spring.std} both`,
        }}>
          <ChronoLogo size={44} color="#fff"/>
        </div>
        <div style={{
          fontFamily: ChronoTokens.font.mono, fontSize: 11.5, fontWeight: 700,
          color: theme.accentColor, letterSpacing: '0.2em', textTransform: 'uppercase',
          marginBottom: 16,
        }}>Welcome</div>
        <h1 style={{
          fontFamily: ChronoTokens.font.display, fontSize: 48, fontWeight: 800,
          letterSpacing: '-0.04em', lineHeight: 1.05,
          margin: '0 0 16px',
        }}>Let's tune Chrono<br/>to how you actually live.</h1>
        <p style={{
          fontSize: 16, lineHeight: 1.55, color: theme.textDim,
          maxWidth: 480, margin: '0 auto 32px',
        }}>
          School, side projects, the gym, deep work — Chrono will plan around all of it. A short survey, about two minutes.
        </p>
      </div>
    ),
  },

  // Identity (name + email) is collected on the sign-up page now, so the
  // survey skips straight from welcome to role.

  // 3. Role — broadened
  {
    id: 'role',
    eyebrow: 'Your role',
    title: 'What describes you best?',
    subtitle: "Sets scheduling defaults, AI behaviour, and which categories ship enabled.",
    render: ({ answers, update, theme }) => (
      <CardSelect theme={theme} value={answers.role} onChange={v => update({ role: v })}
        options={[
          { v: 'undergrad', label: 'Undergraduate', desc: "Bachelor's degree in progress" },
          { v: 'grad', label: 'Graduate student', desc: "Master's, PhD, or professional" },
          { v: 'hs', label: 'High school', desc: 'Or pre-college program' },
          { v: 'solo-dev', label: 'Solo developer', desc: 'Building software on your own time' },
          { v: 'pm', label: 'Project / product manager', desc: 'Coordinating teams and deliverables' },
          { v: 'pro', label: 'Working professional', desc: 'Day job + side commitments' },
          { v: 'creator', label: 'Creator / freelancer', desc: 'Writer, designer, founder, athlete' },
          { v: 'other', label: 'Something else', desc: 'Just here to run my life better' },
        ]}/>
    ),
  },

  // 4. Life domains — NEW, the heart of the broadening
  {
    id: 'domains',
    eyebrow: 'Life domains',
    title: 'What do you want Chrono to plan around?',
    subtitle: 'Pick everything that matters. The AI weighs these when proposing blocks.',
    render: ({ answers, update, theme }) => (
      <ChipMulti theme={theme} value={answers.domains || []} onChange={v => update({ domains: v })}
        options={[
          { v: 'school',     label: 'Schoolwork' },
          { v: 'work',       label: 'Day job / work projects' },
          { v: 'sidebuild',  label: 'Side projects / coding' },
          { v: 'pm',         label: 'Project management' },
          { v: 'gym',        label: 'Fitness / gym' },
          { v: 'growth',     label: 'Personal growth' },
          { v: 'habits',     label: 'Habits & routines' },
          { v: 'hobbies',    label: 'Hobbies / creative time' },
          { v: 'social',     label: 'Friends & family' },
          { v: 'errands',    label: 'Errands & admin' },
        ]}/>
    ),
  },

  // 5. School detail (conditional)
  {
    id: 'school',
    when: isStudent,
    eyebrow: 'Your school',
    title: 'Where are you studying?',
    subtitle: 'Used to connect Canvas and pre-fill the Study category. Skip if you\'d rather not say.',
    render: ({ answers, update, theme }) => (
      <div>
        <Field theme={theme} label="School / institution" value={answers.school} onChange={v => update({ school: v })} placeholder="e.g. University of Illinois"/>
        <Field theme={theme} label="Major / field" value={answers.major} onChange={v => update({ major: v })} placeholder="e.g. Computer Science"/>
        <Field theme={theme} label="Year (optional)" value={answers.year} onChange={v => update({ year: v })} placeholder="e.g. Junior · 2026"/>
      </div>
    ),
  },

  // 6. Class load (conditional)
  {
    id: 'classload',
    when: isStudent,
    eyebrow: 'Course load',
    title: 'How many classes this term?',
    render: ({ answers, update, theme }) => (
      <PillSelect theme={theme} value={answers.classes} onChange={v => update({ classes: v })}
        options={[
          { v: '1-2', label: '1–2 classes' },
          { v: '3-4', label: '3–4 classes' },
          { v: '5-6', label: '5–6 classes' },
          { v: '7+',  label: '7 or more' },
        ]}/>
    ),
  },

  // 7. Solo-dev stack (conditional)
  {
    id: 'devstack',
    when: (a) => a.role === 'solo-dev' || hasDomain(a, 'sidebuild'),
    eyebrow: 'Side projects',
    title: 'What are you shipping?',
    subtitle: 'Free-form. The AI uses this to label deep-work blocks.',
    render: ({ answers, update, theme }) => (
      <Field theme={theme} label="Project / stack" value={answers.devProject} onChange={v => update({ devProject: v })} placeholder="e.g. A SaaS for kitesurfers, Rust + SvelteKit"/>
    ),
  },

  // 8. PM context (conditional)
  {
    id: 'pmcontext',
    when: (a) => a.role === 'pm' || hasDomain(a, 'pm'),
    eyebrow: 'Project management',
    title: 'What are you running?',
    subtitle: 'Used to template recurring meetings and sprint blocks.',
    render: ({ answers, update, theme }) => (
      <>
        <Field theme={theme} label="Team / project name" value={answers.pmTeam} onChange={v => update({ pmTeam: v })} placeholder="e.g. Mobile checkout team"/>
        <div style={{ marginTop: 16 }}>
          <div style={{
            fontFamily: ChronoTokens.font.mono, fontSize: 10.5, fontWeight: 700,
            color: theme.textFaint, letterSpacing: '0.14em', textTransform: 'uppercase',
            marginBottom: 10,
          }}>Cadence</div>
          <PillSelect theme={theme} value={answers.pmCadence} onChange={v => update({ pmCadence: v })}
            options={[
              { v: 'weekly',   label: 'Weekly sprint' },
              { v: 'biweekly', label: 'Two-week sprint' },
              { v: 'monthly',  label: 'Monthly cycle' },
              { v: 'kanban',   label: 'Continuous / kanban' },
            ]}/>
        </div>
      </>
    ),
  },

  // 9. Gym goal (conditional on domain)
  {
    id: 'gym',
    when: (a) => hasDomain(a, 'gym'),
    eyebrow: 'Training',
    title: 'What\'s the goal at the gym?',
    subtitle: 'The AI protects training slots and your recovery days.',
    render: ({ answers, update, theme }) => (
      <>
        <CardSelect theme={theme} value={answers.gymGoal} onChange={v => update({ gymGoal: v })}
          options={[
            { v: 'strength', label: 'Strength / lifting',  desc: 'Heavy compound work, 3–5 days/week' },
            { v: 'hypertrophy', label: 'Hypertrophy',      desc: 'Body composition, 4–6 days/week' },
            { v: 'cardio',   label: 'Cardio / endurance',  desc: 'Runs, rides, conditioning' },
            { v: 'general',  label: 'Stay active',         desc: '2–3 sessions, mixed' },
          ]}/>
        <div style={{ marginTop: 24 }}>
          <div style={{
            fontFamily: ChronoTokens.font.mono, fontSize: 10.5, fontWeight: 700,
            color: theme.textFaint, letterSpacing: '0.14em', textTransform: 'uppercase',
            marginBottom: 10,
          }}>Sessions per week</div>
          <PillSelect theme={theme} value={answers.gymPerWeek} onChange={v => update({ gymPerWeek: v })}
            options={['2','3','4','5','6'].map(n => ({ v: n, label: `${n}×` }))}/>
        </div>
      </>
    ),
  },

  // 10. Schedule density
  {
    id: 'density',
    eyebrow: 'Daily rhythm',
    title: 'How busy is a typical weekday?',
    subtitle: 'Drives buffer minutes and how aggressively the AI protects focus blocks.',
    render: ({ answers, update, theme }) => (
      <CardSelect theme={theme} value={answers.density} onChange={v => update({ density: v })}
        options={[
          { v: 'light', label: 'Light', desc: 'Lots of open time' },
          { v: 'moderate', label: 'Moderate', desc: 'A few fixed commitments' },
          { v: 'packed', label: 'Packed', desc: 'Back-to-back morning to evening' },
          { v: 'overwhelm', label: 'Overwhelming', desc: 'Need help pulling the day apart' },
        ]}/>
    ),
  },

  // 11. Sleep schedule
  {
    id: 'sleep',
    eyebrow: 'Sleep rhythm',
    title: 'When do you wake up and wind down?',
    subtitle: "Sets your work-hour window so the AI doesn't schedule things while you're asleep.",
    render: ({ answers, update, theme }) => (
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
        <Field theme={theme} type="time" label="Wake-up" value={answers.wake || '08:00'} onChange={v => update({ wake: v })}/>
        <Field theme={theme} type="time" label="Bedtime" value={answers.bed || '23:00'} onChange={v => update({ bed: v })}/>
      </div>
    ),
  },

  // 12. Focus rhythm
  {
    id: 'focus',
    eyebrow: 'Focus rhythm',
    title: 'When do you focus best?',
    subtitle: 'The AI biases deep-work suggestions toward your peak window.',
    render: ({ answers, update, theme }) => (
      <>
        <CardSelect theme={theme} value={answers.peak} onChange={v => update({ peak: v })}
          options={[
            { v: 'morning',   label: 'Early morning', desc: '5–10 AM' },
            { v: 'midday',    label: 'Midday',        desc: '10 AM – 2 PM' },
            { v: 'afternoon', label: 'Afternoon',     desc: '2–6 PM' },
            { v: 'night',     label: 'Night owl',     desc: '8 PM – 2 AM' },
          ]}/>
        <div style={{
          marginTop: 22,
          fontFamily: ChronoTokens.font.mono, fontSize: 10.5, fontWeight: 700,
          color: theme.textFaint, letterSpacing: '0.14em', textTransform: 'uppercase',
          marginBottom: 10,
        }}>Ideal session length</div>
        <PillSelect theme={theme} value={answers.sessionLen} onChange={v => update({ sessionLen: v })}
          options={['15','25','45','60','90','120'].map(n => ({ v: n, label: `${n} min` }))}/>
      </>
    ),
  },

  // 13. Procrastination — NEW
  {
    id: 'procrastination',
    eyebrow: 'Friction',
    title: 'Where do you tend to stall?',
    subtitle: 'No judgement. The AI will nudge differently if you tell us where you get stuck.',
    render: ({ answers, update, theme }) => (
      <>
        <ChipMulti theme={theme} value={answers.procrastination || []} onChange={v => update({ procrastination: v })}
          options={[
            { v: 'starting',   label: 'Hard to start tasks' },
            { v: 'overwhelm',  label: 'Get overwhelmed by big projects' },
            { v: 'context',    label: 'Lose context between sessions' },
            { v: 'distraction',label: 'Phone / social media distraction' },
            { v: 'perfection', label: 'Perfectionism stalls me' },
            { v: 'energy',     label: 'Low energy after work / class' },
          ]}/>
        <div style={{ marginTop: 22 }}>
          <ToggleRow theme={theme} label="Send momentum nudges"
            sub="The AI surfaces a 'next smallest step' when it sees a deep-work block stalled."
            value={answers.nudges !== false}
            onChange={v => update({ nudges: v })}/>
        </div>
      </>
    ),
  },

  // 14. Breaks + duration
  {
    id: 'breaks',
    eyebrow: 'Recovery',
    title: 'How long are your breaks?',
    subtitle: 'Used to space focus sessions and to set your AI buffer between events.',
    render: ({ answers, update, theme }) => (
      <>
        <PillSelect theme={theme} value={answers.breakLen} onChange={v => update({ breakLen: v })}
          options={['5','10','15','20','30'].map(n => ({ v: n, label: `${n} min` }))}/>
        <div style={{ marginTop: 24 }}>
          <ToggleRow theme={theme} label="Auto-schedule breaks"
            sub="Chrono inserts breaks between back-to-back events automatically."
            value={answers.autoBreaks !== false}
            onChange={v => update({ autoBreaks: v })}/>
        </div>
      </>
    ),
  },

  // 15. AI personality
  {
    id: 'ai',
    eyebrow: 'AI personality',
    title: 'How should Chrono talk to you?',
    subtitle: 'Change this any time in Settings.',
    render: ({ answers, update, theme }) => (
      <CardSelect theme={theme} value={answers.personality || 'concierge'} onChange={v => update({ personality: v })}
        options={[
          { v: 'concierge', label: 'Concierge', desc: 'Warm, proactive, surfaces ideas before you ask' },
          { v: 'copilot',   label: 'Co-pilot',  desc: 'Direct, only speaks up when it matters' },
          { v: 'silent',    label: 'Silent operator', desc: 'No suggestions, just executes what you ask' },
        ]} columns={1}/>
    ),
  },

  // 16. Display preferences
  {
    id: 'display',
    eyebrow: 'Display',
    title: 'A few visual choices.',
    subtitle: 'Toggle anything later in Settings.',
    render: ({ answers, update, theme }) => (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        <div>
          <div style={{
            fontFamily: ChronoTokens.font.mono, fontSize: 10.5, fontWeight: 700,
            color: theme.textFaint, letterSpacing: '0.14em', textTransform: 'uppercase',
            marginBottom: 8,
          }}>Week starts on</div>
          <PillSelect theme={theme} value={answers.weekStart || 'sun'} onChange={v => update({ weekStart: v })}
            options={[{v:'sun',label:'Sunday'},{v:'mon',label:'Monday'}]}/>
        </div>
        <div>
          <div style={{
            fontFamily: ChronoTokens.font.mono, fontSize: 10.5, fontWeight: 700,
            color: theme.textFaint, letterSpacing: '0.14em', textTransform: 'uppercase',
            marginBottom: 8,
          }}>Time format</div>
          <PillSelect theme={theme} value={answers.timeFmt || '12'} onChange={v => update({ timeFmt: v })}
            options={[{v:'12',label:'12-hour · 3:30 PM'},{v:'24',label:'24-hour · 15:30'}]}/>
        </div>
        <div>
          <div style={{
            fontFamily: ChronoTokens.font.mono, fontSize: 10.5, fontWeight: 700,
            color: theme.textFaint, letterSpacing: '0.14em', textTransform: 'uppercase',
            marginBottom: 8,
          }}>Notification lead time</div>
          <PillSelect theme={theme} value={String(answers.lead ?? 10)} onChange={v => update({ lead: parseInt(v,10) })}
            options={['0','5','10','15','30'].map(n => ({v:n, label: n==='0' ? 'At start' : `${n} min before`}))}/>
        </div>
        <ToggleRow theme={theme} label="Show US federal holidays"
          sub="Adds holiday labels on the week and month views."
          value={answers.holidays !== false}
          onChange={v => update({ holidays: v })}/>
      </div>
    ),
  },

  // 17. Integrations
  {
    id: 'integrations',
    eyebrow: 'Integrations',
    title: 'What should we plug in?',
    subtitle: 'Canvas connects now — the rest are on the way. Manage anytime in Profile.',
    render: ({ answers, update, theme }) => (
      <ChipMulti theme={theme} value={answers.integrations || []} onChange={v => update({ integrations: v })}
        options={[
          { v: 'canvas',  label: 'Canvas LMS' },
          { v: 'google',  label: 'Google Calendar (soon)' },
          { v: 'outlook', label: 'Outlook 365 (soon)' },
          { v: 'icloud',  label: 'iCloud (soon)' },
          { v: 'github',  label: 'GitHub (soon)' },
          { v: 'linear',  label: 'Linear (soon)' },
        ]}/>
    ),
  },

  // 18. Goals — broadened beyond school
  {
    id: 'goals',
    eyebrow: 'Outcomes',
    title: 'What do you want Chrono to help with?',
    subtitle: 'Pick all that apply. This shapes which insights the AI surfaces over time.',
    render: ({ answers, update, theme }) => (
      <ChipMulti theme={theme} value={answers.goals || []} onChange={v => update({ goals: v })}
        options={[
          { v: 'deadlines',  label: 'Stay on top of deadlines' },
          { v: 'shipping',   label: 'Actually ship side projects' },
          { v: 'procrast',   label: 'Procrastinate less' },
          { v: 'gym',        label: 'Train consistently' },
          { v: 'sleep',      label: 'Protect sleep' },
          { v: 'deepwork',   label: 'More deep-work hours' },
          { v: 'habits',     label: 'Build habits that stick' },
          { v: 'stress',     label: 'Reduce schedule stress' },
          { v: 'meetings',   label: 'Run better meetings' },
          { v: 'tracking',   label: 'Track progress over time' },
          { v: 'social',     label: 'Plan time with friends' },
          { v: 'learning',   label: 'Learn / read more' },
        ]}/>
    ),
  },
];

// ═══════════════════════════════════════════════════════════════════════
// TAILORING SPLASH — 20s, actually applies answers to stores in stages.
// ═══════════════════════════════════════════════════════════════════════
function TailoringSplash({ theme, answers, onDone }) {
  const TOTAL_MS = 20000;
  const STAGES = React.useMemo(() => buildTailoringStages(answers), [answers]);
  const [progress, setProgress] = React.useState(0);
  const [stageIdx, setStageIdx] = React.useState(0);
  const startedAt = React.useRef(performance.now());

  React.useEffect(() => {
    let raf;
    const tick = () => {
      const elapsed = performance.now() - startedAt.current;
      const p = Math.min(1, elapsed / TOTAL_MS);
      setProgress(p);
      const idx = Math.min(STAGES.length - 1, Math.floor(p * STAGES.length));
      setStageIdx(idx);
      if (p >= 1) {
        STAGES.forEach(s => s.apply && s.apply());
        onDone();
        return;
      }
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [STAGES, onDone]);

  const appliedRef = React.useRef(new Set());
  React.useEffect(() => {
    for (let i = 0; i <= stageIdx; i++) {
      if (!appliedRef.current.has(i)) {
        appliedRef.current.add(i);
        STAGES[i].apply && STAGES[i].apply();
      }
    }
  }, [stageIdx, STAGES]);

  const huePulse = Math.floor(progress * 60);

  return (
    <div style={{
      position: 'fixed', inset: 0, overflow: 'hidden',
      zIndex: 2147483647,
      background: theme.bg0, color: theme.text,
      fontFamily: ChronoTokens.font.ui,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
    }}>
      <div style={{
        position: 'absolute', top: '20%', left: '15%', width: 540, height: 540,
        borderRadius: '50%',
        background: `radial-gradient(circle, oklch(58% 0.22 ${270+huePulse}) 0%, transparent 60%)`,
        filter: 'blur(110px)', opacity: 0.65,
        animation: 'chrono-orb-1 16s ease-in-out infinite',
      }}/>
      <div style={{
        position: 'absolute', bottom: '15%', right: '14%', width: 460, height: 460,
        borderRadius: '50%',
        background: `radial-gradient(circle, oklch(55% 0.22 ${330-huePulse}) 0%, transparent 60%)`,
        filter: 'blur(100px)', opacity: 0.55,
        animation: 'chrono-orb-2 20s ease-in-out infinite',
      }}/>

      <div style={{
        position: 'relative', zIndex: 2,
        maxWidth: 560, padding: '0 32px', textAlign: 'center',
      }}>
        <div style={{
          width: 120, height: 120, margin: '0 auto 36px',
          position: 'relative',
        }}>
          <div style={{
            position: 'absolute', inset: 0, borderRadius: '50%',
            border: `2px solid ${theme.accentColor}22`,
          }}/>
          <div style={{
            position: 'absolute', inset: 0, borderRadius: '50%',
            border: `2px solid transparent`,
            borderTopColor: theme.accentColor,
            borderRightColor: theme.accentColor,
            boxShadow: `0 0 40px ${theme.accentColor}99`,
            animation: 'chrono-spin 1.6s linear infinite',
          }}/>
          <div style={{
            position: 'absolute', inset: 0,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontFamily: ChronoTokens.font.display, fontSize: 32, fontWeight: 800,
            color: theme.accentColor, letterSpacing: '-0.02em',
            textShadow: `0 0 20px ${theme.accentColor}88`,
          }}>{Math.floor(progress * 100)}%</div>
        </div>

        <div style={{
          fontFamily: ChronoTokens.font.mono, fontSize: 11, fontWeight: 700,
          color: theme.accentColor, letterSpacing: '0.2em', textTransform: 'uppercase',
          marginBottom: 12,
          textShadow: theme.isDark ? `0 0 12px ${theme.accentColor}66` : 'none',
        }}>Tailoring Chrono</div>
        <h2 style={{
          fontFamily: ChronoTokens.font.display, fontSize: 32, fontWeight: 800,
          letterSpacing: '-0.03em', lineHeight: 1.1, margin: '0 0 24px',
        }}>Setting up your workspace.</h2>

        <div key={stageIdx} style={{
          minHeight: 28,
          fontFamily: ChronoTokens.font.mono, fontSize: 13, color: theme.textDim,
          animation: `chrono-fade-up 320ms ${Spring.std} both`,
        }}>{STAGES[stageIdx]?.label || ''}</div>

        <div style={{
          marginTop: 32,
          display: 'flex', flexDirection: 'column', gap: 6,
          textAlign: 'left',
          background: theme.isDark ? 'rgba(255,255,255,0.03)' : 'rgba(10,12,20,0.03)',
          border: `0.5px solid ${theme.hairline}`,
          padding: 18, borderRadius: 14,
        }}>
          {STAGES.map((s, i) => {
            const done = i < stageIdx;
            const active = i === stageIdx;
            return (
              <div key={i} style={{
                display: 'flex', alignItems: 'center', gap: 10,
                fontSize: 12.5,
                color: done ? theme.text : (active ? theme.accentColor : theme.textFaint),
                fontWeight: done || active ? 600 : 500,
                opacity: done || active ? 1 : 0.6,
                transition: 'all 240ms ease',
              }}>
                <span style={{
                  width: 14, height: 14, borderRadius: 999,
                  display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                  background: done ? theme.accentColor : 'transparent',
                  border: `1px solid ${active ? theme.accentColor : theme.hairline}`,
                  color: '#fff', fontSize: 9, fontWeight: 700,
                  boxShadow: active ? `0 0 8px ${theme.accentColor}99` : 'none',
                }}>{done ? '✓' : ''}</span>
                {s.label}
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

function buildTailoringStages(a) {
  const out = [];
  if (a.name || a.email) {
    out.push({
      label: 'Saving your identity…',
      apply: () => setProfile({
        name: a.name || 'Your name',
        email: a.email || 'you@chrono.app',
      }),
    });
  } else {
    out.push({ label: 'Creating your profile…', apply: () => {} });
  }
  out.push({
    label: 'Configuring focus rhythm…',
    apply: () => {
      const sessionLen = parseInt(a.sessionLen || '25', 10);
      const breakLen   = parseInt(a.breakLen   || '10', 10);
      setSettings({
        focusSessionMin: sessionLen,
        defaultDuration: Math.max(sessionLen, 30),
        aiBufferMin: breakLen,
      });
    },
  });
  out.push({
    label: 'Mapping your sleep window…',
    apply: () => setSettings({
      workStart: a.wake || '08:00',
      workEnd:   a.bed  || '23:00',
    }),
  });
  out.push({
    label: 'Tuning AI personality…',
    apply: () => setSettings({ aiPersonality: a.personality || 'concierge' }),
  });
  if (Array.isArray(a.domains) && a.domains.includes('gym')) {
    out.push({
      label: 'Protecting training slots…',
      apply: () => {/* gym preference stored with onboarding answers */},
    });
  }
  if (Array.isArray(a.procrastination) && a.procrastination.length) {
    out.push({
      label: 'Calibrating momentum nudges…',
      apply: () => {/* nudge toggles stored with onboarding answers */},
    });
  }
  out.push({
    label: 'Applying display preferences…',
    apply: () => setSettings({
      weekStartsOn: a.weekStart === 'mon' ? 1 : 0,
      timeFormat: a.timeFmt === '24' ? '24' : '12',
      notifLead: typeof a.lead === 'number' ? a.lead : 10,
      showHolidays: a.holidays !== false,
    }),
  });
  if (Array.isArray(a.integrations) && a.integrations.length) {
    out.push({
      label: 'Queueing integrations…',
      apply: () => {
        // Google / Outlook / iCloud are "coming soon" — selecting them just
        // records interest; only Canvas actually connects today.
        if (a.integrations.includes('canvas')) {
          const courses = (window.mockCanvasCourses ? window.mockCanvasCourses() : []);
          setIntegrations({
            canvas: true,
            canvasSchool: a.school || 'My school',
            canvasCourses: courses,
          });
          if (window.setCanvasEvents) window.setCanvasEvents(courses); // surface due dates on the calendar
        }
      },
    });
  } else {
    out.push({ label: 'Skipping integrations for now…', apply: () => {} });
  }
  out.push({
    label: 'Calibrating density and schedule pressure…',
    apply: () => {
      const buffer = ({ light: 5, moderate: 10, packed: 15, overwhelm: 20 })[a.density] || 10;
      setSettings({ aiBufferMin: buffer });
    },
  });
  out.push({
    label: 'Indexing your goals and domains…',
    apply: () => {/* goals + domains stored with onboarding answers */},
  });
  out.push({
    label: 'Almost ready…',
    apply: () => {},
  });
  return out;
}

// ═══════════════════════════════════════════════════════════════════════
// GUIDED TUTORIAL — spotlight + tooltip walkthrough.
// Steps are computed from survey answers so tips match the user's mix.
// ═══════════════════════════════════════════════════════════════════════
function buildTutorialSteps(answers = {}) {
  const a = answers;
  const has = (k) => Array.isArray(a.domains) && a.domains.includes(k);
  const role = a.role || '';
  const personality = a.personality || 'concierge';

  // Always-on baseline.
  const steps = [
    { view: 'week', target: 'sidebar',
      title: 'Your toolkit',
      body: 'Switch between Week, Month, Focus, AI, and your account from the sidebar.' },
    { view: 'week', target: 'week-grid',
      title: 'Week view',
      body: 'Your full week, scrollable. Click any event for details, edit, reschedule, or delete.' },
    { view: 'week', target: 'new-event',
      title: 'Create events',
      body: 'Hit + to open the composer. Your default duration from setup is pre-filled.' },
  ];

  // Domain-aware tips. Each only fires if the user opted in.
  if (has('school') || role === 'undergrad' || role === 'grad' || role === 'hs') {
    steps.push({ view: 'week', target: 'week-grid',
      title: 'School blocks',
      body: 'Tag events as Study or Canvas — assignments pulled from Canvas land on their due date and the AI will reverse-engineer study blocks back from there.' });
  }
  if (has('sidebuild') || role === 'solo-dev') {
    steps.push({ view: 'week', target: 'week-grid',
      title: 'Deep-work blocks for side projects',
      body: `Long blocks beat scattered hour-here, hour-there. Ask the AI for a 90-min Deep work block${a.devProject ? ` on ${a.devProject}` : ''} during your peak window.` });
  }
  if (has('pm') || role === 'pm') {
    steps.push({ view: 'week', target: 'week-grid',
      title: 'Project management cadence',
      body: `${a.pmCadence === 'weekly' ? 'Sprint planning every Monday, retro every Friday — ' : ''}Ask Chrono to template your standups, reviews, and 1:1s. Recurring events live alongside one-offs.` });
  }
  if (has('gym')) {
    const per = a.gymPerWeek || '3';
    const goal = a.gymGoal === 'strength' ? 'lifting'
               : a.gymGoal === 'hypertrophy' ? 'hypertrophy'
               : a.gymGoal === 'cardio' ? 'cardio'
               : 'training';
    steps.push({ view: 'week', target: 'week-grid',
      title: 'Training stays on the calendar',
      body: `${per}× ${goal} sessions per week, locked in. The AI won't pile work into your training slots, and never proposes a workout that collides with your bedtime.` });
  }
  if (has('growth') || has('habits')) {
    steps.push({ view: 'week', target: 'week-grid',
      title: 'Habits over heroics',
      body: 'Daily 15-min reading, journaling, or stretching blocks beat 90-min Sunday catch-ups. Ask the AI to "anchor a 15-min reading block after lunch."' });
  }

  // Focus, AI, profile, settings — always shown.
  steps.push({ view: 'focus', target: null,
    title: 'Focus sessions',
    body: 'Pick an event from the picker — the timer length matches the event duration. Start/stop with the play button.' });

  // Anti-procrastination — tailored tip if any procrastination signal flagged.
  if (Array.isArray(a.procrastination) && a.procrastination.length) {
    const tip = a.procrastination.includes('starting')   ? 'Tell the AI "I can\'t start" — it will propose a 15-min starter block instead of the full task.'
              : a.procrastination.includes('overwhelm')  ? 'Ask the AI to break a big project into the smallest next step. It will propose just that — not the whole tree.'
              : a.procrastination.includes('perfection') ? 'Set a 25-min "draft only" block. Done beats perfect — the AI won\'t schedule a polish pass until you ask.'
              : 'When momentum stalls, the AI will surface the next smallest step rather than the whole task.';
    steps.push({ view: 'week', target: 'ai-area',
      title: 'Anti-procrastination, built in',
      body: tip });
  }

  steps.push({ view: 'week', target: 'ai-area',
    title: 'AI assistant',
    body: `Plan your week, surface conflicts, propose focus blocks. ${personality === 'concierge' ? 'Concierge mode will anticipate next steps.' : personality === 'copilot' ? 'Co-pilot mode pairs with you on planning.' : 'Silent operator only speaks when asked.'} Show or hide it anytime with the AI button at the top right.` });

  steps.push({ view: 'profile', target: null,
    title: 'Your profile',
    body: 'Edit your name, email, and integrations any time. Stats update live from your event data.' });

  steps.push({ view: 'settings', target: null,
    title: 'Tune anything',
    body: "Every setting Chrono exposes — show/hide holidays, default duration, AI buffer, and more. That's your tour." });

  return steps;
}

function GuidedTutorial({ theme, answers, onDone }) {
  const TUTORIAL_STEPS = React.useMemo(() => buildTutorialSteps(answers), [answers]);
  const [step, setStep] = React.useState(0);
  const s = TUTORIAL_STEPS[step];
  const [rect, setRect] = React.useState(null);

  // Force the assistant popout open for the whole tour so new users see it,
  // then close it again on exit (its default state is hidden).
  React.useEffect(() => {
    setTutorialAI(true);
    return () => setTutorialAI(false);
  }, []);

  React.useEffect(() => {
    setTutorialView(s.view);
    // Keep the popout open if a later step closed/replaced the view.
    setTutorialAI(true);
  }, [step]);

  React.useEffect(() => {
    let raf;
    const measure = () => {
      if (!s.target) { setRect(null); return; }
      const el = document.querySelector(`[data-tutorial-id="${s.target}"]`);
      if (!el) { setRect(null); return; }
      const r = el.getBoundingClientRect();
      // gBCR is in scaled (visual) px under the dynamic UI zoom; the overlay
      // positions in layout px (re-scaled at render) — convert back.
      const sc = window.__chronoScale || 1;
      setRect({ x: r.left / sc, y: r.top / sc, w: r.width / sc, h: r.height / sc });
    };
    raf = requestAnimationFrame(() => raf = requestAnimationFrame(measure));
    window.addEventListener('resize', measure);
    return () => { cancelAnimationFrame(raf); window.removeEventListener('resize', measure); };
  }, [step]);

  const next = () => step < TUTORIAL_STEPS.length - 1 ? setStep(s => s + 1) : onDone();
  const back = () => step > 0 ? setStep(s => s - 1) : null;

  const tipPos = (() => {
    if (!rect) return { left: '50%', top: '50%', transform: 'translate(-50%, -50%)' };
    const TOOLTIP_W = 360, GAP = 16;
    const sc = window.__chronoScale || 1;
    const vw = window.innerWidth / sc, vh = window.innerHeight / sc; // virtual viewport (layout px)
    let left = rect.x + rect.w + GAP;
    let top  = rect.y + rect.h / 2 - 80;
    if (left + TOOLTIP_W > vw - 16) {
      left = rect.x - TOOLTIP_W - GAP;
      if (left < 16) {
        left = Math.max(16, Math.min(vw - TOOLTIP_W - 16, rect.x + rect.w / 2 - TOOLTIP_W / 2));
        top  = rect.y + rect.h + GAP;
      }
    }
    top = Math.max(16, Math.min(vh - 200, top));
    return { left, top };
  })();

  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 2147483647,
      pointerEvents: 'auto',
      fontFamily: ChronoTokens.font.ui,
    }}>
      {rect ? (
        <div style={{
          position: 'absolute',
          left: rect.x - 6, top: rect.y - 6,
          width: rect.w + 12, height: rect.h + 12,
          borderRadius: 14,
          border: `2px solid ${theme.accentColor}`,
          boxShadow: `0 0 0 9999px rgba(0,0,0,0.62), 0 0 40px ${theme.accentColor}, inset 0 0 30px ${theme.accentColor}33`,
          animation: 'chrono-pulse-ring 2.2s ease-in-out infinite',
          transition: 'all 360ms cubic-bezier(0.22, 1, 0.36, 1)',
          pointerEvents: 'none',
        }}/>
      ) : (
        <div style={{
          position: 'absolute', inset: 0,
          background: 'rgba(0,0,0,0.55)',
          backdropFilter: 'blur(2px)',
        }}/>
      )}

      <div style={{
        position: 'absolute', ...tipPos,
        width: 360, padding: 22, borderRadius: 16,
        background: theme.isDark ? 'rgba(20,22,32,0.98)' : 'rgba(255,255,255,0.99)',
        border: `0.5px solid ${theme.accentColor}55`,
        boxShadow: `0 30px 80px rgba(0,0,0,0.6), 0 0 40px ${theme.accentColor}33`,
        backdropFilter: 'blur(30px)',
        color: theme.text,
        animation: `chrono-scale-in 280ms ${Spring.std} both`,
      }}>
        <div style={{
          fontFamily: ChronoTokens.font.mono, fontSize: 10.5, fontWeight: 700,
          color: theme.accentColor, letterSpacing: '0.16em', textTransform: 'uppercase',
          marginBottom: 8,
          display: 'flex', alignItems: 'center', gap: 8,
        }}>
          <span>Tour · {step + 1} of {TUTORIAL_STEPS.length}</span>
          <div style={{
            flex: 1, height: 2, borderRadius: 1,
            background: theme.isDark ? 'rgba(255,255,255,0.08)' : 'rgba(10,12,20,0.08)',
            overflow: 'hidden',
          }}>
            <div style={{
              height: '100%', width: `${((step + 1) / TUTORIAL_STEPS.length) * 100}%`,
              background: theme.accentColor,
              boxShadow: `0 0 6px ${theme.accentColor}`,
              transition: 'width 320ms ease',
            }}/>
          </div>
        </div>
        <div style={{
          fontFamily: ChronoTokens.font.display, fontSize: 19, fontWeight: 800,
          letterSpacing: '-0.02em', marginBottom: 10,
        }}>{s.title}</div>
        <div style={{
          fontSize: 13.5, lineHeight: 1.55, color: theme.textDim,
          marginBottom: 18,
        }}>{s.body}</div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          {step > 0 && (
            <button onClick={back} style={{
              appearance: 'none', cursor: 'pointer', border: 'none', background: 'transparent',
              color: theme.textDim,
              fontFamily: ChronoTokens.font.ui, fontSize: 12.5, fontWeight: 600,
              padding: '6px 8px',
            }}>Back</button>
          )}
          <button onClick={onDone} style={{
            appearance: 'none', cursor: 'pointer', border: 'none', background: 'transparent',
            color: theme.textFaint,
            fontFamily: ChronoTokens.font.ui, fontSize: 12, fontWeight: 600,
            padding: '6px 8px',
          }}>Skip tour</button>
          <div style={{ flex: 1 }}/>
          <button onClick={next} className="chrono-tap" style={{
            appearance: 'none', cursor: 'pointer', border: 'none',
            padding: '8px 18px', borderRadius: 999,
            background: `linear-gradient(135deg, ${theme.accentColor}, ${theme.signal || theme.accentColor})`,
            color: '#fff',
            fontFamily: ChronoTokens.font.ui, fontSize: 12.5, fontWeight: 700,
            boxShadow: `0 4px 14px ${theme.accentColor}55`,
            display: 'inline-flex', alignItems: 'center', gap: 6,
          }}>
            {step === TUTORIAL_STEPS.length - 1 ? 'Get to work' : 'Next'}
            <Icon.fwd s={11}/>
          </button>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { OnboardingApp });
