/* educator-builder.jsx — Study Builder: walk a published deck slide-by-slide
   and style or gamify individual slides. Persists deck.enhance via DeckStore. */

function StudyBuilder({ deck, onClose, toast }) {
  const slides = deck.slides || [];
  const TOTAL = slides.length;
  const [enh, setEnh] = useState(() => JSON.parse(JSON.stringify(deck.enhance || {})));
  const [idx, setIdx] = useState(0);
  const [preview, setPreview] = useState(false);
  const [saving, setSaving] = useState(false);
  const [rebuilding, setRebuilding] = useState(false);
  const [rprompt, setRprompt] = useState("");
  const [proposing, setProposing] = useState(false);
  const [gprompt, setGprompt] = useState("");
  const [showDeckNote, setShowDeckNote] = useState(false);
  const ratio = deck.ratio || 16 / 9;

  const cur = enh[idx] || {};
  function patch(p) {setEnh((prev) => ({ ...prev, [idx]: { ...(prev[idx] || {}), ...p } }));}
  function patchGame(p) {setEnh((prev) => {const s = prev[idx] || {};const g = { ...(s.game || { type: "quiz", q: "", options: ["", "", "", ""], answer: 0, xp: 50 }), ...p };return { ...prev, [idx]: { ...s, game: g } };});}
  function clearSlide() {setEnh((prev) => {const n = { ...prev };delete n[idx];return n;});setRprompt("");}

  /* Accent applies to the slide ITSELF when it has been rebuilt in HTML:
     recolour the slide's HTML in place (previous accent hex → new one). */
  function applyAccent(c) {
    setEnh((prev) => {
      const s = prev[idx] || {};
      const p = { ...s, accent: c };
      if (s.html) {
        const old = s.accent || "#f99d25";
        if (old.toLowerCase() !== c.toLowerCase()) {
          try {
            const esc = old.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
            p.html = s.html.replace(new RegExp(esc, "gi"), c);
          } catch (e) {}
        }
      }
      return { ...prev, [idx]: p };
    });
  }

  async function rebuildHtml() {
    if (rebuilding) return;
    if (!window.claude || !window.claude.complete) {toast && toast("AI rebuild runs in the live preview environment", "x");return;}
    setRebuilding(true);
    const accent = cur.accent || "#f99d25";
    const styleHints = [cur.title && "title: " + cur.title, cur.caption && "caption: " + cur.caption].filter(Boolean).join("; ");
    const prompt = [
    "You are rebuilding one slide of a professional training presentation as a single self-contained HTML document.",
    "Deck: \"" + deck.title + "\" — slide " + (idx + 1) + " of " + TOTAL + ".",
    rprompt.trim() ? "The trainer's instruction for this slide: \"" + rprompt.trim() + "\"" : "Recreate a clean, modern version of a training slide on this deck's topic.",
    styleHints ? "Existing slide metadata — " + styleHints + "." : "",
    "Requirements: Output ONLY raw HTML (a complete document starting with <!DOCTYPE html>). No markdown, no code fences, no commentary.",
    "The slide must fill a 16:9 stage: set html,body{margin:0;width:100vw;height:100vh;overflow:hidden} and a root .slide that is display:flex and fills the viewport.",
    "Use vw/vh-relative units for ALL sizing (font-size in vw, padding in vw) so it scales to any size. Base body font-size around 2.4vw, headings 4-6vw.",
    "Use a refined palette with accent " + accent + ". Inter/system sans-serif. No external images. Keep it concise and well-composed."].
    filter(Boolean).join("\n");
    try {
      let out = await window.claude.complete(prompt);
      out = (out || "").replace(/^```html\s*/i, "").replace(/^```\s*/i, "").replace(/```\s*$/i, "").trim();
      if (!/<(!doctype|html|body|div|section)/i.test(out)) throw new Error("no html");
      setEnh((prev) => ({ ...prev, [idx]: { ...(prev[idx] || {}), html: out, htmlPrompt: rprompt.trim() } }));
      toast && toast("Slide rebuilt in HTML ✓", "check");
    } catch (e) {
      toast && toast("Rebuild failed — " + (e && e.message ? e.message : "try again in a moment"), "x");
    }
    setRebuilding(false);
  }
  function revertHtml() {setEnh((prev) => {const s = { ...(prev[idx] || {}) };delete s.html;delete s.htmlPrompt;return { ...prev, [idx]: s };});}

  async function proposeGame() {
    if (proposing) return;
    if (!window.claude || !window.claude.complete) {toast && toast("AI runs in the live preview environment", "x");return;}
    setProposing(true);
    let content = "";
    if (cur.html) content = cur.html.replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim().slice(0, 700);else
    content = [cur.title && "title: " + cur.title, cur.caption && "caption: " + cur.caption].filter(Boolean).join("; ");
    const prompt = [
    "You design gamification for one slide of a professional training deck.",
    "Deck: \"" + deck.title + "\" — slide " + (idx + 1) + " of " + TOTAL + ".",
    content ? "Slide content: " + content : "No slide text available; infer a relevant question from the deck topic.",
    gprompt.trim() ? "Trainer guidance: \"" + gprompt.trim() + "\"" : "",
    "Propose ONE engaging interaction that checks understanding of THIS slide.",
    "Return ONLY a JSON object (no markdown, no commentary, no code fences) with keys:",
    'type: one of "quiz", "poll", "reflect";',
    "q: the question or prompt text (concise);",
    'options: array of exactly 4 short answer strings (for "reflect" use []);',
    'answer: 0-based index of the correct option for "quiz" (0 otherwise);',
    "xp: one of 20, 50, 100, 200 (harder = more)."].
    filter(Boolean).join("\n");
    try {
      let out = await window.claude.complete(prompt);
      const m = (out || "").match(/\{[\s\S]*\}/);
      if (!m) throw new Error("no proposal returned");
      const p = JSON.parse(m[0]);
      const type = ["quiz", "poll", "reflect"].includes(p.type) ? p.type : "quiz";
      let options = Array.isArray(p.options) ? p.options.map((o) => String(o)).slice(0, 4) : [];
      if (type !== "reflect") {while (options.length < 4) options.push("");}
      const answer = Math.max(0, Math.min(3, parseInt(p.answer, 10) || 0));
      const xp = [20, 50, 100, 200].includes(p.xp) ? p.xp : 50;
      setEnh((prev) => ({ ...prev, [idx]: { ...(prev[idx] || {}), game: { type, q: String(p.q || ""), options, answer, xp } } }));
      toast && toast("Gamification proposed ✓ — review &amp; edit below", "check");
    } catch (e) {
      toast && toast("Couldn't propose — " + (e && e.message ? e.message : "try again"), "x");
    }
    setProposing(false);
  }
  function setGameType(type) {
    if (!type) {setEnh((prev) => {const s = { ...(prev[idx] || {}) };delete s.game;return { ...prev, [idx]: s };});return;}
    setEnh((prev) => {const s = prev[idx] || {};const ex = s.game || {};return { ...prev, [idx]: { ...s, game: { type, q: ex.q || "", options: ex.options || ["", "", "", ""], answer: ex.answer || 0, xp: ex.xp || (type === "kahoot" ? 100 : 50), time: ex.time || 20 } } };});
  }

  async function save() {
    setSaving(true);
    const clean = {};
    Object.keys(enh).forEach((k) => {const e = enh[k];if (e && (e.title || e.caption || e.frame || e.accent || e.game || e.html)) clean[k] = e;});
    const gamified = Object.values(clean).some((e) => e.game);
    try {
      await window.DeckStore.save({ ...deck, enhance: clean, gamified });
      toast && toast("Study saved — " + Object.keys(clean).length + " slides enhanced", "check");
      onClose(true);
    } catch (e) {toast && toast("Save failed — deck may be too large", "x");}
    setSaving(false);
  }

  const g = cur.game;
  const slideHas = (i) => {const e = enh[i] || {};return { styled: !!(e.title || e.caption || e.frame), gamed: !!e.game, html: !!e.html };};

  const lblStyle = { fontSize: 11, fontWeight: 800, color: "var(--ink-3)", textTransform: "uppercase", letterSpacing: ".07em", display: "block", marginBottom: 7 };
  const inputStyle = { width: "100%", padding: "9px 12px", border: "1.5px solid var(--line)", borderRadius: 9, fontSize: 14, color: "var(--ink)", background: "var(--surface)", fontFamily: "var(--body)", outline: "none" };

  return ReactDOM.createPortal(
    <div style={{ position: "fixed", inset: 0, zIndex: 1200, background: "var(--bg, #f4f7fb)", display: "flex", flexDirection: "column", fontFamily: "var(--body)" }}>
      {/* HEADER */}
      <div style={{ height: 60, flex: "none", display: "flex", alignItems: "center", gap: 14, padding: "0 22px", borderBottom: "1px solid var(--line)", background: "var(--card, #fff)" }}>
        <div style={{ width: 34, height: 34, borderRadius: 9, background: "var(--vh-blue-500)", display: "grid", placeItems: "center" }}><Icon name="wand" size={18} style={{ color: "#fff" }} /></div>
        <div>
          <div style={{ fontFamily: "var(--display)", fontWeight: 800, fontSize: 17, color: "var(--ink)", lineHeight: 1.1 }}>Study Builder</div>
          <div style={{ fontSize: 12.5, color: "var(--ink-3)" }}>{deck.title}</div>
        </div>
        <div style={{ flex: 1 }} />
        <button className="btn btn-outline" onClick={() => setPreview(true)}><Icon name="search" size={15} />Preview as learner</button>
        <button className="btn btn-acc" disabled={saving} onClick={save}><Icon name="check" size={15} />{saving ? "Saving…" : "Save study"}</button>
        <button className="btn btn-ghost" onClick={() => onClose(false)}><Icon name="x" size={15} />Close</button>
      </div>

      {/* BODY */}
      <div style={{ flex: 1, display: "flex", minHeight: 0 }}>
        {/* SLIDE RAIL */}
        <div style={{ width: 188, flex: "none", borderRight: "1px solid var(--line)", overflowY: "auto", padding: 12, background: "var(--card,#fff)" }} className="scroll">
          {slides.map((img, i) => {
            const m = slideHas(i);
            return (
              <button key={i} onClick={() => setIdx(i)} style={{ width: "100%", marginBottom: 9, padding: 0, border: "2px solid " + (i === idx ? "var(--vh-blue-500)" : "transparent"), borderRadius: 9, overflow: "hidden", cursor: "pointer", background: "none", position: "relative", display: "block" }}>
                <div style={{ position: "relative", aspectRatio: String(ratio), background: "#000" }}>
                  <img src={img} alt="" style={{ width: "100%", height: "100%", objectFit: "contain", display: "block" }} />
                  <span style={{ position: "absolute", bottom: 3, left: 4, fontSize: 10, fontWeight: 700, color: "#fff", background: "rgba(0,0,0,.6)", borderRadius: 4, padding: "1px 6px" }}>{i + 1}</span>
                  <div style={{ position: "absolute", top: 3, right: 4, display: "flex", gap: 3 }}>
                    {m.styled && <span title="Styled" style={{ width: 15, height: 15, borderRadius: 4, background: "var(--vh-blue-500)", display: "grid", placeItems: "center" }}><Icon name="wand" size={9} style={{ color: "#fff" }} /></span>}
                    {m.html && <span title="Rebuilt in HTML" style={{ width: 15, height: 15, borderRadius: 4, background: "#26890c", display: "grid", placeItems: "center", fontSize: 9, fontWeight: 800, color: "#fff" }}>&lt;&gt;</span>}
                    {m.gamed && <span title="Gamified" style={{ width: 15, height: 15, borderRadius: 4, background: "#f99d25", display: "grid", placeItems: "center", fontSize: 9 }}>⚡</span>}
                  </div>
                </div>
              </button>);

          })}
        </div>

        {/* CENTER PREVIEW */}
        <div style={{ flex: 1, minWidth: 0, background: "#0c1828", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 16, padding: 28, position: "relative" }}>
          <div style={{ position: "relative", display: "inline-block", maxWidth: "100%", lineHeight: 0 }}>
            {cur.html ?
            <DeckHtmlSlide html={cur.html} ratio={ratio} maxW="58vw" maxH={g ? "calc(100vh - 430px)" : "calc(100vh - 200px)"} /> :
            <img src={slides[idx]} alt={"Slide " + (idx + 1)} style={{ maxWidth: "100%", maxHeight: g ? "calc(100vh - 430px)" : "calc(100vh - 200px)", objectFit: "contain", borderRadius: 6, boxShadow: "0 18px 50px rgba(0,0,0,.5)", display: "block" }} />}
            <DeckEnhanceLayer enh={cur} />
            {cur.html && <span style={{ position: "absolute", top: 10, right: 10, display: "inline-flex", alignItems: "center", gap: 5, background: "rgba(38,137,12,.95)", color: "#fff", fontSize: 11, fontWeight: 800, padding: "4px 10px", borderRadius: 999, zIndex: 5 }}>&lt;/&gt; HTML</span>}
          </div>
          {g && <DeckGameGate game={g} live={false} />}
          <div style={{ display: "flex", alignItems: "center", gap: 12, position: "absolute", bottom: 16 }}>
            <button onClick={() => setIdx(Math.max(0, idx - 1))} disabled={idx === 0} style={{ width: 40, height: 40, borderRadius: "50%", border: "none", background: "rgba(255,255,255,.12)", color: "#fff", cursor: idx === 0 ? "default" : "pointer", opacity: idx === 0 ? .3 : 1, display: "grid", placeItems: "center" }}><Icon name="chevL" size={20} /></button>
            <span style={{ fontSize: 13, color: "#8aa3c4", fontWeight: 600, minWidth: 70, textAlign: "center" }}>{idx + 1} / {TOTAL}</span>
            <button onClick={() => setIdx(Math.min(TOTAL - 1, idx + 1))} disabled={idx === TOTAL - 1} style={{ width: 40, height: 40, borderRadius: "50%", border: "none", background: "rgba(255,255,255,.12)", color: "#fff", cursor: idx === TOTAL - 1 ? "default" : "pointer", opacity: idx === TOTAL - 1 ? .3 : 1, display: "grid", placeItems: "center", transform: "scaleX(-1)" }}><Icon name="chevL" size={20} /></button>
          </div>
        </div>

        {/* INSPECTOR */}
        <div style={{ width: 340, flex: "none", borderLeft: "1px solid var(--line)", overflowY: "auto", background: "var(--card,#fff)" }} className="scroll">
          <div style={{ padding: "18px 20px", borderBottom: "1px solid var(--line)", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
            <div style={{ fontFamily: "var(--display)", fontWeight: 800, fontSize: 16, color: "var(--ink)" }}>Slide {idx + 1}</div>
            {(cur.title || cur.caption || cur.frame || cur.game) && <button onClick={clearSlide} className="btn btn-ghost btn-sm" style={{ color: "var(--ink-3)" }}><Icon name="x" size={13} />Clear</button>}
          </div>

          {/* REBUILD IN HTML */}
          <div style={{ padding: "18px 20px", borderBottom: "1px solid var(--line)" }}>
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }}><Icon name="wand" size={15} style={{ color: "#26890c" }} /><span style={{ fontFamily: "var(--display)", fontWeight: 800, fontSize: 14, color: "var(--ink)" }}>Rebuild in HTML</span></div>
            <p style={{ fontSize: 12.5, color: "var(--ink-3)", lineHeight: 1.5, marginBottom: 12 }}>Regenerate this slide as crisp, scalable HTML — describe how you want it different.</p>
            <textarea value={rprompt} onChange={(e) => setRprompt(e.target.value)} placeholder={"e.g. “Modern dark theme, big title and 3 concise bullets, accent orange”"} style={{ ...inputStyle, minHeight: 64, resize: "none", lineHeight: 1.5, marginBottom: 10 }} />
            <button onClick={rebuildHtml} disabled={rebuilding} style={{ width: "100%", padding: "11px 0", borderRadius: 9, border: "none", background: rebuilding ? "var(--surface-3,#dde6f2)" : "#26890c", color: rebuilding ? "var(--ink-3)" : "#fff", fontFamily: "var(--display)", fontWeight: 800, fontSize: 14, cursor: rebuilding ? "default" : "pointer", display: "flex", alignItems: "center", justifyContent: "center", gap: 8 }}>
              {rebuilding ? <>⏳ Rebuilding…</> : <><Icon name="wand" size={15} />{cur.html ? "Regenerate" : "Rebuild slide"}</>}
            </button>
            {cur.html &&
            <button onClick={revertHtml} className="btn btn-ghost btn-sm" style={{ width: "100%", justifyContent: "center", marginTop: 8, color: "var(--ink-2)" }}><Icon name="x" size={13} />Revert to original image</button>
            }
          </div>

          {/* STYLING — applies to the slide itself (per slide for now; whole-deck once the full presentation is HTML) */}
          <div style={{ padding: "18px 20px", borderBottom: "1px solid var(--line)" }}>
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 4 }}><Icon name="edit" size={15} style={{ color: "var(--vh-blue-500)" }} /><span style={{ fontFamily: "var(--display)", fontWeight: 800, fontSize: 14, color: "var(--ink)" }}>Slide styling</span></div>
            <p style={{ fontSize: 12, color: "var(--ink-3)", lineHeight: 1.5, marginBottom: 13 }}>Restyles the slide itself — not the frame around it.</p>

            <label style={lblStyle}>Apply styling to</label>
            <div style={{ display: "flex", gap: 7, marginBottom: showDeckNote ? 10 : 16 }}>
              <button onClick={() => setShowDeckNote(false)} style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, padding: "9px 0", borderRadius: 9, fontSize: 13, fontWeight: 700, fontFamily: "var(--body)", cursor: "pointer", border: "1.5px solid " + (!showDeckNote ? "var(--vh-blue-500)" : "var(--line)"), background: !showDeckNote ? "var(--vh-blue-50)" : "var(--surface)", color: !showDeckNote ? "var(--vh-blue-600)" : "var(--ink-2)" }}>
                <Icon name="slides" size={13} />This slide
              </button>
              <button onClick={() => setShowDeckNote(true)} title="Coming soon" style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, padding: "9px 0", borderRadius: 9, fontSize: 13, fontWeight: 700, fontFamily: "var(--body)", cursor: "pointer", border: "1.5px solid " + (showDeckNote ? "var(--vh-blue-300,#94bfe6)" : "var(--line)"), background: showDeckNote ? "var(--vh-blue-50)" : "var(--surface)", color: "var(--ink-3)" }}>
                <Icon name="lock" size={12} />Whole deck
              </button>
            </div>
            {showDeckNote &&
            <div style={{ display: "flex", gap: 9, background: "var(--vh-blue-50)", border: "1px solid var(--vh-blue-100,#dcebf8)", borderRadius: 10, padding: "11px 12px", marginBottom: 16 }}>
              <Icon name="wand" size={14} style={{ color: "var(--vh-blue-500)", flexShrink: 0, marginTop: 1 }} />
              <p style={{ fontSize: 12, color: "var(--ink-2)", lineHeight: 1.5 }}>Whole-deck styling arrives once the full presentation is converted to HTML. For now styling applies <b>per slide</b> — convert slides one by one with <b>Rebuild in HTML</b> above.</p>
            </div>
            }

            <label style={lblStyle}>Accent colour</label>
            <div style={{ display: "flex", gap: 8, marginBottom: 8 }}>
              {DECK_ACCENTS.map((c) =>
              <button key={c} onClick={() => applyAccent(c)} style={{ width: 30, height: 30, borderRadius: 8, background: c, border: (cur.accent || "#f99d25") === c ? "3px solid var(--ink)" : "2px solid var(--line)", cursor: "pointer" }} />
              )}
            </div>
            {cur.html ?
            <p style={{ fontSize: 11.5, color: "#26890c", fontWeight: 600, lineHeight: 1.5, marginBottom: 16 }}>✓ Recolours this slide's HTML directly.</p> :
            <div style={{ display: "flex", gap: 8, background: "var(--surface-2,#f4f7fb)", border: "1px solid var(--line)", borderRadius: 9, padding: "9px 11px", marginBottom: 16 }}>
              <Icon name="wand" size={13} style={{ color: "var(--ink-3)", flexShrink: 0, marginTop: 1 }} />
              <p style={{ fontSize: 11.5, color: "var(--ink-3)", lineHeight: 1.5 }}>This slide is still a static image, so the accent can't restyle it yet — use <b>Rebuild in HTML</b> above to make styling change the slide itself. Until then it colours the overlays and quiz gate.</p>
            </div>
            }

            <div style={{ display: "flex", alignItems: "center", gap: 8, margin: "2px 0 12px" }}>
              <span style={{ fontSize: 11, fontWeight: 800, color: "var(--ink-3)", textTransform: "uppercase", letterSpacing: ".07em", whiteSpace: "nowrap" }}>Overlays · on top of the slide</span>
              <div style={{ height: 1, background: "var(--line)", flex: 1 }}></div>
            </div>

            <label style={lblStyle}>Title overlay</label>
            <input value={cur.title || ""} onChange={(e) => patch({ title: e.target.value })} placeholder="e.g. Key concept" style={{ ...inputStyle, marginBottom: 14 }} />

            <label style={lblStyle}>Caption (lower third)</label>
            <textarea value={cur.caption || ""} onChange={(e) => patch({ caption: e.target.value })} placeholder="Short explanatory caption…" style={{ ...inputStyle, minHeight: 56, resize: "none", lineHeight: 1.5, marginBottom: 2 }} />
          </div>

          {/* GAMIFICATION */}
          <div style={{ padding: "18px 20px" }}>
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 12 }}><Icon name="bolt" size={15} style={{ color: "#f99d25" }} /><span style={{ fontFamily: "var(--display)", fontWeight: 800, fontSize: 14, color: "var(--ink)" }}>Gamification</span></div>

            {/* AI auto-gamify */}
            <div style={{ background: "rgba(249,157,37,.07)", border: "1px solid rgba(249,157,37,.25)", borderRadius: 11, padding: "13px 14px", marginBottom: 16 }}>
              <div style={{ fontSize: 12.5, fontWeight: 700, color: "#c97010", marginBottom: 8 }}>✨ Auto-gamify this slide</div>
              <p style={{ fontSize: 12, color: "var(--ink-3)", lineHeight: 1.5, marginBottom: 9 }}>AI analyses the slide{cur.html ? " (using its HTML content)" : ""} and proposes an interaction. Add an optional steer below.</p>
              <textarea value={gprompt} onChange={(e) => setGprompt(e.target.value)} placeholder={"Guide (optional) — e.g. “make it a hard multiple-choice on the key risk”"} style={{ ...inputStyle, minHeight: 52, resize: "none", lineHeight: 1.5, marginBottom: 9 }} />
              <button onClick={proposeGame} disabled={proposing} style={{ width: "100%", padding: "10px 0", borderRadius: 9, border: "none", background: proposing ? "var(--surface-3,#dde6f2)" : "#f99d25", color: proposing ? "var(--ink-3)" : "#231f20", fontFamily: "var(--display)", fontWeight: 800, fontSize: 13.5, cursor: proposing ? "default" : "pointer", display: "flex", alignItems: "center", justifyContent: "center", gap: 8 }}>
                {proposing ? <>⏳ Analysing…</> : <><Icon name="bolt" size={14} />{g ? "Propose again" : "Propose gamification"}</>}
              </button>
            </div>

            <label style={lblStyle}>Interaction type</label>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 7, marginBottom: g ? 16 : 0 }}>
              {[["", "None"], ["quiz", "Quiz"], ["kahoot", "Kahoot"], ["poll", "Poll"], ["reflect", "Reflection"], ["claude", "Ask Claude"]].map(([v, lbl]) => {
                const active = (g ? g.type : "") === v;
                return <button key={v} onClick={() => setGameType(v)} style={{ padding: "9px 0", borderRadius: 9, fontSize: 13, fontWeight: 700, border: "1.5px solid " + (active ? "#f99d25" : "var(--line)"), background: active ? "rgba(249,157,37,.12)" : "var(--surface)", color: active ? "#c97010" : "var(--ink-2)", cursor: "pointer", fontFamily: "var(--body)" }}>{lbl}</button>;
              })}
            </div>

            {g &&
            <div>
                <label style={lblStyle}>{g.type === "claude" ? "Question for the learner to answer" : g.type === "reflect" ? "Reflection prompt" : "Question"}</label>
                <textarea value={g.q} onChange={(e) => patchGame({ q: e.target.value })} placeholder={g.type === "claude" ? "e.g. In your own words, what is stakeholder management?" : "What would you like to ask?"} style={{ ...inputStyle, minHeight: 56, resize: "none", lineHeight: 1.5, marginBottom: 14 }} />

                {(g.type === "quiz" || g.type === "poll" || g.type === "kahoot") &&
              <div style={{ marginBottom: 14 }}>
                    <label style={lblStyle}>{g.type === "quiz" || g.type === "kahoot" ? "Options (tap ● to mark correct)" : "Options"}</label>
                    <div style={{ display: "flex", flexDirection: "column", gap: 7 }}>
                      {g.options.map((opt, oi) =>
                  <div key={oi} style={{ display: "flex", alignItems: "center", gap: 8 }}>
                          {(g.type === "quiz" || g.type === "kahoot") &&
                    <button onClick={() => patchGame({ answer: oi })} title="Mark correct" style={{ width: 22, height: 22, borderRadius: "50%", flexShrink: 0, border: "2px solid " + (g.answer === oi ? "#26890c" : "var(--line)"), background: g.answer === oi ? "#26890c" : "transparent", cursor: "pointer", display: "grid", placeItems: "center" }}>{g.answer === oi && <Icon name="check" size={12} style={{ color: "#fff" }} />}</button>
                    }
                          <span style={{ width: 20, fontSize: 13, fontWeight: 700, color: "var(--ink-3)", flexShrink: 0 }}>{String.fromCharCode(65 + oi)}</span>
                          <input value={opt} onChange={(e) => {const o = [...g.options];o[oi] = e.target.value;patchGame({ options: o });}} placeholder={"Option " + String.fromCharCode(65 + oi)} style={{ ...inputStyle, padding: "7px 10px", fontSize: 13.5 }} />
                        </div>
                  )}
                    </div>
                  </div>
              }

                {g.type === "kahoot" &&
              <div style={{ marginBottom: 14 }}>
                    <label style={lblStyle}>Time limit</label>
                    <div style={{ display: "flex", gap: 7 }}>
                      {[10, 20, 30, 45].map((v) =>
                  <button key={v} onClick={() => patchGame({ time: v })} style={{ flex: 1, padding: "8px 0", borderRadius: 8, fontSize: 13, fontWeight: 800, fontFamily: "var(--display)", border: "1.5px solid " + ((g.time || 20) === v ? "#f99d25" : "var(--line)"), background: (g.time || 20) === v ? "rgba(249,157,37,.12)" : "var(--surface)", color: (g.time || 20) === v ? "#c97010" : "var(--ink-2)", cursor: "pointer" }}>{v}s</button>
                  )}
                    </div>
                  </div>
              }

                <label style={lblStyle}>{g.type === "kahoot" ? "Max points (speed-scaled)" : "XP reward"}</label>
                <div style={{ display: "flex", gap: 7 }}>
                  {[20, 50, 100, 200].map((v) =>
                <button key={v} onClick={() => patchGame({ xp: v })} style={{ flex: 1, padding: "8px 0", borderRadius: 8, fontSize: 13, fontWeight: 800, fontFamily: "var(--display)", border: "1.5px solid " + (g.xp === v ? "#f99d25" : "var(--line)"), background: g.xp === v ? "rgba(249,157,37,.12)" : "var(--surface)", color: g.xp === v ? "#c97010" : "var(--ink-2)", cursor: "pointer" }}>{v}</button>
                )}
                </div>
              </div>
            }
          </div>
        </div>
      </div>

      {preview && <UploadedDeckViewer deck={{ ...deck, slides, enhance: enh, gamified: Object.values(enh).some((e) => e && e.game) }} toast={toast} onClose={() => setPreview(false)} />}
    </div>, document.body);
}

Object.assign(window, { StudyBuilder });