/* CHAIRSIDE — App
   Modern SaaS / Masterclass aesthetic. Warm, premium, not stuffy.
   Six views: Home, Curriculum, Lesson, Scripts, Lexicon, Outcomes
*/
const { useState, useEffect, useMemo, useRef } = React;
const COURSE = window.CHAIRSIDE_COURSE;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "theme": "warm",
  "accent": "amber",
  "density": "regular"
}/*EDITMODE-END*/;

const THEMES = {
  warm: {
    bg: "#FAF7F2", surface: "#FFFFFF", surface2: "#F5F0E8", line: "#E8DFD0",
    text: "#1F1B16", soft: "#5A5247", mute: "#8A8170", ink: "#0F0D0A",
  },
  cream: {
    bg: "#F8F4EC", surface: "#FFFCF6", surface2: "#F0E9DA", line: "#E2D7BF",
    text: "#1A1612", soft: "#4F473B", mute: "#82785F", ink: "#0A0805",
  },
  dark: {
    bg: "#1A1612", surface: "#231E18", surface2: "#2A241D", line: "#3A3329",
    text: "#F5EDDC", soft: "#B5AB95", mute: "#827960", ink: "#FFFFFF",
  },
};

const ACCENTS = {
  amber: { c: "#C77A2A", c2: "#E89A4A", soft: "#FCEBD3" },
  rose: { c: "#B5536A", c2: "#D27086", soft: "#F7DCE3" },
  teal: { c: "#2A8A88", c2: "#4FAEAC", soft: "#D0EAE9" },
  indigo: { c: "#4B5495", c2: "#6E78B8", soft: "#DDE0F2" },
};

const ROLE_COLORS = {
  tc: "#C77A2A", fd: "#B5536A", hy: "#2A8A88", dr: "#4B5495", ow: "#5A5247", all: "#8A8170",
};

const DEFAULT_PRACTICE_ROLE = "tc";

// The member's own Chairside role id (tc/fd/hy/dr/ow), resolved once from the
// server-written suite role (WS1: /api/me.role via suiteRoleToChairside in
// chairside-shared.jsx). Module-scoped because modulePracticeHref is called
// from deep components; App's setState after the fetch re-renders the tree so
// every call site picks it up. null = unknown or unmapped => "tc" fallback,
// exactly the old behavior (fail open).
let USER_PRACTICE_ROLE = null;

function defaultPracticeHref() {
  return `Chairside-Roles.html#role=${USER_PRACTICE_ROLE || DEFAULT_PRACTICE_ROLE}&view=practice&launch=1`;
}

const useStored = (key, init) => {
  const [v, set] = useState(() => {
    try { const r = localStorage.getItem(key); return r ? JSON.parse(r) : init; } catch { return init; }
  });
  useEffect(() => { try { localStorage.setItem(key, JSON.stringify(v)); } catch {} }, [key, v]);
  return [v, set];
};

function GlobalStyle({ tokens, accent }) {
  return (
    <style>{`
      :root {
        --bg: ${tokens.bg}; --surface: ${tokens.surface}; --surface2: ${tokens.surface2};
        --line: ${tokens.line}; --text: ${tokens.text}; --soft: ${tokens.soft};
        --mute: ${tokens.mute}; --ink: ${tokens.ink};
        --accent: ${accent.c}; --accent2: ${accent.c2}; --accent-soft: ${accent.soft};
      }
      * { box-sizing: border-box; }
      html, body { margin: 0; padding: 0; }
      body {
        background: var(--bg); color: var(--text);
        font-family: 'Inter', system-ui, -apple-system, sans-serif;
        font-size: 15px; line-height: 1.55;
        -webkit-font-smoothing: antialiased;
        font-feature-settings: "ss01", "kern", "cv11";
      }
      h1, h2, h3, h4 { font-family: 'Fraunces', Georgia, serif; font-weight: 400; letter-spacing: -0.02em; margin: 0; color: var(--text); }
      h1 { font-size: clamp(48px, 6vw, 84px); line-height: 0.98; letter-spacing: -0.028em; }
      h2 { font-size: clamp(32px, 3.6vw, 52px); line-height: 1.05; letter-spacing: -0.022em; }
      h3 { font-size: clamp(22px, 2vw, 28px); line-height: 1.2; }
      .display { font-family: 'Fraunces', serif; }
      .mono { font-family: 'JetBrains Mono', ui-monospace, monospace; letter-spacing: 0.02em; font-size: 12px; }
      .eyebrow { font-family: 'JetBrains Mono', monospace; font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; }
      ::selection { background: var(--accent); color: white; }
      a { color: inherit; text-decoration: none; }
      button { font-family: inherit; cursor: pointer; }

      .btn {
        display: inline-flex; align-items: center; gap: 8px;
        padding: 12px 20px; border-radius: 999px;
        background: var(--surface); border: 1px solid var(--line); color: var(--text);
        font-size: 14px; font-weight: 500;
        transition: all .2s ease;
      }
      .btn:hover { border-color: var(--text); transform: translateY(-1px); }
      .btn.primary { background: var(--ink); color: var(--bg); border-color: var(--ink); }
      .btn.primary:hover { background: var(--accent); border-color: var(--accent); color: white; }
      .btn.accent { background: var(--accent); color: white; border-color: var(--accent); }
      .btn.accent:hover { background: var(--accent2); border-color: var(--accent2); }
      .btn.ghost { background: transparent; border-color: transparent; }
      .btn.ghost:hover { background: var(--surface); border-color: var(--line); }

      .card {
        background: var(--surface); border: 1px solid var(--line); border-radius: 18px;
        transition: border-color .25s ease, transform .25s ease, box-shadow .25s ease;
      }
      .card:hover { border-color: color-mix(in srgb, var(--text) 20%, var(--line)); }
      .card.lift:hover { transform: translateY(-3px); box-shadow: 0 18px 40px -20px rgba(0,0,0,.18); }

      input, textarea, select {
        font-family: inherit; font-size: 14px; color: var(--text);
        background: var(--surface); border: 1px solid var(--line); border-radius: 12px;
        padding: 12px 14px; outline: none; width: 100%;
        transition: border-color .2s ease, box-shadow .2s ease;
      }
      input:focus, textarea:focus, select:focus {
        border-color: var(--accent);
        box-shadow: 0 0 0 3px var(--accent-soft);
      }
      textarea { min-height: 110px; line-height: 1.6; resize: vertical; }

      details > summary { list-style: none; cursor: pointer; }
      details > summary::-webkit-details-marker { display: none; }

      @keyframes fadeUp { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
      .fade-up { opacity: 1; }
      @media (prefers-reduced-motion: no-preference) {
        .fade-up { animation: fadeUp .7s ease both; }
      }

      .pill {
        display: inline-flex; align-items: center; gap: 6px;
        padding: 4px 10px; border-radius: 999px;
        background: var(--accent-soft); color: var(--accent);
        font-family: 'JetBrains Mono', monospace; font-size: 10px; letter-spacing: 0.1em;
        text-transform: uppercase; font-weight: 500;
      }
      .pill.role-tc { background: #FCEBD3; color: #8A4F15; }
      .pill.role-fd { background: #F7DCE3; color: #7A2A40; }
      .pill.role-hy { background: #D0EAE9; color: #1B5856; }
      .pill.role-dr { background: #DDE0F2; color: #2D356A; }
      .pill.role-ow { background: #E8E2D5; color: #3D362A; }
      .pill.role-all { background: var(--surface2); color: var(--soft); }

      .chip {
        padding: 8px 14px; border-radius: 999px; border: 1px solid var(--line);
        background: var(--surface); font-size: 13px; color: var(--soft);
        transition: all .2s ease;
      }
      .chip.active { background: var(--ink); color: var(--bg); border-color: var(--ink); }

      .rule { height: 1px; background: var(--line); }

      @keyframes cheatFadeIn { from { opacity: 0; } to { opacity: 1; } }
      @keyframes cheatSlideIn { from { transform: translateX(100%); } to { transform: translateX(0); } }
      .cheat-backdrop { animation: cheatFadeIn .2s ease both; }
      .cheat-drawer { animation: cheatFadeIn .2s ease both; }
      @media (prefers-reduced-motion: no-preference) {
        .cheat-drawer { animation: cheatSlideIn .28s cubic-bezier(.22,.61,.36,1) both; }
      }
      @media (max-width: 760px) {
        .chairside-course-header-inner {
          display: grid !important; grid-template-columns: minmax(0, 1fr) auto !important;
          gap: 8px 12px !important; padding: 10px 16px 8px !important;
        }
        .chairside-local-nav {
          grid-column: 1 / -1; grid-row: 2; width: 100%; overflow-x: auto;
          justify-content: flex-start !important; scrollbar-width: none;
        }
        .chairside-local-nav::-webkit-scrollbar { display: none; }
        .chairside-local-nav button { flex: 0 0 auto; padding: 8px 12px !important; }
        .chairside-continue-button { display: none !important; }
      }
    `}</style>
  );
}

// ─── Logo
function Logo({ size = 32, accent }) {
  return (
    <svg width={size} height={size} viewBox="0 0 40 40" aria-hidden>
      <rect width="40" height="40" rx="10" fill={accent} />
      <path d="M12 14 Q20 10 28 14 L28 24 Q20 28 12 24 Z" fill="white" opacity="0.95" />
      <circle cx="20" cy="19" r="2.5" fill={accent} />
    </svg>
  );
}

// ─── Chrome
function Chrome({ view, setView, tokens, accent, progress }) {
  const items = [
    { k: "home", label: "Overview" },
    { k: "curriculum", label: "Modules" },
    { k: "scripts", label: "Resources" },
  ];
  const lessons = COURSE.modules.flatMap(module => module.lessons.map(lesson => ({ moduleId: module.id, lessonId: lesson.id })));
  const nextLesson = lessons.find(lesson => !progress[lesson.lessonId]?.done) || lessons[0];
  const courseComplete = lessons.length > 0 && lessons.every(lesson => progress[lesson.lessonId]?.done);
  return (
    <header style={{
      position: "sticky", top: 0, zIndex: 40,
      backdropFilter: "blur(16px) saturate(140%)",
      background: `color-mix(in srgb, ${tokens.bg} 82%, transparent)`,
      borderBottom: `1px solid ${tokens.line}`,
    }}>
      <div className="chairside-course-header-inner" style={{ maxWidth: 1320, margin: "0 auto", padding: "14px 28px", display: "flex", alignItems: "center", justifyContent: "space-between", gap: 24 }}>
        <button onClick={() => setView({ name: "home" })} style={{ background: "none", border: "none", padding: 0, display: "flex", alignItems: "center", gap: 12, color: tokens.text }}>
          <Logo size={32} accent={accent.c} />
          <div style={{ textAlign: "left", lineHeight: 1.1 }}>
            <div className="display" style={{ fontSize: 22, letterSpacing: "-0.02em" }}>Chairside</div>
            <div className="mono" style={{ color: tokens.mute, marginTop: 2, fontSize: 10 }}>The art of the yes that helps</div>
          </div>
        </button>
        <nav className="chairside-local-nav" aria-label="Chairside course" style={{ display: "flex", gap: 4 }}>
          {items.map(it => {
            const active = view.name === it.k ||
              (it.k === "curriculum" && view.name === "lesson") ||
              (it.k === "scripts" && view.name === "lexicon");
            const baseStyle = {
              background: active ? tokens.surface2 : "none", border: "none",
              padding: "8px 14px", borderRadius: 999,
              fontSize: 14, fontWeight: 500,
              color: active ? tokens.text : tokens.soft,
              transition: "all .2s ease",
              cursor: "pointer", fontFamily: "inherit",
            };
            return (
              <button key={it.k} onClick={() => setView({ name: it.k })} style={baseStyle}>
                {it.label}
              </button>
            );
          })}
        </nav>
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <button className="btn primary chairside-continue-button" onClick={() => setView({ name: "lesson", moduleId: nextLesson.moduleId, lessonId: nextLesson.lessonId })}>
            {courseComplete ? "Review" : "Continue"} →
          </button>
          {window.SuiteAccountMenu && <window.SuiteAccountMenu tokens={tokens} accent={accent} />}
        </div>
      </div>
    </header>
  );
}

// ─── Home
function Home({ tokens, accent, setView, progress, memberRoleId }) {
  const useIsPhoneHook = window.useIsPhone || (() => false);
  const isPhone = useIsPhoneHook();
  const total = COURSE.modules.reduce((a, m) => a + m.lessons.length, 0);
  const done = Object.keys(progress).filter(k => progress[k]?.done).length;
  const pct = total ? Math.round((done / total) * 100) : 0;

  // Operator-dashboard helpers. "Practice with AI" lives on the Roles surface
  // (Chairside-Roles.html), which loads the roleplay engine. The course home
  // surfaces it via deep-link rather than mounting the engine here.
  const allLessons = COURSE.modules.flatMap(m =>
    m.lessons.map(l => ({
      id: l.id, title: l.title, brief: l.brief, duration: l.duration,
      moduleId: m.id, moduleNumber: m.number, moduleTitle: m.title,
    }))
  );
  const nextLesson = allLessons.find(l => !progress[l.id]?.done) || allLessons[0];
  const nextModule = COURSE.modules.find(m => m.id === nextLesson.moduleId);
  const nextPracticeHref = nextModule ? modulePracticeHref(nextModule) : defaultPracticeHref();
  const nextPracticeRoleName = nextModule ? moduleRoleName(nextModule) : "team";
  const memberRole = COURSE.roles.find(role => role.id === memberRoleId) || null;

  // A5 — habit loop. Derive a practice streak from lesson-completion timestamps
  // already stored in progress (local day, no new storage). A day counts if any
  // lesson was completed that day; the streak is the run ending today/yesterday.
  const dayKey = (d) => { const x = new Date(d); return `${x.getFullYear()}-${x.getMonth()}-${x.getDate()}`; };
  const activeDays = new Set(Object.values(progress).map(p => p && p.at).filter(Boolean).map(dayKey));
  const todayKey = dayKey(Date.now());
  const practicedToday = activeDays.has(todayKey);
  let streak = 0;
  if (practicedToday || activeDays.has(dayKey(Date.now() - 86400000))) {
    let cur = practicedToday ? Date.now() : Date.now() - 86400000;
    while (activeDays.has(dayKey(cur))) { streak++; cur -= 86400000; }
  }

  return (
    <main>
      {/* DASHBOARD HERO */}
      <section style={{ position: "relative", padding: isPhone ? "48px 20px 36px" : "84px 28px 64px", overflow: "hidden" }}>
        <div style={{ position: "absolute", inset: 0, background: `radial-gradient(60% 60% at 80% 20%, ${accent.soft} 0%, transparent 60%)`, opacity: .8, pointerEvents: "none" }} />
        <div style={{ position: "relative", maxWidth: 1320, margin: "0 auto", display: "grid", gridTemplateColumns: isPhone ? "1fr" : "1.4fr 1fr", gap: isPhone ? 28 : 64, alignItems: "center" }}>
          <div className="fade-up">
            <div className="pill" style={{ marginBottom: 24 }}>● {pct > 0 ? `${pct}% through the course` : "Your practice"}</div>
            <h1 className="display" style={{ marginBottom: 22, fontSize: isPhone ? "clamp(34px, 9vw, 46px)" : undefined, overflowWrap: "break-word" }}>
              Practice the<br/>conversations that<br/>change the case.
            </h1>
            <p style={{ fontSize: 18, lineHeight: 1.55, color: tokens.soft, maxWidth: 540, marginBottom: 32 }}>
              Twelve modules on rapport, presentation, and the conversations every operatory has but few are trained for — plus live practice against an AI patient, scored turn by turn.
            </p>
            <div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>
              <button className="btn primary" style={{ padding: "16px 28px", fontSize: 15 }} onClick={() => setView({ name: "lesson", moduleId: nextLesson.moduleId, lessonId: nextLesson.id })}>
                {pct > 0 ? "Continue training" : "Start Module 1"} <span>→</span>
              </button>
              <a className="btn accent" href={nextPracticeHref} style={{ padding: "16px 24px", fontSize: 15, textDecoration: "none" }}>
                Practice with AI →
              </a>
            </div>
            {pct > 0 && (
              <div style={{ marginTop: 26, display: "flex", alignItems: "center", gap: 14 }}>
                <div style={{ width: 200, height: 4, background: tokens.line, borderRadius: 2, overflow: "hidden" }}>
                  <div style={{ height: "100%", background: accent.c, width: `${pct}%`, transition: "width .4s ease" }} />
                </div>
                <span className="mono" style={{ color: tokens.mute }}>{done}/{total} lessons · {pct}%</span>
              </div>
            )}
          </div>
          <div style={{ position: "relative" }}>
            <button onClick={() => setView({ name: "lesson", moduleId: nextLesson.moduleId, lessonId: nextLesson.id })}
              className="card lift" style={{ padding: 28, textAlign: "left", width: "100%", cursor: "pointer", display: "block" }}>
              <div className="eyebrow" style={{ color: accent.c, marginBottom: 14 }}>{pct > 0 ? "Pick up where you left off" : "Start here"}</div>
              <div className="mono" style={{ color: tokens.mute, fontSize: 11, marginBottom: 8 }}>Module {nextLesson.moduleNumber} · {nextLesson.moduleTitle}</div>
              <h3 className="display" style={{ fontSize: 24, marginBottom: 10 }}>{nextLesson.title}</h3>
              <p style={{ color: tokens.soft, fontSize: 14, lineHeight: 1.5, marginBottom: 16 }}>{nextLesson.brief}</p>
              <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
                <span className="mono" style={{ color: tokens.mute, fontSize: 11 }}>● {nextLesson.duration} min</span>
                <span style={{ color: accent.c, fontSize: 14, fontWeight: 500 }}>Open lesson →</span>
              </div>
            </button>
          </div>
        </div>
      </section>

      {/* A5 — Daily practice / streak (habit loop) */}
      <section style={{ padding: isPhone ? "0 20px 8px" : "0 28px 8px" }}>
        <div style={{ maxWidth: 1320, margin: "0 auto" }}>
          <div className="card" style={{ padding: isPhone ? "16px 18px" : "18px 24px", display: "flex", alignItems: "center", justifyContent: "space-between", gap: 18, flexWrap: "wrap" }}>
            <div style={{ display: "flex", alignItems: "center", gap: 16, minWidth: 0 }}>
              <div style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", width: 54, height: 54, borderRadius: 14, background: accent.soft, color: accent.c, flexShrink: 0 }}>
                <span className="display" style={{ fontSize: 24, lineHeight: 1 }}>{streak}</span>
                <span className="mono" style={{ fontSize: 8, letterSpacing: "0.08em" }}>DAY{streak === 1 ? "" : "S"}</span>
              </div>
              <div style={{ minWidth: 0 }}>
                <div style={{ fontSize: 15, fontWeight: 500, color: tokens.text }}>
                  {practicedToday ? "You practiced today." : streak > 0 ? "Keep the streak alive." : "Start a streak today."}
                </div>
                <div style={{ fontSize: 13, color: tokens.soft, marginTop: 2, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
                  {practicedToday ? `${streak}-day streak — come back tomorrow.` : `Today's rep: ${nextLesson.title}`}
                </div>
              </div>
            </div>
            <div style={{ display: "flex", gap: 10, flexWrap: "wrap" }}>
              <button className="btn" onClick={() => setView({ name: "lesson", moduleId: nextLesson.moduleId, lessonId: nextLesson.id })} style={{ fontSize: 13 }}>
                {practicedToday ? "Next lesson" : "Today's lesson"} →
              </button>
              <a className="btn accent" href={nextPracticeHref} style={{ fontSize: 13, textDecoration: "none" }}>Practice with AI →</a>
            </div>
          </div>
        </div>
      </section>

      <LearningLoopPanel
        tokens={tokens}
        accent={accent}
        isPhone={isPhone}
        nextLesson={nextLesson}
        nextPracticeHref={nextPracticeHref}
        nextPracticeRoleName={nextPracticeRoleName}
        setView={setView}
      />

      {/* LIVE PRACTICE — the AI simulator, surfaced as a co-hero */}
      <section style={{ padding: isPhone ? "20px 20px 56px" : "20px 28px 80px" }}>
        <div style={{ maxWidth: 1320, margin: "0 auto" }}>
          <div className="card" style={{ padding: isPhone ? "28px 24px" : "40px 44px", background: tokens.ink, border: "none", borderRadius: 24, display: "grid", gridTemplateColumns: isPhone ? "1fr" : "1.5fr 1fr", gap: isPhone ? 24 : 40, alignItems: "center" }}>
            <div>
              <div className="eyebrow" style={{ color: accent.c2, marginBottom: 14 }}>Live practice</div>
              <h2 className="display" style={{ fontSize: 34, lineHeight: 1.08, marginBottom: 14, color: tokens.bg }}>
                Rehearse with an AI patient.<br/>Get scored. Run it again.
              </h2>
              <p style={{ fontSize: 16, lineHeight: 1.55, color: `color-mix(in srgb, ${tokens.bg} 72%, transparent)`, maxWidth: 540, margin: 0 }}>
                Every script in the role library runs as a live roleplay — beginner to expert, by voice or text — with a rubric and per-turn coaching. Reading builds understanding; reps build fluency.
              </p>
            </div>
            <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
              <a className="btn accent" href={nextPracticeHref} style={{ padding: "16px 24px", fontSize: 15, justifyContent: "center", textDecoration: "none" }}>
                Start a roleplay →
              </a>
              <a className="btn" href="Chairside-Roles.html" style={{ padding: "14px 22px", justifyContent: "center", textDecoration: "none", background: "transparent", color: tokens.bg, borderColor: `color-mix(in srgb, ${tokens.bg} 35%, transparent)` }}>
                Browse role scripts
              </a>
            </div>
          </div>
        </div>
      </section>

      {/* WHAT YOU LEARN — Modules grid */}
      <section style={{ padding: "80px 28px" }}>
        <div style={{ maxWidth: 1320, margin: "0 auto" }}>
          <div style={{ display: "flex", alignItems: "end", justifyContent: "space-between", marginBottom: 44, gap: 24 }}>
            <div>
              <div className="eyebrow" style={{ color: accent.c, marginBottom: 12 }}>The curriculum</div>
              <h2 className="display">Twelve modules. The whole patient journey.</h2>
            </div>
            <button className="btn ghost" onClick={() => setView({ name: "curriculum" })}>View all →</button>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: isPhone ? "1fr" : "repeat(3, 1fr)", gap: 16 }}>
            {COURSE.modules.map((m, i) => (
              <button key={m.id} onClick={() => setView({ name: "lesson", moduleId: m.id, lessonId: m.lessons[0].id })}
                className="card lift"
                style={{ padding: 24, textAlign: "left", cursor: "pointer", display: "flex", flexDirection: "column", gap: 14, minHeight: 200 }}>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
                  <span className="mono" style={{ color: accent.c, fontSize: 14, fontWeight: 500 }}>{m.number}</span>
                  <span className={`pill role-${m.role}`}>{m.kicker}</span>
                </div>
                <h3 className="display" style={{ fontSize: 22, marginTop: 4 }}>{m.title}</h3>
                <p style={{ color: tokens.soft, fontSize: 14, lineHeight: 1.5, marginBottom: "auto" }}>{m.synopsis.slice(0, 110)}…</p>
                <div style={{ display: "flex", justifyContent: "space-between", color: tokens.mute, fontSize: 12 }}>
                  <span>{m.lessons.length} lessons</span>
                  <span>{m.hours}h</span>
                </div>
              </button>
            ))}
          </div>
        </div>
      </section>

      {/* PERSONALIZED ROLE PATH */}
      <section style={{ padding: "80px 28px", background: tokens.surface2 }}>
        <div style={{ maxWidth: 1320, margin: "0 auto" }}>
          <div className="card" style={{ padding: isPhone ? 24 : 32, borderRadius: 8, display: "grid", gridTemplateColumns: isPhone ? "1fr" : "auto 1fr auto", gap: isPhone ? 20 : 28, alignItems: "center" }}>
            <div style={{ width: 56, height: 56, borderRadius: 8, background: memberRole ? ROLE_COLORS[memberRole.id] : accent.c, color: "white", display: "flex", alignItems: "center", justifyContent: "center", fontWeight: 600, fontSize: 16 }}>
              {memberRole ? memberRole.name.split(" ").map(part => part[0]).join("").slice(0, 2) : "YP"}
            </div>
            <div>
              <div className="eyebrow" style={{ color: accent.c, marginBottom: 8 }}>{memberRole ? "Your path" : "Role-based paths"}</div>
              <h2 className="display" style={{ fontSize: isPhone ? 28 : 34, marginBottom: 8 }}>{memberRole ? memberRole.name : "Find the modules for your seat."}</h2>
              <p style={{ color: tokens.soft, fontSize: 15, lineHeight: 1.6, maxWidth: 650, margin: 0 }}>
                {memberRole ? memberRole.desc : "Use the role filters in Modules to bring the most relevant lessons forward. The full course always stays available."}
              </p>
            </div>
            <div style={{ display: "flex", gap: 10, flexWrap: "wrap", justifyContent: isPhone ? "flex-start" : "flex-end" }}>
              <button className="btn" onClick={() => setView({ name: "curriculum", roleId: memberRole ? memberRole.id : null })}>
                {memberRole ? "Recommended modules" : "View modules"} →
              </button>
              {memberRole && (
                <a className="btn accent" href={`Chairside-Roles.html#role=${memberRole.id}&view=practice&launch=1`} style={{ textDecoration: "none" }}>
                  Practice as {memberRole.name.replace(/s$/, "")} →
                </a>
              )}
            </div>
          </div>
        </div>
      </section>

      {/* Faculty / cohort-outcomes / testimonials intentionally removed.
          This surface is an internal team tool behind authenticated access — the
          people here are already enrolled, so marketing proof reads as filler
          (and the testimonials/faculty were placeholder). The underlying data
          (COURSE.outcomes / faculty / testimonials) is kept in chairside-data.jsx
          for a future public marketing page. */}

      {/* CTA */}
      <section style={{ padding: "100px 28px" }}>
        <div style={{ maxWidth: 900, margin: "0 auto", textAlign: "center" }}>
          <h2 className="display" style={{ marginBottom: 20 }}>{pct > 0 ? "Keep the reps going." : "Start with one conversation."}</h2>
          <p style={{ fontSize: 17, color: tokens.soft, marginBottom: 32, maxWidth: 540, marginInline: "auto" }}>
            Read the lesson, then rehearse it against an AI patient until the words are yours. Understanding comes from reading; fluency comes from reps.
          </p>
          <div style={{ display: "flex", gap: 12, justifyContent: "center", flexWrap: "wrap" }}>
            <button className="btn primary" style={{ padding: "16px 28px", fontSize: 15 }} onClick={() => setView({ name: "lesson", moduleId: nextLesson.moduleId, lessonId: nextLesson.id })}>
              {pct > 0 ? "Continue training" : "Start Module 1"} →
            </button>
            <a className="btn accent" href={nextPracticeHref} style={{ padding: "16px 24px", fontSize: 15, textDecoration: "none" }}>
              Practice with AI →
            </a>
          </div>
        </div>
      </section>

      <footer style={{ borderTop: `1px solid ${tokens.line}`, padding: "32px 28px" }}>
        <div style={{ maxWidth: 1320, margin: "0 auto", display: "flex", justifyContent: "space-between", alignItems: "center" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <Logo size={24} accent={accent.c} />
            <span className="display" style={{ fontSize: 17 }}>Chairside</span>
            <span className="mono" style={{ color: tokens.mute }}>· {COURSE.brand.edition}</span>
          </div>
          <span style={{ color: tokens.mute, fontSize: 13 }}>{COURSE.brand.tagline}</span>
        </div>
      </footer>
    </main>
  );
}

function LearningLoopPanel({ tokens, accent, isPhone, nextLesson, nextPracticeHref, nextPracticeRoleName, setView }) {
  const steps = [
    {
      k: "01",
      eyebrow: "Learn",
      title: nextLesson.title,
      body: `${nextLesson.duration} min · Module ${nextLesson.moduleNumber}`,
      action: "Open lesson",
      onClick: () => setView({ name: "lesson", moduleId: nextLesson.moduleId, lessonId: nextLesson.id }),
    },
    {
      k: "02",
      eyebrow: "Practice",
      title: `${nextPracticeRoleName} AI rep`,
      body: "Run the conversation while the lesson is fresh.",
      action: "Start roleplay",
      href: nextPracticeHref,
    },
    {
      k: "03",
      eyebrow: "Review",
      title: "Practice report",
      body: "Use the score and coaching notes to choose the next rep.",
      action: "Progress",
      href: "Chairside-My-Practice.html",
    },
  ];

  return (
    <section style={{ padding: isPhone ? "18px 20px 16px" : "24px 28px 28px" }}>
      <div style={{ maxWidth: 1320, margin: "0 auto" }}>
        <div style={{ display: "grid", gridTemplateColumns: isPhone ? "1fr" : "0.8fr 1.4fr", gap: isPhone ? 16 : 28, alignItems: "start" }}>
          <div>
            <div className="eyebrow" style={{ color: accent.c, marginBottom: 10 }}>Your training loop</div>
            <h2 className="display" style={{ fontSize: isPhone ? 28 : 34, lineHeight: 1.08, marginBottom: 10 }}>Learn it. Rehearse it. Review it.</h2>
            <p style={{ margin: 0, color: tokens.soft, fontSize: 15, lineHeight: 1.6 }}>
              Chairside works best as a short cycle: lesson, live rep, report, repeat.
            </p>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: isPhone ? "1fr" : "repeat(3, minmax(0, 1fr))", gap: 10 }}>
            {steps.map(step => {
              const content = (
                <>
                  <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, marginBottom: 18 }}>
                    <span className="mono" style={{ color: accent.c, fontSize: 11 }}>{step.k}</span>
                    <span className="eyebrow" style={{ color: tokens.mute, fontSize: 10 }}>{step.eyebrow}</span>
                  </div>
                  <h3 className="display" style={{ fontSize: 20, lineHeight: 1.18, marginBottom: 8 }}>{step.title}</h3>
                  <p style={{ color: tokens.soft, fontSize: 13, lineHeight: 1.5, margin: "0 0 18px" }}>{step.body}</p>
                  <span style={{ marginTop: "auto", color: step.href ? accent.c : tokens.text, fontSize: 13, fontWeight: 500 }}>{step.action} →</span>
                </>
              );
              const baseStyle = {
                minHeight: 190,
                padding: 20,
                textAlign: "left",
                color: tokens.text,
                display: "flex",
                flexDirection: "column",
                borderRadius: 16,
                border: `1px solid ${tokens.line}`,
                background: tokens.surface,
                textDecoration: "none",
              };
              return step.href ? (
                <a key={step.k} className="card lift" href={step.href} style={baseStyle}>{content}</a>
              ) : (
                <button key={step.k} className="card lift" onClick={step.onClick} style={{ ...baseStyle, cursor: "pointer", fontFamily: "inherit" }}>{content}</button>
              );
            })}
          </div>
        </div>
      </div>
    </section>
  );
}

function moduleLessonDoneCount(module, progress) {
  return module.lessons.filter(lesson => progress[lesson.id]?.done).length;
}

function moduleDrillMeta(module, progress) {
  const drillLessons = module.lessons.filter(lesson => lesson.drill);
  return {
    total: drillLessons.length,
    done: drillLessons.filter(lesson => progress[lesson.id]?.drillDone).length,
  };
}

function moduleDisplayName(module) {
  if (!/^\d+$/.test(String(module.number))) return `Volume ${module.number}`;
  return `Module ${module.number}`;
}

function modulePracticeHref(module) {
  const roleIds = new Set((COURSE.roles || []).map(role => role.id));
  // A module with a specific lead role practices that role's scripts; a
  // whole-team module practices the MEMBER's role when the server knows it.
  const role = module.role && module.role !== "all" && roleIds.has(module.role)
    ? module.role
    : (USER_PRACTICE_ROLE || DEFAULT_PRACTICE_ROLE);
  return `Chairside-Roles.html#role=${role}&view=practice&launch=1`;
}

function moduleRoleName(module) {
  if (module.role === "all") return "team";
  return COURSE.roles.find(role => role.id === module.role)?.name || "team";
}

function nextModuleFor(module) {
  const idx = COURSE.modules.findIndex(item => item.id === module.id);
  return idx >= 0 ? COURSE.modules[idx + 1] : null;
}

function buildGuideSummary(module, nextModule, drillMeta) {
  const lessonWord = module.lessons.length === 1 ? "lesson" : "lessons";
  const drillPhrase = drillMeta.total === 0
    ? "Then one AI practice rep"
    : `Then ${drillMeta.total === 1 ? "the drill" : `${drillMeta.total} drills`} and one AI practice rep`;
  const destination = nextModule
    ? `before ${moduleDisplayName(nextModule)}.`
    : "to close the Chairside path.";
  return `Briefing first. ${module.lessons.length} ${lessonWord} next. ${drillPhrase} ${destination}`;
}

function moduleNextLesson(module, progress) {
  return (
    module.lessons.find(lesson => !progress[lesson.id]?.done) ||
    module.lessons.find(lesson => lesson.drill && !progress[lesson.id]?.drillDone) ||
    module.lessons[0]
  );
}

function ModuleCurriculumGuide({ module, progress, setView, tokens, accent, isPhone }) {
  const doneCount = moduleLessonDoneCount(module, progress);
  const drillMeta = moduleDrillMeta(module, progress);
  const nextLesson = moduleNextLesson(module, progress);
  const nextModule = nextModuleFor(module);
  const lessonTotal = module.lessons.length;
  const moduleDone = doneCount === lessonTotal;
  const drillsDone = drillMeta.total === 0 || drillMeta.done === drillMeta.total;
  const steps = [
    { label: "Briefing", detail: doneCount > 0 ? "Started" : "Watch first", done: doneCount > 0, active: doneCount === 0 },
    { label: "Lessons", detail: `${doneCount}/${lessonTotal} complete`, done: moduleDone, active: doneCount > 0 && !moduleDone },
    { label: "Drill", detail: drillMeta.total ? `${drillMeta.done}/${drillMeta.total} complete` : "No drill", done: drillsDone, active: moduleDone && !drillsDone },
    { label: "Practice", detail: `${moduleRoleName(module)} roleplay`, done: false, active: moduleDone && drillsDone },
  ];

  return (
    <div style={{ marginTop: 22, paddingTop: 22, borderTop: `1px solid ${tokens.line}` }}>
      <div style={{ display: "grid", gridTemplateColumns: isPhone ? "1fr" : "1fr auto", gap: 18, alignItems: "center" }}>
        <div>
          <div className="eyebrow" style={{ color: accent.c, marginBottom: 8 }}>
            {moduleDone ? "Review path" : "Start here"}
          </div>
          <div style={{ fontSize: 15, color: tokens.soft, lineHeight: 1.55 }}>
            {buildGuideSummary(module, nextModule, drillMeta)}
          </div>
        </div>
        <button
          className="btn primary"
          onClick={() => setView({ name: "lesson", moduleId: module.id, lessonId: nextLesson.id })}
          style={{ justifyContent: "center" }}
        >
          {moduleDone && drillsDone ? `Review ${moduleDisplayName(module)}` : moduleDone ? "Open drill" : "Open next step"} →
        </button>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: isPhone ? "1fr 1fr" : "repeat(4, 1fr)", gap: 8, marginTop: 18 }}>
        {steps.map(step => (
          <div key={step.label} style={{
            minHeight: 70, padding: "12px 12px", borderRadius: 12,
            background: step.active ? accent.soft : tokens.surface2,
            border: `1px solid ${step.active ? `${accent.c}55` : tokens.line}`,
          }}>
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }}>
              <span style={{
                width: 12, height: 12, borderRadius: 999, flexShrink: 0,
                background: step.done ? accent.c : step.active ? "transparent" : tokens.line,
                border: `1.5px solid ${step.done || step.active ? accent.c : tokens.line}`,
              }} />
              <span className="mono" style={{ color: step.active ? accent.c : tokens.mute, fontSize: 10 }}>{step.label}</span>
            </div>
            <div style={{ fontSize: 12, lineHeight: 1.35, color: tokens.soft }}>{step.detail}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

function ModuleLessonGuide({ module, lesson, progress, setView, tokens, accent, isPhone }) {
  const doneCount = moduleLessonDoneCount(module, progress);
  const drillMeta = moduleDrillMeta(module, progress);
  const practiceHref = modulePracticeHref(module);

  return (
    <div style={{
      marginBottom: 30, padding: isPhone ? "16px 16px" : "18px 20px",
      border: `1px solid ${tokens.line}`, borderRadius: 16,
      background: tokens.surface,
    }}>
      <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 16, marginBottom: 16, flexWrap: "wrap" }}>
        <div>
          <div className="eyebrow" style={{ color: accent.c, marginBottom: 6 }}>{moduleDisplayName(module)} path</div>
          <div style={{ fontSize: 14, color: tokens.soft }}>
            {doneCount}/{module.lessons.length} lessons complete
            {drillMeta.total > 0 ? ` · ${drillMeta.done}/${drillMeta.total} drills complete` : ""}
          </div>
        </div>
        <a className="btn accent" href={practiceHref} style={{ textDecoration: "none", padding: "10px 16px", minHeight: 40 }}>
          Practice with AI →
        </a>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: isPhone ? "1fr" : "repeat(auto-fit, minmax(150px, 1fr))", gap: 8 }}>
        {module.lessons.map((item, i) => {
          const active = item.id === lesson.id;
          const done = !!progress[item.id]?.done;
          return (
            <button
              key={item.id}
              onClick={() => setView({ name: "lesson", moduleId: module.id, lessonId: item.id })}
              style={{
                minHeight: 58, padding: "10px 12px", borderRadius: 12,
                border: `1px solid ${active ? accent.c : tokens.line}`,
                background: active ? accent.soft : tokens.surface2,
                color: active ? accent.c : tokens.text,
                textAlign: "left", fontFamily: "inherit",
                display: "grid", gridTemplateColumns: "auto 1fr", gap: 10,
                alignItems: "center",
              }}
            >
              <span style={{
                width: 20, height: 20, borderRadius: 999,
                background: done ? accent.c : "transparent",
                border: `1.5px solid ${done || active ? accent.c : tokens.line}`,
                color: done ? "white" : active ? accent.c : tokens.mute, display: "flex", alignItems: "center", justifyContent: "center",
                fontSize: 11,
              }}>
                {done ? "✓" : i + 1}
              </span>
              <span style={{ minWidth: 0 }}>
                <span className="mono" style={{ color: active ? accent.c : tokens.mute, fontSize: 10 }}>Lesson {i + 1}</span>
                <span style={{ display: "block", fontSize: 13, lineHeight: 1.3, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{item.title}</span>
              </span>
            </button>
          );
        })}
        <a
          href={practiceHref}
          style={{
            minHeight: 58, padding: "10px 12px", borderRadius: 12,
            border: `1px solid ${accent.c}55`, background: tokens.ink,
            color: tokens.bg, textDecoration: "none",
            display: "grid", alignContent: "center", gap: 2,
          }}
        >
          <span className="mono" style={{ color: `color-mix(in srgb, ${tokens.bg} 72%, transparent)`, fontSize: 10 }}>Practice</span>
          <span style={{ fontSize: 13, lineHeight: 1.3 }}>AI roleplay room</span>
        </a>
      </div>
    </div>
  );
}

function ModulePracticeCheckpoint({ module, next, nextModule, setView, tokens, accent }) {
  const isFinalLesson = !next && !!nextModule;
  const isCourseFinalLesson = !next && !nextModule;
  const practiceHref = modulePracticeHref(module);

  return (
    <div style={{
      margin: "44px 0 0", padding: 24, borderRadius: 16,
      background: tokens.ink, color: tokens.bg,
      display: "grid", gridTemplateColumns: "1fr", gap: 18,
    }}>
      <div>
        <div className="eyebrow" style={{ color: accent.c2, marginBottom: 8 }}>
          {isFinalLesson || isCourseFinalLesson ? "Practice checkpoint" : "Next step"}
        </div>
        <h4 className="display" style={{ fontSize: 24, lineHeight: 1.2, color: tokens.bg, marginBottom: 8 }}>
          {isCourseFinalLesson
            ? "Run one AI rep to close Chairside."
            : isFinalLesson
              ? `Run one AI rep before ${moduleDisplayName(nextModule)}.`
              : `Keep the ${moduleDisplayName(module)} path moving.`}
        </h4>
        <p style={{ margin: 0, fontSize: 15, lineHeight: 1.6, color: `color-mix(in srgb, ${tokens.bg} 72%, transparent)` }}>
          {isCourseFinalLesson
            ? "Use the roleplay room to turn the final lesson into a live rep while the language is still fresh."
            : isFinalLesson
              ? `Use the roleplay room to rehearse this module before moving into ${nextModule.title}.`
            : "Mark this lesson complete, continue to the next lesson, or open the AI practice room for a quick rep."}
        </p>
      </div>
      <div style={{ display: "flex", gap: 10, flexWrap: "wrap" }}>
        <a className="btn accent" href={practiceHref} style={{ textDecoration: "none", justifyContent: "center" }}>
          Practice with AI →
        </a>
        {next && (
          <button className="btn" onClick={() => setView({ name: "lesson", moduleId: module.id, lessonId: next.id })}
            style={{ background: "transparent", color: tokens.bg, borderColor: `color-mix(in srgb, ${tokens.bg} 35%, transparent)` }}>
            Next lesson →
          </button>
        )}
        {isFinalLesson && (
          <button className="btn" onClick={() => setView({ name: "lesson", moduleId: nextModule.id, lessonId: nextModule.lessons[0].id })}
            style={{ background: "transparent", color: tokens.bg, borderColor: `color-mix(in srgb, ${tokens.bg} 35%, transparent)` }}>
            Continue to {moduleDisplayName(nextModule)} →
          </button>
        )}
      </div>
    </div>
  );
}

// ─── Curriculum
function Curriculum({ tokens, accent, setView, progress, defaultRoleId }) {
  const isPhone = (window.useIsPhone || (() => false))();
  const [filter, setFilter] = useState(() => COURSE.roles.some(role => role.id === defaultRoleId) ? defaultRoleId : "all");
  const appliedDefault = useRef(false);
  useEffect(() => {
    if (!appliedDefault.current && COURSE.roles.some(role => role.id === defaultRoleId)) {
      setFilter(defaultRoleId);
      appliedDefault.current = true;
    }
  }, [defaultRoleId]);
  const total = COURSE.modules.reduce((a, m) => a + m.lessons.length, 0);
  const done = Object.keys(progress).filter(k => progress[k]?.done).length;

  return (
    <main style={{ maxWidth: 1320, margin: "0 auto", padding: "64px 28px 100px" }}>
      <div style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 60, alignItems: "end", marginBottom: 48 }}>
        <div>
          <div className="eyebrow" style={{ color: accent.c, marginBottom: 12 }}>The curriculum</div>
          <h1 className="display">Twelve modules,<br/>~40 lessons.</h1>
        </div>
        <div className="card" style={{ padding: 24 }}>
          <div className="eyebrow" style={{ color: tokens.mute, marginBottom: 12 }}>Your progress</div>
          <div style={{ display: "flex", alignItems: "baseline", gap: 8, marginBottom: 14 }}>
            <span className="display" style={{ fontSize: 36, color: accent.c }}>{done}</span>
            <span style={{ color: tokens.soft }}>/ {total} lessons walked</span>
          </div>
          <div style={{ height: 6, background: tokens.surface2, borderRadius: 3, overflow: "hidden" }}>
            <div style={{ height: "100%", background: accent.c, width: `${(done/total)*100}%`, transition: "width .4s ease" }} />
          </div>
        </div>
      </div>

      {/* Role filter */}
      <div style={{ display: "flex", gap: 8, marginBottom: 32, flexWrap: "wrap" }}>
        <button className={`chip ${filter === "all" ? "active" : ""}`} onClick={() => setFilter("all")}>All roles</button>
        {COURSE.roles.map(r => (
          <button key={r.id} className={`chip ${filter === r.id ? "active" : ""}`} onClick={() => setFilter(r.id)}>{r.name}</button>
        ))}
      </div>

      {/* Modules */}
      <div style={{ display: "grid", gap: 16 }}>
        {COURSE.modules.map(m => {
          const dim = filter !== "all" && m.role !== filter && m.role !== "all";
          const moduleDone = m.lessons.filter(l => progress[l.id]?.done).length;
          return (
            <div key={m.id} className="card" style={{ padding: 28, opacity: dim ? 0.4 : 1, transition: "opacity .25s ease" }}>
              <div style={{ display: "grid", gridTemplateColumns: isPhone ? "1fr" : "100px 1fr 1.2fr", gap: isPhone ? 16 : 32, alignItems: "start" }}>
                <div>
                  <div className="display" style={{ fontSize: 56, color: accent.c, lineHeight: 1, fontWeight: 400 }}>{m.number}</div>
                  <span className={`pill role-${m.role}`} style={{ marginTop: 12 }}>{m.kicker}</span>
                </div>
                <div>
                  <h2 className="display" style={{ fontSize: 28, marginBottom: 12 }}>{m.title}</h2>
                  <p style={{ color: tokens.soft, lineHeight: 1.6, fontSize: 15, marginBottom: 16 }}>{m.synopsis}</p>
                  <div style={{ display: "flex", gap: 14, color: tokens.mute, fontSize: 13 }}>
                    <span>{m.lessons.length} lessons</span>
                    <span>·</span>
                    <span>{m.hours}h</span>
                    <span>·</span>
                    <span style={{ color: accent.c }}>{moduleDone}/{m.lessons.length} done</span>
                  </div>
                </div>
                <div>
                  <ol style={{ listStyle: "none", padding: 0, margin: 0 }}>
                    {m.lessons.map((l, i) => {
                      const isDone = !!progress[l.id]?.done;
                      return (
                        <li key={l.id}>
                          <button onClick={() => setView({ name: "lesson", moduleId: m.id, lessonId: l.id })}
                            style={{
                              background: "none", border: "none", padding: "12px 0", width: "100%",
                              borderTop: i > 0 ? `1px solid ${tokens.line}` : "none",
                              display: "grid", gridTemplateColumns: "24px 1fr auto", gap: 12,
                              textAlign: "left", color: tokens.text, cursor: "pointer", transition: "color .2s ease",
                            }}
                            onMouseEnter={e => e.currentTarget.style.color = accent.c}
                            onMouseLeave={e => e.currentTarget.style.color = tokens.text}>
                            <span style={{ width: 16, height: 16, borderRadius: 999, background: isDone ? accent.c : "transparent", border: `1.5px solid ${isDone ? accent.c : tokens.line}`, display: "flex", alignItems: "center", justifyContent: "center", color: "white", fontSize: 10, marginTop: 2 }}>
                              {isDone ? "✓" : ""}
                            </span>
                            <div>
                              <div style={{ fontWeight: 500, fontSize: 14 }}>{l.title}</div>
                              <div style={{ color: tokens.mute, fontSize: 12, marginTop: 2 }}>{l.brief}</div>
                            </div>
                            <span className="mono" style={{ color: tokens.mute, fontSize: 11 }}>{l.duration}m</span>
                          </button>
                        </li>
                      );
                    })}
                  </ol>
                </div>
              </div>
              <ModuleCurriculumGuide
                module={m}
                progress={progress}
                setView={setView}
                tokens={tokens}
                accent={accent}
                isPhone={isPhone}
              />
            </div>
          );
        })}
      </div>
    </main>
  );
}

// ─── Lesson
function Lesson({ tokens, accent, view, setView, progress, setProgress, journal, setJournal }) {
  const isPhone = (window.useIsPhone || (() => false))();
  const m = COURSE.modules.find(x => x.id === view.moduleId);
  const l = m?.lessons.find(x => x.id === view.lessonId);
  const idx = m.lessons.findIndex(x => x.id === l.id);
  const next = m.lessons[idx + 1];
  const nextModule = !next ? COURSE.modules[COURSE.modules.findIndex(x => x.id === m.id) + 1] : null;
  const prev = m.lessons[idx - 1];

  // Module video — one summary video per module, rendered on the first lesson only.
  // PraxiaVideo expects token keys Chairside's `tokens` lacks; adapt them here.
  const Player = window.PraxiaVideo;
  const moduleVideoSrc = (COURSE.media && COURSE.media.baseUrl && m.video)
    ? `${COURSE.media.baseUrl}/${m.video}` : null;
  const videoTokens = {
    line: tokens.line, surface: tokens.surface, surface2: tokens.surface2,
    text: tokens.text, mute: tokens.mute, soft: tokens.soft, bg: tokens.bg,
    bg2: tokens.surface2, accent: accent.c, accent2: accent.c2,
  };

  const isDone = !!progress[l.id]?.done;
  const markDone = () => setProgress(p => ({ ...p, [l.id]: { ...(p[l.id] || {}), done: true, at: Date.now() } }));

  const [readPct, setReadPct] = useState(0);
  const articleRef = useRef(null);

  // Reference layer: any module with a cheatSheet array gets a slide-in drawer.
  const hasCheat = Array.isArray(m.cheatSheet) && m.cheatSheet.length > 0;
  const [cheatOpen, setCheatOpen] = useState(false);
  useEffect(() => { setCheatOpen(false); }, [view.lessonId, view.moduleId]);
  useEffect(() => {
    if (!cheatOpen) return;
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    const onKey = (e) => { if (e.key === "Escape") setCheatOpen(false); };
    window.addEventListener("keydown", onKey);
    return () => { document.body.style.overflow = prevOverflow; window.removeEventListener("keydown", onKey); };
  }, [cheatOpen]);
  useEffect(() => {
    const onScroll = () => {
      const el = articleRef.current; if (!el) return;
      const rect = el.getBoundingClientRect();
      const total = rect.height - window.innerHeight;
      const passed = Math.min(Math.max(-rect.top, 0), Math.max(total, 1));
      setReadPct(Math.round((passed / Math.max(total, 1)) * 100));
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener("scroll", onScroll);
  }, [view.lessonId]);

  useEffect(() => { window.scrollTo({ top: 0 }); }, [view.lessonId]);

  return (
    <main>
      {/* Reading bar */}
      <div style={{ position: "fixed", top: 64, left: 0, right: 0, height: 2, background: tokens.line, zIndex: 30 }}>
        <div style={{ height: 2, width: `${readPct}%`, background: accent.c, transition: "width .15s ease" }} />
      </div>

      <div style={{ display: "grid", gridTemplateColumns: isPhone ? "1fr" : "280px 1fr 240px", maxWidth: 1320, margin: "0 auto" }}>
        {/* Left rail */}
        <aside style={{ display: isPhone ? "none" : "block", borderRight: `1px solid ${tokens.line}`, padding: "44px 24px", position: "sticky", top: 64, height: "calc(100vh - 64px)", overflowY: "auto" }}>
          <div className="eyebrow" style={{ color: accent.c, marginBottom: 8 }}>Module {m.number}</div>
          <h3 className="display" style={{ fontSize: 20, marginBottom: 20 }}>{m.title}</h3>
          <ol style={{ listStyle: "none", padding: 0, margin: 0 }}>
            {m.lessons.map((ll, i) => {
              const active = ll.id === l.id;
              const dn = !!progress[ll.id]?.done;
              return (
                <li key={ll.id}>
                  <button onClick={() => setView({ name: "lesson", moduleId: m.id, lessonId: ll.id })}
                    style={{
                      background: active ? tokens.surface2 : "none", border: "none",
                      padding: "10px 12px", borderRadius: 10, marginBottom: 2,
                      display: "grid", gridTemplateColumns: "20px 1fr", gap: 10,
                      width: "100%", textAlign: "left", color: active ? accent.c : tokens.text,
                      cursor: "pointer", fontSize: 13, fontWeight: active ? 500 : 400, transition: "all .2s ease",
                    }}>
                    <span style={{ width: 14, height: 14, borderRadius: 999, background: dn ? accent.c : "transparent", border: `1.5px solid ${dn ? accent.c : tokens.line}`, display: "flex", alignItems: "center", justifyContent: "center", color: "white", fontSize: 9, marginTop: 2 }}>
                      {dn ? "✓" : ""}
                    </span>
                    <span style={{ lineHeight: 1.35 }}>{ll.title}</span>
                  </button>
                </li>
              );
            })}
          </ol>
          <button className="btn ghost" style={{ marginTop: 28, width: "100%", justifyContent: "flex-start" }} onClick={() => setView({ name: "curriculum" })}>← All modules</button>
        </aside>

        {/* Article */}
        <article ref={articleRef} style={{ padding: isPhone ? "32px 20px 80px" : "60px 56px 100px", maxWidth: 760 }}>
          {idx === 0 && Player && moduleVideoSrc && (
            <Player
              src={moduleVideoSrc}
              eyebrow={`Module ${m.number}`}
              title={m.title}
              posterText={`A short video briefing for "${m.title}." Watch first, then work the lesson.`}
              tokens={videoTokens}
              variant="module"
            />
          )}
          <ModuleLessonGuide
            module={m}
            lesson={l}
            progress={progress}
            setView={setView}
            tokens={tokens}
            accent={accent}
            isPhone={isPhone}
          />
          <div className="mono" style={{ color: tokens.mute, marginBottom: 18 }}>
            <button onClick={() => setView({ name: "curriculum" })} style={{ background: "none", border: "none", color: tokens.mute, padding: 0, cursor: "pointer", fontFamily: "inherit", fontSize: 12 }}>Curriculum</button>
            <span style={{ margin: "0 8px" }}>/</span>
            <span style={{ color: accent.c }}>Module {m.number}</span>
            <span style={{ margin: "0 8px" }}>/</span>
            Lesson {idx + 1}
          </div>

          <div className="pill role-{m.role}" style={{ marginBottom: 20, background: tokens.surface2, color: tokens.soft }}>
            ● {m.kicker}
          </div>

          <h1 className="display" style={{ fontSize: "clamp(36px, 4.4vw, 56px)", lineHeight: 1.04, letterSpacing: "-0.026em", marginBottom: 24 }}>
            {l.title}
          </h1>
          <p style={{ fontSize: 20, lineHeight: 1.5, color: tokens.soft, marginBottom: 32, fontFamily: "Fraunces", fontWeight: 300 }}>
            {l.brief}
          </p>
          <div style={{ display: "flex", gap: 16, alignItems: "center", paddingBottom: 28, borderBottom: `1px solid ${tokens.line}`, marginBottom: 40 }}>
            <span className="mono" style={{ color: tokens.mute }}>● {l.duration} min read</span>
            {isDone && <span className="mono" style={{ color: accent.c }}>✓ Completed</span>}
          </div>

          {/* Body */}
          <div>
            {l.body.map((b, i) => {
              if (b.kind === "p") {
                return (
                  <p key={i} style={{ fontSize: 17, lineHeight: 1.7, color: tokens.text, marginBottom: 20 }}>
                    {b.text}
                  </p>
                );
              }
              if (b.kind === "h3") {
                return (
                  <h3 key={i} className="display" style={{ fontSize: 24, marginTop: 36, marginBottom: 14, color: tokens.text }}>{b.text}</h3>
                );
              }
              if (b.kind === "list") {
                return (
                  <ul key={i} style={{ listStyle: "none", padding: 0, margin: "8px 0 28px" }}>
                    {b.items.map((it, j) => (
                      <li key={j} style={{ display: "grid", gridTemplateColumns: "auto 1fr", gap: 14, padding: "12px 0", borderBottom: j < b.items.length - 1 ? `1px solid ${tokens.line}` : "none", fontSize: 16, lineHeight: 1.6 }}>
                        <span style={{ width: 20, height: 20, borderRadius: 999, background: accent.soft, color: accent.c, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 11, fontWeight: 600, marginTop: 3 }}>
                          {j + 1}
                        </span>
                        <span>{it}</span>
                      </li>
                    ))}
                  </ul>
                );
              }
              if (b.kind === "script") {
                return (
                  <div key={i} style={{
                    margin: "8px 0 28px", padding: "16px 20px",
                    background: tokens.surface2, borderLeft: `3px solid ${accent.c}`,
                    borderRadius: "0 10px 10px 0",
                  }}>
                    <div className="eyebrow" style={{ color: accent.c, marginBottom: 8, fontSize: 11 }}>Say this</div>
                    <p style={{ fontSize: 17, lineHeight: 1.65, color: tokens.text, margin: 0, fontStyle: "italic" }}>
                      {b.text}
                    </p>
                  </div>
                );
              }
              return null;
            })}
          </div>

          {/* Frame */}
          {l.frame && (
            <div style={{ margin: "44px 0", padding: 28, background: accent.soft, borderRadius: 16, border: `1px solid ${accent.c}22` }}>
              <div className="eyebrow" style={{ color: accent.c, marginBottom: 10 }}>The frame</div>
              <h4 className="display" style={{ fontSize: 22, marginBottom: 12 }}>{l.frame.title}</h4>
              <p style={{ fontSize: 16, lineHeight: 1.6, color: tokens.text }}>{l.frame.text}</p>
            </div>
          )}

          {/* Drill */}
          {l.drill && <DrillCard drill={l.drill} tokens={tokens} accent={accent}
            done={!!progress[l.id]?.drillDone}
            onDone={() => setProgress(p => ({ ...p, [l.id]: { ...(p[l.id] || {}), drillDone: true } }))} />}

          {/* Keystones */}
          <div style={{ margin: "44px 0", padding: "28px 0", borderTop: `1px solid ${tokens.line}`, borderBottom: `1px solid ${tokens.line}` }}>
            <div className="eyebrow" style={{ color: accent.c, marginBottom: 16 }}>Keystones</div>
            <ul style={{ listStyle: "none", padding: 0, margin: 0, display: "grid", gap: 12 }}>
              {l.keystones.map((k, i) => (
                <li key={i} style={{ display: "grid", gridTemplateColumns: "auto 1fr", gap: 12, fontSize: 16, lineHeight: 1.55 }}>
                  <span style={{ color: accent.c, fontWeight: 600, marginTop: 1 }}>—</span>
                  <span>{k}</span>
                </li>
              ))}
            </ul>
          </div>

          {/* Prompt */}
          {l.prompt && (
            <div style={{ margin: "44px 0" }}>
              <div className="eyebrow" style={{ color: accent.c, marginBottom: 12 }}>Reflection</div>
              <h4 className="display" style={{ fontSize: 24, marginBottom: 16, lineHeight: 1.3 }}>{l.prompt}</h4>
              <textarea
                placeholder="Write what's true. The act of writing is half the practice."
                value={journal[l.id] || ""}
                onChange={e => setJournal(j => ({ ...j, [l.id]: e.target.value }))}
                style={{ minHeight: 140, fontSize: 15, lineHeight: 1.6 }}
              />
              {journal[l.id] && <div className="mono" style={{ color: tokens.mute, marginTop: 8 }}>● Saved · {journal[l.id].split(/\s+/).filter(Boolean).length} words</div>}
            </div>
          )}

          <ModulePracticeCheckpoint
            module={m}
            next={next}
            nextModule={nextModule}
            setView={setView}
            tokens={tokens}
            accent={accent}
          />

          {/* Mark done + nav */}
          <div style={{ marginTop: 48, paddingTop: 32, borderTop: `1px solid ${tokens.line}` }}>
            {!isDone ? (
              <button className="btn accent" onClick={markDone} style={{ width: "100%", justifyContent: "center", padding: "16px 22px" }}>
                Mark lesson complete →
              </button>
            ) : (
              <div style={{ padding: "12px 18px", background: accent.soft, color: accent.c, borderRadius: 999, textAlign: "center", fontWeight: 500, fontSize: 14 }}>
                ✓ Completed · {new Date(progress[l.id]?.at || Date.now()).toLocaleDateString(undefined, { month: "short", day: "numeric" })}
              </div>
            )}
            <div style={{ display: "flex", justifyContent: "space-between", marginTop: 20, gap: 12 }}>
              <button className="btn" onClick={() => prev ? setView({ name: "lesson", moduleId: m.id, lessonId: prev.id }) : setView({ name: "curriculum" })}>
                ← {prev ? prev.title : "Curriculum"}
              </button>
              {(next || nextModule) && (
                <button className="btn primary" onClick={() => {
                  if (next) setView({ name: "lesson", moduleId: m.id, lessonId: next.id });
                  else if (nextModule) setView({ name: "lesson", moduleId: nextModule.id, lessonId: nextModule.lessons[0].id });
                }}>
                  {next ? next.title : `Module ${nextModule.number}`} →
                </button>
              )}
            </div>
          </div>
        </article>

        {/* Right rail */}
        <aside style={{ display: isPhone ? "none" : "block", padding: "60px 20px", position: "sticky", top: 64, height: "calc(100vh - 64px)" }}>
          <div className="eyebrow" style={{ color: tokens.mute, marginBottom: 14 }}>On this page</div>
          <ul style={{ listStyle: "none", padding: 0, margin: 0, display: "grid", gap: 10 }}>
            <li style={{ color: accent.c, fontSize: 13, fontWeight: 500 }}>Reading</li>
            {l.frame && <li style={{ color: tokens.soft, fontSize: 13 }}>The frame</li>}
            {l.drill && <li style={{ color: tokens.soft, fontSize: 13 }}>The drill</li>}
            <li style={{ color: tokens.soft, fontSize: 13 }}>Keystones</li>
            {l.prompt && <li style={{ color: tokens.soft, fontSize: 13 }}>Reflection</li>}
          </ul>
          <div style={{ marginTop: 28, paddingTop: 18, borderTop: `1px solid ${tokens.line}` }}>
            <div className="eyebrow" style={{ color: tokens.mute, marginBottom: 10 }}>Reading</div>
            <div style={{ height: 4, background: tokens.line, borderRadius: 2, overflow: "hidden" }}>
              <div style={{ height: 4, width: `${readPct}%`, background: accent.c, transition: "width .15s ease" }} />
            </div>
            <div className="mono" style={{ color: tokens.mute, marginTop: 6 }}>{readPct}%</div>
          </div>
        </aside>
      </div>

      {/* Reference layer — persistent cheat-sheet button + slide-in drawer */}
      {hasCheat && (
        <button
          onClick={() => setCheatOpen(true)}
          aria-label="Open cheat sheet"
          style={{
            position: "fixed", right: isPhone ? 16 : 24, zIndex: 90,
            bottom: isPhone
              ? "calc(var(--touch-target-comfortable, 48px) + var(--space-6, 24px) + env(safe-area-inset-bottom) + 12px)"
              : 24,
            display: "flex", alignItems: "center", gap: 8, minHeight: 44,
            padding: "12px 18px", borderRadius: 999,
            background: tokens.surface2, color: accent.c,
            border: `1px solid ${accent.c}55`, cursor: "pointer",
            fontFamily: "inherit", fontSize: 14, fontWeight: 600,
            boxShadow: "0 6px 24px rgba(0,0,0,0.38)",
          }}
        >
          <span aria-hidden="true" style={{ fontSize: 15, lineHeight: 1 }}>▤</span>
          Cheat sheet
        </button>
      )}

      {hasCheat && cheatOpen && (
        <div
          className="cheat-backdrop"
          onClick={() => setCheatOpen(false)}
          style={{ position: "fixed", inset: 0, zIndex: 200, background: "rgba(0,0,0,0.6)", backdropFilter: "blur(2px)", display: "flex", justifyContent: "flex-end" }}
        >
          <div
            className="cheat-drawer"
            role="dialog"
            aria-modal="true"
            aria-label={`${m.title} — cheat sheet`}
            onClick={(e) => e.stopPropagation()}
            style={{
              width: isPhone ? "100vw" : "min(440px, 92vw)", maxWidth: "100vw", height: "100%",
              background: tokens.surface, borderLeft: `1px solid ${tokens.line}`,
              display: "flex", flexDirection: "column", overflowY: "auto",
              boxShadow: "-12px 0 44px rgba(0,0,0,0.5)",
            }}
          >
            <div style={{ position: "sticky", top: 0, background: tokens.surface, borderBottom: `1px solid ${tokens.line}`, padding: "18px 22px", display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 12, zIndex: 1 }}>
              <div>
                <div className="eyebrow" style={{ color: accent.c, fontSize: 11, marginBottom: 4 }}>Cheat sheet</div>
                <div className="display" style={{ fontSize: 18, lineHeight: 1.2 }}>{m.title}</div>
              </div>
              <button
                onClick={() => setCheatOpen(false)}
                aria-label="Close cheat sheet"
                style={{ flexShrink: 0, background: tokens.surface2, border: `1px solid ${tokens.line}`, color: tokens.text, borderRadius: 999, width: 36, height: 36, minWidth: 36, cursor: "pointer", fontSize: 20, lineHeight: 1, display: "flex", alignItems: "center", justifyContent: "center" }}
              >
                ×
              </button>
            </div>
            <div style={{ padding: "14px 22px 40px", display: "grid", gap: 16 }}>
              {m.cheatSheet.map((c, i) => (
                <div key={i} style={{ paddingBottom: 14, borderBottom: i < m.cheatSheet.length - 1 ? `1px solid ${tokens.line}` : "none" }}>
                  <div className="eyebrow" style={{ color: accent.c, fontSize: 11, marginBottom: 6 }}>{c.label}</div>
                  <div style={{ fontSize: 15, lineHeight: 1.6, color: tokens.text }}>{c.text}</div>
                </div>
              ))}
            </div>
          </div>
        </div>
      )}
    </main>
  );
}

function DrillCard({ drill, tokens, accent, done, onDone }) {
  const [open, setOpen] = useState(false);
  return (
    <div style={{ margin: "44px 0", border: `1.5px solid ${accent.c}33`, borderRadius: 16, overflow: "hidden", background: tokens.surface }}>
      <div style={{ padding: "22px 26px", display: "flex", justifyContent: "space-between", gap: 16, alignItems: "center", borderBottom: open ? `1px solid ${tokens.line}` : "none" }}>
        <div>
          <div className="eyebrow" style={{ color: accent.c, marginBottom: 6 }}>The drill · {drill.level} · {drill.duration}</div>
          <h4 className="display" style={{ fontSize: 22 }}>{drill.title}</h4>
        </div>
        <button className="btn" onClick={() => setOpen(!open)}>{open ? "Hide" : "Open"}</button>
      </div>
      {open && (
        <div style={{ padding: "22px 26px" }}>
          <ol style={{ listStyle: "none", padding: 0, margin: 0 }}>
            {drill.steps.map((s, i) => (
              <li key={i} style={{ display: "grid", gridTemplateColumns: "auto 1fr", gap: 14, padding: "12px 0", borderBottom: i < drill.steps.length - 1 ? `1px solid ${tokens.line}` : "none" }}>
                <span style={{ width: 22, height: 22, borderRadius: 999, background: accent.c, color: "white", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 11, fontWeight: 600 }}>{i + 1}</span>
                <span style={{ fontSize: 15, lineHeight: 1.6 }}>{s}</span>
              </li>
            ))}
          </ol>
          <div style={{ marginTop: 20 }}>
            {!done ? (
              <button className="btn accent" onClick={onDone}>Mark drill complete</button>
            ) : (
              <span className="mono" style={{ color: accent.c }}>✓ Drill complete</span>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

function ResourceTabs({ tokens, active, setView }) {
  return (
    <div role="tablist" aria-label="Course resources" style={{ display: "inline-flex", gap: 4, padding: 4, marginBottom: 32, background: tokens.surface2, border: `1px solid ${tokens.line}`, borderRadius: 8 }}>
      {[
        { id: "scripts", label: "Scripts" },
        { id: "lexicon", label: "Lexicon" },
      ].map(resource => {
        const selected = active === resource.id;
        return (
          <button
            key={resource.id}
            type="button"
            role="tab"
            aria-selected={selected}
            onClick={() => setView({ name: resource.id })}
            style={{
              minHeight: 40, padding: "8px 16px", border: selected ? `1px solid ${tokens.line}` : "1px solid transparent",
              borderRadius: 6, background: selected ? tokens.surface : "transparent", color: selected ? tokens.text : tokens.soft,
              fontSize: 13, fontWeight: 500, boxShadow: selected ? "0 1px 3px rgba(31,27,22,.06)" : "none",
            }}
          >
            {resource.label}
          </button>
        );
      })}
    </div>
  );
}

// ─── Scripts library
function Scripts({ tokens, accent, setView }) {
  const [filter, setFilter] = useState("all");
  const cats = ["all", ...new Set(COURSE.scripts.map(s => s.category))];
  const filtered = filter === "all" ? COURSE.scripts : COURSE.scripts.filter(s => s.category === filter);

  return (
    <main style={{ maxWidth: 1080, margin: "0 auto", padding: "64px 28px 100px" }}>
      <ResourceTabs tokens={tokens} active="scripts" setView={setView} />
      <div className="eyebrow" style={{ color: accent.c, marginBottom: 12 }}>Scripts library</div>
      <h1 className="display" style={{ marginBottom: 16 }}>Verbatim. Drillable. Yours.</h1>
      <p style={{ color: tokens.soft, fontSize: 17, maxWidth: 620, marginBottom: 32 }}>
        Every conversation in the course, written out word for word. Print them. Tape them inside binders. Roleplay them on Friday mornings until they become natural.
      </p>

      <div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 28 }}>
        {cats.map(c => (
          <button key={c} className={`chip ${filter === c ? "active" : ""}`} onClick={() => setFilter(c)}>
            {c === "all" ? "All scripts" : c}
          </button>
        ))}
      </div>

      <div style={{ display: "grid", gap: 16 }}>
        {filtered.map((s, i) => (
          <div key={i} className="card" style={{ padding: 28 }}>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "start", gap: 16, marginBottom: 18 }}>
              <div>
                <div className="eyebrow" style={{ color: tokens.mute, marginBottom: 6 }}>{s.category}</div>
                <h3 className="display" style={{ fontSize: 22 }}>{s.title}</h3>
              </div>
              <span className={`pill role-${s.role}`}>{COURSE.roles.find(r => r.id === s.role)?.name || s.role}</span>
            </div>
            <div style={{ display: "grid", gap: 10 }}>
              {s.lines.map((ln, j) => {
                const isPatient = ln.speaker === "Patient";
                return (
                  <div key={j} style={{
                    padding: "12px 16px", borderRadius: 10,
                    background: isPatient ? tokens.surface2 : accent.soft,
                    borderLeft: `3px solid ${isPatient ? tokens.line : accent.c}`,
                  }}>
                    <div className="mono" style={{ color: isPatient ? tokens.mute : accent.c, fontSize: 10, marginBottom: 4, fontWeight: 600 }}>{ln.speaker.toUpperCase()}</div>
                    <div style={{ fontSize: 15, lineHeight: 1.55, color: tokens.text }}>{ln.text}</div>
                  </div>
                );
              })}
            </div>
          </div>
        ))}
      </div>
    </main>
  );
}

// ─── Lexicon
function Lexicon({ tokens, accent, setView }) {
  const isPhone = (window.useIsPhone || (() => false))();
  const [q, setQ] = useState("");
  const filtered = COURSE.glossary.filter(g =>
    q === "" || g.term.toLowerCase().includes(q.toLowerCase()) || g.def.toLowerCase().includes(q.toLowerCase())
  );

  return (
    <main style={{ maxWidth: 1080, margin: "0 auto", padding: "64px 28px 100px" }}>
      <ResourceTabs tokens={tokens} active="lexicon" setView={setView} />
      <div style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 40, alignItems: "end", marginBottom: 36 }}>
        <div>
          <div className="eyebrow" style={{ color: accent.c, marginBottom: 12 }}>The lexicon</div>
          <h1 className="display">Words held<br/>with precision.</h1>
        </div>
        <input placeholder="Search a term…" value={q} onChange={e => setQ(e.target.value)} />
      </div>

      <div style={{ display: "grid", gap: 0 }}>
        {filtered.map((g, i) => (
          <div key={g.term} style={{ display: "grid", gridTemplateColumns: isPhone ? "1fr" : "260px 1fr", gap: isPhone ? 8 : 32, padding: "24px 0", borderTop: `1px solid ${tokens.line}` }}>
            <div>
              <div className="mono" style={{ color: accent.c, marginBottom: 4, fontSize: 11 }}>{String(i + 1).padStart(3, "0")}</div>
              <div className="display" style={{ fontSize: 22, lineHeight: 1.2 }}>{g.term}</div>
            </div>
            <p style={{ color: tokens.soft, fontSize: 15, lineHeight: 1.6, alignSelf: "center" }}>{g.def}</p>
          </div>
        ))}
        {filtered.length === 0 && <div style={{ padding: 60, textAlign: "center", color: tokens.mute }}>No entries found.</div>}
      </div>
    </main>
  );
}

// ─── Outcomes / dashboard
function Outcomes({ tokens, accent, progress, journal }) {
  const isPhone = (window.useIsPhone || (() => false))();
  const total = COURSE.modules.reduce((a, m) => a + m.lessons.length, 0);
  const done = Object.keys(progress).filter(k => progress[k]?.done).length;
  const drillsDone = Object.keys(progress).filter(k => progress[k]?.drillDone).length;
  const totalDrills = COURSE.modules.flatMap(m => m.lessons).filter(l => l.drill).length;
  const journalEntries = Object.values(journal).filter(e => e && e.trim()).length;
  const journalWords = Object.values(journal).reduce((a, e) => a + (e ? e.split(/\s+/).filter(Boolean).length : 0), 0);

  return (
    <main style={{ maxWidth: 1080, margin: "0 auto", padding: "64px 28px 100px" }}>
      <div className="eyebrow" style={{ color: accent.c, marginBottom: 12 }}>Your training</div>
      <h1 className="display" style={{ marginBottom: 36 }}>Outcomes & progress.</h1>

      <div style={{ display: "grid", gridTemplateColumns: isPhone ? "repeat(2, 1fr)" : "repeat(4, 1fr)", gap: 16, marginBottom: 40 }}>
        <Stat tokens={tokens} accent={accent} n={`${done}/${total}`} l="Lessons walked" />
        <Stat tokens={tokens} accent={accent} n={`${drillsDone}/${totalDrills}`} l="Drills completed" />
        <Stat tokens={tokens} accent={accent} n={`${journalEntries}`} l="Reflections written" />
        <Stat tokens={tokens} accent={accent} n={`${journalWords}`} l="Journal words" />
      </div>

      {/* Module progress bars */}
      <div className="card" style={{ padding: 28, marginBottom: 32 }}>
        <h3 className="display" style={{ fontSize: 22, marginBottom: 20 }}>Module progress</h3>
        <div style={{ display: "grid", gap: 14 }}>
          {COURSE.modules.map(m => {
            const md = m.lessons.filter(l => progress[l.id]?.done).length;
            const pct = (md / m.lessons.length) * 100;
            return (
              <div key={m.id} style={{ display: "grid", gridTemplateColumns: "60px 1fr 80px", gap: 16, alignItems: "center" }}>
                <span className="mono" style={{ color: accent.c, fontSize: 13, fontWeight: 500 }}>{m.number}</span>
                <div>
                  <div style={{ fontSize: 14, fontWeight: 500, marginBottom: 6 }}>{m.title}</div>
                  <div style={{ height: 4, background: tokens.surface2, borderRadius: 2, overflow: "hidden" }}>
                    <div style={{ height: "100%", background: accent.c, width: `${pct}%`, transition: "width .4s ease" }} />
                  </div>
                </div>
                <span className="mono" style={{ color: tokens.mute, fontSize: 12, textAlign: "right" }}>{md}/{m.lessons.length}</span>
              </div>
            );
          })}
        </div>
      </div>

      {/* Certification */}
      <div className="card" style={{ padding: 32, background: accent.soft, border: "none" }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 24 }}>
          <div>
            <div className="eyebrow" style={{ color: accent.c, marginBottom: 10 }}>Certification</div>
            <h3 className="display" style={{ fontSize: 24, marginBottom: 8 }}>
              {done === total ? "You've completed Chairside." : `${total - done} lessons until certification.`}
            </h3>
            <p style={{ color: tokens.soft, fontSize: 14 }}>
              Complete all 12 modules and 30+ drills to receive your Chairside Certified Communicator credential.
            </p>
          </div>
          <button className="btn primary" disabled={done < total} style={{ opacity: done < total ? 0.4 : 1 }}>
            {done === total ? "Claim certificate →" : "Locked"}
          </button>
        </div>
      </div>
    </main>
  );
}

function Stat({ tokens, accent, n, l }) {
  return (
    <div className="card" style={{ padding: 22 }}>
      <div className="display" style={{ fontSize: 36, color: accent.c, lineHeight: 1, marginBottom: 6 }}>{n}</div>
      <div style={{ color: tokens.soft, fontSize: 13 }}>{l}</div>
    </div>
  );
}

function chairsideViewFromHash() {
  if (typeof window === "undefined" || !window.location.hash) return null;
  const params = new URLSearchParams(window.location.hash.replace(/^#/, ""));
  const name = params.get("view");
  if (["home", "curriculum", "scripts", "lexicon", "outcomes"].includes(name)) return { name };
  if (name !== "lesson") return null;

  const moduleId = params.get("module");
  const lessonId = params.get("lesson");
  const module = COURSE.modules.find((item) => item.id === moduleId);
  const lesson = module && module.lessons.find((item) => item.id === lessonId);
  return module && lesson ? { name: "lesson", moduleId, lessonId } : null;
}

// ─── Root
function App() {
  const [view, setView] = useStored("chairside.view", { name: "home" });
  // Content progress + journal sync across devices when signed in; falls back to
  // localStorage-only when the sync hook isn't available (offline / file://).
  const useSyncedStore = window.useSyncedStore || useStored;
  const [progress, setProgress] = useSyncedStore("chairside.progress", {});
  const [journal, setJournal] = useSyncedStore("chairside.journal", {});
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [member, setMember] = useState(null);

  // Home-dashboard links can open the curriculum, progress, or an exact next
  // lesson while preserving the existing local view for ordinary visits.
  useEffect(() => {
    const syncFromHash = () => {
      const linkedView = chairsideViewFromHash();
      if (linkedView) setView(linkedView);
    };
    syncFromHash();
    window.addEventListener("hashchange", syncFromHash);
    return () => window.removeEventListener("hashchange", syncFromHash);
  }, []);

  // Resolve the member's Chairside role from the server-written suite role,
  // so "Practice with AI" links match their seat. The setState re-renders the
  // tree once resolved; any failure leaves the "tc" fallback (fail open).
  useEffect(() => {
    let alive = true;
    fetch("/api/me", { credentials: "same-origin" })
      .then((r) => (r.ok ? r.json() : null))
      .then((data) => {
        if (!alive || !data) return;
        setMember(data);
        if (!data.role || !window.suiteRoleToChairside) return;
        const cid = window.suiteRoleToChairside(data.role);
        if (cid) USER_PRACTICE_ROLE = cid;
      })
      .catch(() => { /* fail open: keep the tc fallback */ });
    return () => { alive = false; };
  }, []);

  const tokens = useMemo(() => THEMES[tweaks.theme] || THEMES.warm, [tweaks.theme]);
  const accent = useMemo(() => ACCENTS[tweaks.accent] || ACCENTS.amber, [tweaks.accent]);
  const memberRoleId = member && member.role && window.suiteRoleToChairside ? window.suiteRoleToChairside(member.role) : null;

  const density = { compact: 0.94, regular: 1, comfy: 1.08 }[tweaks.density] || 1;

  return (
    <div style={{ fontSize: `${density}rem` }}>
      <GlobalStyle tokens={tokens} accent={accent} />
      {window.SuiteSidebar && (
        <window.SuiteSidebar tokens={tokens} accent={accent} currentHref="Chairside.html" />
      )}
      <Chrome view={view} setView={setView} tokens={tokens} accent={accent} progress={progress} />
      {view.name === "home" && <Home tokens={tokens} accent={accent} setView={setView} progress={progress} memberRoleId={memberRoleId} />}
      {view.name === "curriculum" && <Curriculum tokens={tokens} accent={accent} setView={setView} progress={progress} defaultRoleId={view.roleId || memberRoleId} />}
      {view.name === "lesson" && <Lesson tokens={tokens} accent={accent} view={view} setView={setView} progress={progress} setProgress={setProgress} journal={journal} setJournal={setJournal} />}
      {view.name === "scripts" && <Scripts tokens={tokens} accent={accent} setView={setView} />}
      {view.name === "lexicon" && <Lexicon tokens={tokens} accent={accent} setView={setView} />}
      {view.name === "outcomes" && <Outcomes tokens={tokens} accent={accent} progress={progress} journal={journal} />}

      <TweaksPanel title="Tweaks">
        <TweakSection label="Theme" />
        <TweakRadio label="Mode" value={tweaks.theme} onChange={v => setTweak("theme", v)} options={[{ value: "warm", label: "Warm" }, { value: "cream", label: "Cream" }, { value: "dark", label: "Dark" }]} />
        <TweakSelect label="Accent" value={tweaks.accent} onChange={v => setTweak("accent", v)} options={[{ value: "amber", label: "Amber" }, { value: "rose", label: "Rose" }, { value: "teal", label: "Teal" }, { value: "indigo", label: "Indigo" }]} />
        <TweakSection label="Density" />
        <TweakRadio label="Spacing" value={tweaks.density} onChange={v => setTweak("density", v)} options={[{ value: "compact", label: "Tight" }, { value: "regular", label: "Reg" }, { value: "comfy", label: "Wide" }]} />
      </TweaksPanel>
    </div>
  );
}

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