// ====================================================================
// lw-studio-app.jsx — Last Wishes Studio (champion concept)
// Shell mirrors the PO Activation Prototype: Nav + stage + persistent
// Helm chat rail. Screens: intro → theme → prep → studio → review → done.
// ====================================================================
const { useState: useStateAPP, useEffect: useEffectAPP, useRef: useRefAPP, useCallback: useCbAPP, useMemo: useMemoAPP } = React;

const LW_SCREEN_TITLE = {
  intro:  "Personal videos",
  theme:  "Personal videos",
  prep:   (t) => `${t.label} · Video ${t.id === "payout" ? "2" : "1"} of 2`,
  studio: (t) => `Recording · ${t.label}`,
  review: (t) => `${t.label} · review`,
  done:   "Personal videos",
};

function StudioApp() {
  const [screen, setScreen] = useStateAPP("intro");
  const [beneId, setBeneId] = useStateAPP(null);
  const [themeId, setThemeId] = useStateAPP("hope");
  const [beats, setBeats] = useStateAPP([]);
  const [segClips, setSegClips] = useStateAPP([]);
  const [saved, setSaved] = useStateAPP({});        // { john: ['hope'], ... }
  const [savedVideos, setSavedVideos] = useStateAPP({}); // { john: { hope: {beats, segClips} } }
  const [watchVideo, setWatchVideo] = useStateAPP(null);
  const [railBump, setRailBump] = useStateAPP(0);
  const [example, setExample] = useStateAPP(null);
  const [askOpen, setAskOpen] = useStateAPP(false);
  const [studioStart, setStudioStart] = useStateAPP(0);
  const [convo, setConvo] = useStateAPP([]);
  const [isTyping, setIsTyping] = useStateAPP(false);
  const streamIdRef = useRefAPP(0);
  const streamedRef = useRefAPP(new Set());
  const rec = useRecorder();

  const bene = beneId ? lwBene(beneId) : null;
  const theme = lwTheme(themeId);
  const suggestions = useMemoAPP(() => (beneId ? lwPrompts(beneId, themeId) : []), [beneId, themeId]);
  const savedForBene = (saved[beneId] || []);

  // ── conversation helpers (mirrors the main prototype) ─────────────
  const streamMessages = useCbAPP((msgs) => {
    if (!msgs || !msgs.length) return;
    const id = ++streamIdRef.current;
    let i = 0;
    const reveal = () => {
      if (id !== streamIdRef.current) return;
      if (i >= msgs.length) { setIsTyping(false); return; }
      setIsTyping(true);
      const m = msgs[i];
      const len = typeof m.text === "string" ? m.text.length : 80;
      const ms = Math.min(1200, Math.max(500, len * 10));
      setTimeout(() => {
        if (id !== streamIdRef.current) return;
        setConvo((p) => [...p, m]); i++;
        if (i >= msgs.length) setIsTyping(false); else setTimeout(reveal, 340);
      }, ms);
    };
    reveal();
  }, []);

  const logActivity = useCbAPP((text, meta) => {
    setConvo((p) => [...p, { from: "activity", text, meta }]);
  }, []);

  // ── stream each screen's intro lines once per context ─────────────
  useEffectAPP(() => {
    const key = `${screen}:${beneId || "-"}:${themeId}`;
    if (screen === "studio") return;                 // studio talks via the pill on demand
    if (streamedRef.current.has(key)) return;
    streamedRef.current.add(key);
    const lines = scriptFor(screen, bene, theme, savedForBene);
    if (lines.length) setTimeout(() => streamMessages(lines), 260);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [screen, beneId, themeId]);

  // ── free-text → Helm (live with graceful fallback) ────────────────
  const handleUserSend = useCbAPP(async (text) => {
    setConvo((p) => [...p, { from: "user", text }]);
    let reply = null;
    if (window.claude && typeof window.claude.complete === "function") {
      try {
        const ctx = bene ? `They are recording a "${theme.label}" video for ${bene.firstName} (${bene.rel}).` : "They are about to record short personal videos for loved ones.";
        reply = await window.claude.complete(
          `You are Helm, a warm, calm guide helping someone record short "last wishes" videos for the people they love, to be watched after they pass. ${ctx} ` +
          `They said: "${text}". Reply in 1–3 short, kind, practical sentences. Be encouraging and human; never clinical or morbid. Do not use lists.`);
        reply = (reply || "").trim();
      } catch (e) { reply = null; }
    }
    if (!reply) reply = cannedReply(text, bene);
    setConvo((p) => [...p, { from: "ai", prose: true, text: reply }]);
  }, [bene, theme]);

  // ── transitions ───────────────────────────────────────────────────
  const seedBeats = (bid, tid) => lwPrompts(bid, tid).slice(0, 4).map((p, i) => ({ ...p, key: tid + "_" + i }));

  const pickBene = (id) => { setBeneId(id); logActivity(`Recording for ${lwBene(id).name}`, lwBene(id).rel); setScreen("theme"); };
  const startTheme = (tid) => {
    setThemeId(tid);
    setBeats(seedBeats(beneId, tid));
    setSegClips(new Array(4).fill(null));
    rec.resetAll();
    logActivity(`Starting: ${lwTheme(tid).label}`, `Video ${tid === "payout" ? "2" : "1"} of 2`);
    setScreen("prep");
  };
  // keep segClips array sized to beats
  useEffectAPP(() => {
    setSegClips((s) => { const n = beats.map((_, i) => s[i] || null); return n; });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [beats.length]);

  // landing on the review screen saves the current cut to the inventory
  // (reads settled segClips → no stale/missing clip)
  useEffectAPP(() => {
    if (screen === "review" && beneId && segClips.some(Boolean)) {
      setSavedVideos((s) => ({ ...s, [beneId]: { ...(s[beneId] || {}), [themeId]: { beats, segClips } } }));
      setSaved((s) => { const cur = s[beneId] || []; return cur.includes(themeId) ? s : { ...s, [beneId]: [...cur, themeId] }; });
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [screen, segClips]);

  const enterStudio = useCbAPP(async () => {
    if (rec.status === "idle") await rec.requestCamera();
    setScreen("studio");
  }, [rec]);

  // reorder beats AND their recorded clips together (review screen, drag-and-drop)
  const reorderBeats = (from, to) => {
    setBeats((arr) => { const n = [...arr]; const [m] = n.splice(from, 1); n.splice(to, 0, m); return n; });
    setSegClips((arr) => { const n = [...arr]; const [m] = n.splice(from, 1); n.splice(to, 0, m); return n; });
  };
  const addBeatInReview = (b) => { const newIdx = beats.length; setBeats((arr) => [...arr, b]); setStudioStart(newIdx); setScreen("studio"); };
  // remove a beat AND its recorded clip together (review screen, with confirm)
  const deleteBeat = (i) => {
    setBeats((arr) => arr.filter((_, idx) => idx !== i));
    setSegClips((arr) => arr.filter((_, idx) => idx !== i));
  };

  const saveVideo = () => {
    setSaved((s) => ({ ...s, [beneId]: [...new Set([...(s[beneId] || []), themeId])] }));
    setSavedVideos((s) => ({ ...s, [beneId]: { ...(s[beneId] || {}), [themeId]: { beats, segClips } } }));
    logActivity(`Saved: ${theme.label} for ${bene.firstName}`, `${beats.length} beats`);
    setScreen("theme");
  };
  const playVideo = (tid) => { const v = (savedVideos[beneId] || {})[tid]; if (v) setWatchVideo({ ...v, theme: lwTheme(tid) }); };
  const editVideo = (tid) => { const v = (savedVideos[beneId] || {})[tid]; if (!v) return; setThemeId(tid); setBeats(v.beats); setSegClips(v.segClips); setScreen("review"); };

  const recordOther = () => {
    const other = themeId === "hope" ? "payout" : "hope";
    startTheme(other);
  };
  const resetAll = () => {
    streamIdRef.current++; setIsTyping(false);
    setScreen("intro"); setBeneId(null); setThemeId("hope"); setBeats([]); setSegClips([]);
    setSaved({}); setSavedVideos({}); setWatchVideo(null); setConvo([]); streamedRef.current = new Set(); rec.resetAll();
  };

  // ── chat config per screen ────────────────────────────────────────
  const chatStyle = screen === "studio" ? "pill" : "panel";
  const titleSrc = LW_SCREEN_TITLE[screen];
  const title = typeof titleSrc === "function" ? titleSrc(theme) : titleSrc;

  const quickReplies = useMemoAPP(() => {
    if (isTyping) return [];
    if (screen === "prep") return [
      { label: "I'm ready — open the studio", onClick: enterStudio },
      { label: "Show me an example", soft: true, onClick: () => setExample(theme.id === "payout" ? LW_EXAMPLES[1] : LW_EXAMPLES[0]) },
    ];
    if (screen === "review") return [
      { label: "Save this video", onClick: saveVideo },
      { label: "Re-record a beat", soft: true, onClick: () => setScreen("studio") },
    ];
    if (screen === "done") {
      const both = savedForBene.includes("hope") && savedForBene.includes("payout");
      if (!both) return [{ label: `Record ${themeId === "hope" ? "guidance for the payout" : "the message of hope"}`, onClick: recordOther },
        { label: "I'll finish later", soft: true, onClick: resetAll }];
      return [{ label: "Record for someone else", onClick: resetAll }, { label: "I'm all done", soft: true, onClick: resetAll }];
    }
    return [];
  }, [screen, isTyping, theme, savedForBene, themeId, enterStudio]);

  const customCard = useMemoAPP(() => {
    if (isTyping) return null;
    if (screen === "prep" && bene) return (<HowItWorksCard theme={theme} />);
    if (screen === "review") return (
      <ReviewModule beats={beats} segClips={segClips} onReorder={reorderBeats} suggestions={suggestions} theme={theme} onAddBeat={addBeatInReview} onExpand={() => setRailBump((n) => n + 1)} onRedo={(i) => { rec.retake(true); setStudioStart(i); setScreen("studio"); }} onDelete={deleteBeat} />
    );
    return null;
  }, [screen, isTyping, bene, theme, beats, segClips, suggestions]);

  return (
    <div className="app-layout">
      <Nav onLogoClick={resetAll} avatarInitials="SG" />
      <div className="stage-with-rail">
        <div className="stage">
          {screen === "intro" && <div data-screen-label="01 Choose recipient"><IntroStage onPick={pickBene} savedVideos={savedVideos} /></div>}
          {screen === "theme" && <div data-screen-label="02 Choose video"><ThemeStage bene={bene} savedMap={savedVideos[beneId] || {}} onStart={startTheme} onPlay={playVideo} onEdit={editVideo} onBack={() => setScreen("intro")} /></div>}
          {screen === "prep" && <div data-screen-label="03 Prepare"><PrepStage bene={bene} theme={theme} beats={beats} setBeats={setBeats} suggestions={suggestions} onEnter={enterStudio} onExample={() => setExample(theme.id === "payout" ? LW_EXAMPLES[1] : LW_EXAMPLES[0])} onBack={() => setScreen("theme")} /></div>}
          {screen === "studio" && <div data-screen-label="04 Studio">
            <StudioScreen bene={bene} theme={theme} beats={beats} segClips={segClips} setSegClips={setSegClips}
              rec={rec} logActivity={logActivity} startAt={studioStart}
              onExit={() => { setStudioStart(0); setScreen("prep"); }} onReviewAll={() => setScreen("review")} />
          </div>}
          {screen === "review" && <div data-screen-label="05 Review"><ReviewStage bene={bene} theme={theme} beats={beats} segClips={segClips} onSave={saveVideo} /></div>}
          {screen === "done" && <div data-screen-label="06 Saved"><DoneStage bene={bene} theme={theme} saved={savedForBene} onRecordOther={recordOther} onReset={resetAll} /></div>}
        </div>

        {/* ── persistent Helm rail · hidden in the studio (Ask-Helm launcher instead) ── */}
        {screen !== "studio" ? (
          <>
            <div className="chat-rail-spacer" />
            <ChatGuide style="panel" title={title} messages={convo} quickReplies={quickReplies}
              inputEnabled={true} onUserSend={handleUserSend} busy={isTyping} customCard={customCard}
              bottomKey={beats.length + (saved[beneId]?.length || 0) + railBump} />
          </>
        ) : askOpen ? (
          <>
            <div onClick={() => setAskOpen(false)} style={{ position: "fixed", inset: 0, zIndex: 49,
              background: "rgba(0,12,0,.28)" }} />
            <ChatGuide style="panel" title="Ask Helm" messages={convo} quickReplies={[]} inputEnabled={true}
              onUserSend={handleUserSend} busy={isTyping} />
            <button onClick={() => setAskOpen(false)} className="lw-press" aria-label="Close Helm" style={{
              position: "fixed", top: 100, right: 460 + 24 - 6, zIndex: 60, width: 36, height: 36, borderRadius: "50%",
              border: "1px solid var(--helm-dark-green)", background: "var(--helm-white)", cursor: "pointer",
              boxShadow: "var(--shadow-card)", fontSize: 18, lineHeight: 1, color: "var(--helm-dark-green)" }}>×</button>
          </>
        ) : (
          <button onClick={() => setAskOpen(true)} className="lw-press" style={{ position: "fixed", right: 26, bottom: 158,
            zIndex: 55, display: "flex", alignItems: "center", gap: 10, padding: "12px 20px 12px 14px", borderRadius: 999,
            background: "var(--helm-white)", border: "1px solid var(--helm-dark-green)", boxShadow: "var(--shadow-card)",
            cursor: "pointer", fontFamily: "var(--font-sans)", fontWeight: 600, fontSize: 14, color: "var(--helm-dark-green)" }}>
            <span style={{ width: 30, height: 30, borderRadius: "50%", background: "var(--helm-dark-green)", display: "flex",
              alignItems: "center", justifyContent: "center" }}><LWStar size={16} on="light" /></span>
            Ask Helm
          </button>
        )}
      </div>

      {example && <ExampleClip ex={example} onClose={() => setExample(null)} />}

      {watchVideo && (
        <div onClick={() => setWatchVideo(null)} style={{ position: "fixed", inset: 0, zIndex: 130, background: "rgba(12,18,12,.6)",
          backdropFilter: "blur(8px)", display: "flex", alignItems: "center", justifyContent: "center", padding: 40 }}>
          <div onClick={(e) => e.stopPropagation()} style={{ width: 860, maxWidth: "100%", borderRadius: 24, overflow: "hidden",
            background: "#0c160c", boxShadow: "0 24px 80px rgba(0,0,0,.5)" }}>
            <div style={{ position: "relative", aspectRatio: "16 / 10" }}>
              <StitchPlayer segments={watchVideo.beats.map((b, i) => ({ label: b.t, clip: watchVideo.segClips[i] }))} poster={LW_PO.photo} />
            </div>
            <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "16px 22px",
              background: "#0c160c", color: "rgba(254,252,245,.9)", fontFamily: "var(--font-sans)" }}>
              <span style={{ fontSize: 15, fontWeight: 600 }}>{watchVideo.theme.label} · {bene ? bene.firstName : ""}</span>
              <button onClick={() => setWatchVideo(null)} style={{ background: "none", border: "none", color: "rgba(254,252,245,.7)",
                cursor: "pointer", fontSize: 14, fontWeight: 600, fontFamily: "var(--font-sans)" }}>Close</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ── Scripted Helm intros per screen (streamed once) ──────────────────
function scriptFor(screen, bene, theme, savedForBene) {
  const n = bene ? bene.firstName : "them";
  if (screen === "intro") return [
    { from: "ai", prose: true, text: "Now for the most personal part of your plan — short videos for the people you love, to be opened after you're gone." },
    { from: "ai", prose: true, text: "There's no rush, and nothing here is final. Who would you like to record for first?" },
  ];
  if (screen === "theme") return [
    { from: "ai", prose: true, text: `Let's make two short videos for ${n} — one with your hopes for ${bene && /her|daughter/i.test(bene.rel) ? "her" : "them"}, and one about the money you're leaving.` },
    { from: "ai", prose: true, text: "Most people find the first one easier than they expect. Pick whichever feels right to start with." },
  ];
  if (screen === "prep") {
    const breathe = theme.id === "payout"
      ? `This one's practical — just ${n} and a few clear wishes about the money.`
      : "Take a breath. We'll go gently, one small piece at a time.";
    return [
      { from: "ai", prose: true, text: breathe },
      { from: "ai", prose: true, text: "We'll record one beat at a time — about twenty seconds each. After every beat you can watch it back and redo it if you'd like, so nothing has to be perfect on the first try." },
      { from: "ai", prose: true, text: "Here are a few things you might touch on. Keep the ones that fit, reorder them, or add your own — then open the studio when you're ready." },
    ];
  }
  if (screen === "review") return [
    { from: "ai", prose: true, text: `Here's your message for ${n}, start to finish.` },
    { from: "ai", prose: true, text: "Watch it through. If any one beat feels off, you can redo just that piece — the rest stays exactly as it is." },
  ];
  if (screen === "done") {
    const both = savedForBene.includes("hope") && savedForBene.includes("payout");
    return both
      ? [{ from: "ai", prose: true, text: `Both videos for ${n} are saved.` },
         { from: "ai", prose: true, text: "They'll be kept safe and shared only when the time comes. You can re-record or add more from your plan whenever you like." }]
      : [{ from: "ai", prose: true, text: "Saved. That was the brave part." },
         { from: "ai", prose: true, text: `Whenever you're ready, the ${theme.id === "hope" ? "payout guidance" : "message of hope"} is just a few more short beats.` }];
  }
  return [];
}

function cannedReply(text, bene) {
  const t = (text || "").toLowerCase();
  const n = bene ? bene.firstName : "them";
  if (/nervous|scared|hard|cry|emotional|don't know|cant|can't/.test(t))
    return `That's completely normal — almost everyone feels that way. You can pause, laugh, or tear up and keep going; ${n} will treasure that it's real. Start with just their name.`;
  if (/how long|length|time|minutes?/.test(t))
    return "Short is better than long — a minute or two per video is plenty. Each beat only needs about twenty seconds.";
  if (/what.*say|words|stuck/.test(t))
    return `Tap “See an example” on any beat and you'll get a line you can read or reshape. Speaking to ${n} like they're in the room with you works best.`;
  if (/redo|again|mistake|restart|delete/.test(t))
    return "You can redo any single beat without touching the others, as many times as you like — and nothing saves until you choose to keep it.";
  return `I'm right here with you. Say it the way you'd say it to ${n} at the kitchen table — that's always the right version.`;
}

// ════════════════════════════════════════════════════════════════════
// STAGE SCREENS (left of the rail), on the purple brand background
// ════════════════════════════════════════════════════════════════════

function IntroStage({ onPick, savedVideos, benes = LW_BENES }) {
  return (
    <div style={{ position: "relative", width: "100%", height: "100%" }}>
      <WaveBg>
        <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", justifyContent: "center",
          padding: "0 72px" }} className="lw-fade">
          <div style={{ maxWidth: 560 }}>
            <div style={{ display: "inline-flex", alignItems: "center", gap: 8, marginBottom: 22, padding: "7px 15px",
              borderRadius: 999, background: "var(--helm-white)" }}>
              <LWStar size={15} /><span style={{ fontFamily: "var(--font-sans)", fontWeight: 600, fontSize: 13,
                color: "var(--helm-dark-green)" }}>The most personal part of your plan</span>
            </div>
            <h1 style={{ fontFamily: "var(--font-serif)", fontSize: 52, lineHeight: "56px", letterSpacing: "-.01em",
              color: "var(--helm-dark-green)", margin: "0 0 16px" }}>A message only you can leave.</h1>
            <p className="prose" style={{ color: "var(--helm-dark-green-2)", margin: "0 0 32px", maxWidth: 470 }}>
              We'll record short videos for the people you love. Choose who to start with — you can record for anyone
              in your plan, in any order.</p>
            <div style={{ display: "flex", flexDirection: "column", gap: 12, maxWidth: 380 }}>
              {benes.map((b) => (
                <button key={b.id} onClick={() => onPick(b.id)} style={{ display: "flex", alignItems: "center",
                  gap: 14, padding: "12px 18px 12px 12px", borderRadius: 16, background: "var(--helm-white)",
                  border: "1px solid var(--helm-dark-green)", boxShadow: "var(--shadow-card)", cursor: "pointer",
                  transition: "transform .14s ease, box-shadow .14s ease" }}
                  onMouseEnter={e => { e.currentTarget.style.transform = "translate(-2px, -2px)"; e.currentTarget.style.boxShadow = "6px 6px 0 0 var(--helm-dark-green)"; }}
                  onMouseLeave={e => { e.currentTarget.style.transform = "none"; e.currentTarget.style.boxShadow = "var(--shadow-card)"; }}>
                  <Avatar bene={b} size={48} />
                  <div style={{ flex: 1, textAlign: "left" }}>
                    <div style={{ fontFamily: "var(--font-sans)", fontWeight: 600, fontSize: 16, color: "var(--helm-dark-green)" }}>{b.name}</div>
                    <div style={{ fontFamily: "var(--font-sans)", fontSize: 13, color: "var(--helm-dark-grey)" }}>{b.rel}</div>
                  </div>
                  {(() => { const n = Object.keys((savedVideos || {})[b.id] || {}).length; return (
                    <span style={{ display: "flex", alignItems: "center", gap: 6, padding: "5px 11px", borderRadius: 999, whiteSpace: "nowrap",
                      background: n > 0 ? "var(--helm-soft-chartreuse)" : "var(--helm-ecru)", color: n > 0 ? "var(--helm-white)" : "var(--helm-dark-grey)",
                      fontFamily: "var(--font-sans)", fontSize: 11.5, fontWeight: 700 }}>
                      {n > 0 && <svg width="11" height="11" viewBox="0 0 16 16" fill="none" stroke="#fff" strokeWidth="2.8" strokeLinecap="round" strokeLinejoin="round"><path d="M3.5 8.5l3 3 6-7"/></svg>}
                      {n} of 2 recorded</span>
                  ); })()}
                  <svg width="18" height="18" viewBox="0 0 16 16" fill="none" stroke="var(--helm-dark-green)" strokeWidth="2" strokeLinecap="round"><path d="M6 3l5 5-5 5"/></svg>
                </button>
              ))}
            </div>
          </div>
        </div>
      </WaveBg>
    </div>
  );
}

function ThemeStage({ bene, savedMap, onStart, onPlay, onEdit, onBack }) {
  if (!bene) return null;
  const savedCount = Object.keys(savedMap || {}).length;
  return (
    <div style={{ position: "relative", width: "100%", height: "100%" }}>
      <WaveBg>
        <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", justifyContent: "center",
          padding: "0 72px" }} className="lw-fade">
          <button onClick={onBack} className="lw-press" style={{ alignSelf: "flex-start", display: "flex", alignItems: "center", gap: 7,
            marginBottom: 20, padding: "8px 14px 8px 10px", borderRadius: 999, background: "var(--helm-white)",
            border: "1px solid var(--helm-dark-green)", cursor: "pointer", fontFamily: "var(--font-sans)", fontSize: 13,
            fontWeight: 600, color: "var(--helm-dark-green)" }}>
            <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M10 3L5 8l5 5"/></svg>
            Someone else</button>
          <div style={{ display: "flex", alignItems: "center", gap: 14, marginBottom: 18 }}>
            <Avatar bene={bene} size={56} radius={14} />
            <div>
              <div style={{ fontFamily: "var(--font-sans)", fontSize: 13, color: "var(--helm-dark-green-2)" }}>Personal videos for</div>
              <div style={{ fontFamily: "var(--font-serif)", fontSize: 30, lineHeight: "34px", color: "var(--helm-dark-green)" }}>{bene.name}</div>
            </div>
            <span style={{ marginLeft: 6, display: "flex", alignItems: "center", gap: 7, padding: "7px 14px", borderRadius: 999,
              background: savedCount === 2 ? "var(--helm-chartreuse-2)" : "var(--helm-white)", color: savedCount === 2 ? "#fff" : "var(--helm-dark-green)",
              border: savedCount === 2 ? "none" : "1px solid var(--helm-dark-green)", fontFamily: "var(--font-sans)", fontSize: 13, fontWeight: 700 }}>
              {savedCount === 2 && <svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="#fff" strokeWidth="2.8" strokeLinecap="round" strokeLinejoin="round"><path d="M3.5 8.5l3 3 6-7"/></svg>}
              {savedCount} of 2 videos recorded</span>
          </div>
          <div style={{ display: "flex", gap: 16, maxWidth: 760 }}>
            {LW_THEMES.map((t, idx) => {
              const v = (savedMap || {})[t.id];
              const isSaved = !!v;
              return (
                <div key={t.id} style={{ flex: 1, position: "relative", display: "flex", flexDirection: "column",
                  padding: 24, borderRadius: 18, background: "var(--helm-white)", border: "1px solid var(--helm-dark-green)",
                  boxShadow: "var(--shadow-card)", transition: "transform .14s ease, box-shadow .14s ease" }}
                  onMouseEnter={e => { e.currentTarget.style.transform = "translate(-2px, -2px)"; e.currentTarget.style.boxShadow = "6px 6px 0 0 var(--helm-dark-green)"; }}
                  onMouseLeave={e => { e.currentTarget.style.transform = "none"; e.currentTarget.style.boxShadow = "var(--shadow-card)"; }}>
                  <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 16,
                    padding: "14px 16px", borderRadius: 14, background: t.accent + "26" }}>
                    <span style={{ width: 46, height: 46, borderRadius: 12, flexShrink: 0, background: "var(--helm-white)",
                      display: "flex", alignItems: "center", justifyContent: "center" }}>
                      <ThemeIcon id={t.id} color={t.accent} /></span>
                    {isSaved ? (
                      <span style={{ display: "flex", alignItems: "center", gap: 6, padding: "5px 11px", borderRadius: 999,
                        background: "var(--helm-soft-chartreuse)", color: "var(--helm-white)", fontFamily: "var(--font-sans)",
                        fontSize: 11.5, fontWeight: 700, letterSpacing: ".04em" }}>
                        <svg width="11" height="11" viewBox="0 0 16 16" fill="none" stroke="#fff" strokeWidth="2.8" strokeLinecap="round" strokeLinejoin="round"><path d="M3.5 8.5l3 3 6-7"/></svg>
                        RECORDED</span>
                    ) : (
                      <span style={{ display: "flex", alignItems: "center", gap: 8, padding: "6px 12px 6px 6px", borderRadius: 999,
                        background: "var(--helm-white)", fontFamily: "var(--font-sans)", fontSize: 11.5, fontWeight: 700,
                        letterSpacing: ".05em", textTransform: "uppercase", color: "var(--helm-dark-green)" }}>
                        <span style={{ width: 20, height: 20, borderRadius: 6, background: t.accent + "33", display: "flex",
                          alignItems: "center", justifyContent: "center", fontFamily: "var(--font-serif)", fontSize: 12 }}>{idx + 1}</span>
                        {t.order}</span>
                    )}
                  </div>
                  <div style={{ fontFamily: "var(--font-serif)", fontSize: 23, lineHeight: "27px", color: "var(--helm-dark-green)", marginBottom: 7 }}>{t.label}</div>
                  <div style={{ fontFamily: "var(--font-sans)", fontSize: 14.5, lineHeight: "21px", color: "var(--helm-dark-green-2)", marginBottom: 16 }}>{t.blurb}</div>
                  {isSaved ? (
                    <>
                      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 18, fontFamily: "var(--font-sans)",
                        fontSize: 13.5, color: "var(--helm-dark-green-2)" }}>
                        <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="var(--helm-chartreuse-2)" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><path d="M3.5 8.5l3 3 6-7"/></svg>
                        {v.beats.length} {v.beats.length === 1 ? "beat" : "beats"} recorded</div>
                      <div style={{ marginTop: "auto", display: "flex", gap: 10 }}>
                        <button onClick={() => onPlay(t.id)} className="lw-press" style={{ display: "inline-flex", alignItems: "center", gap: 8,
                          padding: "11px 20px", borderRadius: 999, background: "var(--helm-white)", border: "1px solid var(--helm-dark-green)",
                          cursor: "pointer", fontFamily: "var(--font-sans)", fontWeight: 700, fontSize: 15, color: "var(--helm-dark-green)" }}>
                          <svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M7 4.5v15l13-7.5z"/></svg>Play</button>
                        <button onClick={() => onEdit(t.id)} className="lw-press" style={{ display: "inline-flex", alignItems: "center", gap: 8,
                          padding: "11px 20px", borderRadius: 999, background: "var(--helm-chartreuse-2)", border: "none",
                          cursor: "pointer", fontFamily: "var(--font-sans)", fontWeight: 700, fontSize: 15, color: "var(--helm-white)" }}>Edit</button>
                      </div>
                    </>
                  ) : (
                    <>
                      <div style={{ fontFamily: "var(--font-sans)", fontSize: 11, fontWeight: 700, letterSpacing: ".07em",
                        textTransform: "uppercase", color: "var(--helm-dark-grey)", marginBottom: 9 }}>You'll touch on</div>
                      <div style={{ display: "flex", flexDirection: "column", gap: 7, marginBottom: 20 }}>
                        {lwPrompts(bene, t.id).slice(0, 3).map((p, i) => (
                          <div key={i} style={{ display: "flex", alignItems: "center", gap: 9, fontFamily: "var(--font-sans)",
                            fontSize: 13.5, lineHeight: "17px", color: "var(--helm-dark-green)" }}>
                            <span style={{ width: 7, height: 7, borderRadius: "50%", background: t.accent, flexShrink: 0 }} />{p.t}</div>
                        ))}
                      </div>
                      <button onClick={() => onStart(t.id)} className="lw-press" style={{ marginTop: "auto", display: "inline-flex", alignItems: "center", gap: 8,
                        alignSelf: "flex-start", padding: "11px 22px", borderRadius: 999, background: "var(--helm-chartreuse-2)", border: "none",
                        cursor: "pointer", color: "var(--helm-white)", fontFamily: "var(--font-sans)", fontWeight: 700, fontSize: 15 }}>
                        Start
                        <svg width="15" height="15" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M6 3l5 5-5 5"/></svg></button>
                    </>
                  )}
                </div>
              );
            })}
          </div>
        </div>
      </WaveBg>
    </div>
  );
}

function ThemeIcon({ id, color }) {
  if (id === "payout") return (
    <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <path d="M12 3l7 3v5c0 4.5-3 7.6-7 9-4-1.4-7-4.5-7-9V6z"/>
      <path d="M9 12l2 2 4-4"/>
    </svg>
  );
  return (
    <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <path d="M3 18.5h18"/>
      <path d="M7 18.5a5 5 0 0 1 10 0"/>
      <path d="M12 4.5V6.5M5.2 8.2l1.4 1.4M18.8 8.2l-1.4 1.4"/>
    </svg>
  );
}

function PrepStage({ bene, theme, beats, setBeats, suggestions, onEnter, onExample, onBack }) {
  if (!bene) return null;
  return (
    <div style={{ position: "relative", width: "100%", height: "100%" }}>
      <WaveBg>
        <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", justifyContent: "center",
          padding: "0 64px" }} className="lw-fade">
          <div style={{ width: "100%", maxWidth: 600 }}>
            <button onClick={onBack} className="lw-press" style={{ display: "inline-flex", alignItems: "center", gap: 7,
              marginBottom: 16, padding: "8px 14px 8px 10px", borderRadius: 999, background: "var(--helm-white)",
              border: "1px solid var(--helm-dark-green)", cursor: "pointer", fontFamily: "var(--font-sans)", fontSize: 13,
              fontWeight: 600, color: "var(--helm-dark-green)" }}>
              <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M10 3L5 8l5 5"/></svg>
              Choose a different video</button>
            <div style={{ fontFamily: "var(--font-sans)", fontSize: 13, fontWeight: 600, letterSpacing: ".08em",
              textTransform: "uppercase", color: "var(--helm-dark-green-2)", marginBottom: 10 }}>{theme.order} · for {bene.firstName}</div>
            <h1 style={{ fontFamily: "var(--font-serif)", fontSize: 40, lineHeight: "44px", letterSpacing: "-.01em",
              color: "var(--helm-dark-green)", margin: "0 0 10px" }}>{theme.label}</h1>
            <p className="prose" style={{ color: "var(--helm-dark-green-2)", margin: "0 0 20px", maxWidth: 520, fontSize: 19, lineHeight: "27px" }}>
              Put your message together — keep the prompts that fit, reorder them, or write your own.</p>
            <div style={{ maxHeight: "46vh", overflowY: "auto", marginBottom: 22, paddingRight: 2 }}>
              <PointsModule bene={bene} theme={theme} beats={beats} setBeats={setBeats} suggestions={suggestions} onWatchExample={onExample} />
            </div>
            <Button variant="primary" onClick={onEnter} disabled={beats.length === 0}>
              Enter the studio →</Button>
          </div>
        </div>
      </WaveBg>
    </div>
  );
}
function StepIco({ kind }) {
  if (kind === "rec") return <span style={{ width: 16, height: 16, borderRadius: "50%", background: "rgb(232,7,7)" }} />;
  if (kind === "play") return <svg width="17" height="17" viewBox="0 0 24 24" fill="currentColor"><path d="M7 4.5v15l13-7.5z"/></svg>;
  return <svg width="18" height="18" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M3 8a5 5 0 1 0 1.5-3.5"/><path d="M3 2.5v3h3"/></svg>;
}

function ReviewStage({ bene, theme, beats, segClips, onSave }) {
  if (!bene) return null;
  // Only beats that were actually recorded make it into the final video —
  // a "Save & Exit" before every beat is done simply drops the empty ones.
  const kept = beats.map((b, i) => ({ b, clip: segClips[i] })).filter((x) => x.clip);
  const keptBeats = kept.map((x) => x.b);
  const keptClips = kept.map((x) => x.clip);
  const total = keptClips.reduce((a, c) => a + (c ? c.dur : 0), 0);
  return (
    <div style={{ position: "relative", width: "100%", height: "100%" }}>
      <WaveBg>
        <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", padding: "24px 40px 26px 56px", gap: 16 }} className="lw-fade">
          {/* compact header + save */}
          <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: 24, flexShrink: 0 }}>
            <div>
              <div style={{ fontFamily: "var(--font-sans)", fontSize: 12.5, fontWeight: 600, letterSpacing: ".07em",
                textTransform: "uppercase", color: "var(--helm-dark-green-2)", marginBottom: 6 }}>
                {theme.order} · for {bene.firstName} · {keptBeats.length} beats · {lwFmt(total)}</div>
              <h1 style={{ fontFamily: "var(--font-serif)", fontSize: 34, lineHeight: "38px", letterSpacing: "-.01em",
                color: "var(--helm-dark-green)", margin: 0 }}>{`Review full ${theme.id === "payout" ? "payout guidance" : "message of hope"} video.`}</h1>
            </div>
            <Button variant="primary" onClick={onSave}>Save this video</Button>
          </div>
          {/* big player — fills the majority of the view */}
          <div style={{ flex: 1, minHeight: 0, position: "relative", borderRadius: 24, overflow: "hidden",
            border: "1px solid var(--helm-dark-green)", boxShadow: "var(--shadow-card-lg)" }}>
            <StitchPlayer segments={keptBeats.map((b, i) => ({ label: b.t, clip: keptClips[i] }))} poster={LW_PO.photo} />
          </div>
          <div style={{ fontFamily: "var(--font-sans)", fontSize: 13.5, color: "var(--helm-dark-green-2)", flexShrink: 0 }}>
            Watch {bene.firstName}'s message through — redo any beat from the guide, or keep it just as it is.</div>
        </div>
      </WaveBg>
    </div>
  );
}

function DoneStage({ bene, theme, saved, onRecordOther, onReset }) {
  if (!bene) return null;
  const both = saved.includes("hope") && saved.includes("payout");
  return (
    <div style={{ position: "relative", width: "100%", height: "100%" }}>
      <WaveBg base={both ? "var(--helm-blue)" : "var(--helm-dark-purple)"}>
        <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", justifyContent: "center",
          padding: "0 72px" }} className="lw-fade">
          <div style={{ maxWidth: 560 }}>
            <div style={{ width: 64, height: 64, borderRadius: "50%", marginBottom: 22, background: "var(--helm-dark-green)",
              display: "flex", alignItems: "center", justifyContent: "center" }}>
              <svg width="30" height="30" viewBox="0 0 24 24" fill="none" stroke="var(--helm-lemon)" strokeWidth="2.6" strokeLinecap="round" strokeLinejoin="round"><path d="M4 12.5l5 5 11-12"/></svg>
            </div>
            <h1 style={{ fontFamily: "var(--font-serif)", fontSize: 46, lineHeight: "50px", letterSpacing: "-.01em",
              color: "var(--helm-dark-green)", margin: "0 0 14px" }}>
              {both ? `${bene.firstName}'s videos are saved.` : "Saved. Beautifully done."}</h1>
            <p className="prose" style={{ color: "var(--helm-dark-green-2)", margin: "0 0 28px", maxWidth: 460 }}>
              {both ? `Both messages for ${bene.firstName} are kept safe, to be shared only when the time comes.`
                : `That was the hard part. When you're ready, the ${theme.id === "hope" ? "payout guidance" : "message of hope"} is just a few short beats.`}</p>
            <div style={{ display: "flex", gap: 12, marginBottom: 30 }}>
              {LW_THEMES.map((t) => {
                const done = saved.includes(t.id);
                return (
                  <div key={t.id} style={{ display: "flex", alignItems: "center", gap: 10, padding: "11px 16px", borderRadius: 13,
                    background: "var(--helm-white)", border: "1px solid var(--helm-dark-green)", opacity: done ? 1 : .55 }}>
                    <span style={{ width: 24, height: 24, borderRadius: "50%", display: "flex", alignItems: "center", justifyContent: "center",
                      background: done ? "var(--helm-chartreuse-2)" : "var(--helm-dark-ecru)" }}>
                      {done ? <svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="#fff" strokeWidth="2.6" strokeLinecap="round" strokeLinejoin="round"><path d="M3.5 8.5l3 3 6-7"/></svg>
                        : <span style={{ fontSize: 11, color: "var(--helm-dark-grey)" }}>·</span>}
                    </span>
                    <span style={{ fontFamily: "var(--font-sans)", fontWeight: 600, fontSize: 14.5, color: "var(--helm-dark-green)" }}>{t.label}</span>
                  </div>
                );
              })}
            </div>
            <div style={{ display: "flex", gap: 12 }}>
              {!both && <Button variant="primary" onClick={onRecordOther}>
                {`Record ${theme.id === "hope" ? "payout guidance" : "the message of hope"} →`}</Button>}
              {both && <Button variant="primary" onClick={onReset}>Record for someone else</Button>}
              <Button variant="secondary" onClick={onReset}>I'm done for now</Button>
            </div>
          </div>
        </div>
      </WaveBg>
    </div>
  );
}

window.StudioApp = StudioApp;
// Reusable stage screens + scripting, consumed by the integrated
// Last Wishes section in the main PO Activation prototype.
Object.assign(window, {
  LWIntroStage: IntroStage,
  LWThemeStage: ThemeStage,
  LWPrepStage: PrepStage,
  LWReviewStage: ReviewStage,
  LWDoneStage: DoneStage,
  lwScriptFor: scriptFor,
  lwCannedReply: cannedReply,
});
