// Early phases: Topic lock, Intake, Blind opening + Synthesis, Steel-man
// Depends on: SCENARIO, Icons, ScribbleCheck, InkLine, AgentBadge, useTypewriter, SimBar

function TopicLockPhase({ seat, godView, onPhase }) {
  const S = window.SCENARIO;
  // Detect mode: live (Claude key + fresh) vs canned
  const [, setStatusTick] = React.useState(0);
  React.useEffect(() => CG.onStatus(() => setStatusTick(t => t + 1)), []);
  // re-render on session state changes (so guest sees host's signature arrive)
  const [, setSessionTick] = React.useState(0);
  React.useEffect(() => {
    if (CG.session && CG.session.onState) {
      return CG.session.onState(() => setSessionTick(t => t + 1));
    }
  }, []);
  const liveMode = (CG.hasKey() && CG.getMode() === 'fresh') || !!(CG.session && CG.session.isGuest && CG.session.isGuest());
  const mp = liveMode && CG.session && CG.session.isMultiplayer && CG.session.isMultiplayer();
  const isHost = !mp || seat === 'A';
  const isGuest = mp && seat === 'B';
  const sharedTopic = (liveMode && CG.session && CG.session.shared && CG.session.shared.topic) || null;

  const [step, setStep] = React.useState(liveMode ? 1 : 1);
  const [mayaDraft, setMayaDraft] = React.useState("");
  const [choice, setChoice] = React.useState(liveMode ? -1 : 1);
  const [customEdit, setCustomEdit] = React.useState("");
  const [editingOption, setEditingOption] = React.useState(false);
  const [jordanAction, setJordanAction] = React.useState(null);
  const [jordanAmendment, setJordanAmendment] = React.useState("");
  const [signedA, setSignedA] = React.useState(false);
  const [signedB, setSignedB] = React.useState(false);

  // sim state (only used when !liveMode)
  const [playing, setPlaying] = React.useState(!liveMode);
  const [resetKey, setResetKey] = React.useState(0);
  const [done, setDone] = React.useState(false);

  // Live refiner state
  const [refinerLoading, setRefinerLoading] = React.useState(false);
  const [refinerError, setRefinerError] = React.useState(null);
  const [liveFramings, setLiveFramings] = React.useState(null); // [{kind, text, tradeoff}]
  const [streamingFramings, setStreamingFramings] = React.useState(null); // in-flight partial

  // Canned fallback framings (from original sim)
  const cannedFramings = [
    { kind: 'still-characterization', text: "Does my partner respect my time?", tradeoff: "still a character judgment, not a workable topic" },
    { kind: 'neutral-specific', text: "How should we handle last-minute schedule changes, and what do we owe each other's time?", tradeoff: "specific and workable · recommended" },
    { kind: 'structural-reframe', text: "What's a workable way to flex plans without it feeling like one person is being deprioritized?", tradeoff: "broader · focuses on shared rules, not people" },
  ];

  const framings = liveMode
    ? (liveFramings || streamingFramings || cannedFramings)
    : cannedFramings;
  const streaming = liveMode && streamingFramings && !liveFramings;
  // Always lay out 3 slots so cards don't jump as framings stream in.
  const slotOrder = ['still-characterization', 'neutral-specific', 'structural-reframe'];
  const displayFramings = streaming
    ? slotOrder.map(kind => framings.find(f => f.kind === kind) || { kind, text: '', tradeoff: '', pending: true })
    : framings;
  const pickedTopic = editingOption && customEdit ? customEdit : (choice >= 0 && displayFramings[choice] ? displayFramings[choice].text : "");

  const callRefiner = async () => {
    setRefinerError(null);
    setRefinerLoading(true);
    setStreamingFramings([]); // empty array signals "streaming started"
    // Advance to step 2 immediately so the user sees skeleton cards populating,
    // rather than sitting on step 1 staring at a button for 8-20s.
    setStep(2);
    try {
      const { framings: f } = await CG.agents.refiner.propose(mayaDraft, {
        party: 'A',
        onStream: (evt) => {
          if (evt.kind === 'framings_partial') {
            setStreamingFramings(evt.framings);
          } else if (evt.kind === 'tool_stop') {
            setStreamingFramings(evt.framings || []);
          }
        },
      });
      setLiveFramings(f);
      setStreamingFramings(null);
      setChoice(1); // default to neutral-specific
    } catch (e) {
      setRefinerError(e.message);
      setStreamingFramings(null);
      setStep(1); // send user back to the draft so they can retry
    } finally {
      setRefinerLoading(false);
    }
  };

  const isA = seat === 'A' || seat === 'god';
  const isB = seat === 'B' || seat === 'god';

  // Live-mode names: read from session, fall back to host/guest. Canned stays maya/jordan.
  const sessionNames = (liveMode && CG.session.getNames) ? CG.session.getNames() : null;
  const nameA = liveMode ? ((sessionNames && sessionNames.A) || 'host') : 'maya';
  const nameB = liveMode ? ((sessionNames && sessionNames.B) || 'guest') : 'jordan';

  const stepDef = [
    { n: 1, label: `${nameA} drafts`, who: 'A' },
    { n: 2, label: "refiner · framings", who: 'A' },
    { n: 3, label: `${nameA} signs`, who: 'A' },
    { n: 4, label: `${nameB} · amends`, who: 'B' },
    { n: 5, label: `${nameB} signs`, who: 'B' },
    { n: 6, label: "locked", who: 'both' },
  ];

  // Typewriter for maya's raw draft during step 1 of sim
  const mayaTypedDraft = useTypewriter(playing ? S.topic.draft : "", 300, playing, resetKey);
  const jordanTypedAmend = useTypewriter(playing ? "…and what counts as 'last-minute'?" : "", 0, playing && step >= 4, resetKey + ':amend');

  // Keep mayaDraft in sync with typed while playing
  React.useEffect(() => {
    if (playing && step === 1 && !liveMode) setMayaDraft(mayaTypedDraft);
  }, [mayaTypedDraft, playing, step, liveMode]);

  React.useEffect(() => {
    if (playing && step >= 4 && jordanAction === 'amend') setJordanAmendment(jordanTypedAmend);
  }, [jordanTypedAmend, playing, step, jordanAction]);

  // --- Live MP: drive step + sync picked topic from shared.topic ---
  // Host runs steps 1→3 locally, then waits for guest signature at step 3.5 (handled in render).
  // Guest waits for host signature, then runs steps 4→5. Both land on step 6 when both signed.
  React.useEffect(() => {
    if (!mp) return;
    if (!sharedTopic) return;
    // Mirror host's proposed topic into local pickedTopic derived state.
    if (sharedTopic.text && choice === -1 && !customEdit) {
      // stash as custom edit so pickedTopic resolves; pick choice 1 as sentinel
      setCustomEdit(sharedTopic.text);
      setEditingOption(true);
      setChoice(1);
    }
    // Mirror amendment if present
    if (sharedTopic.amendment && !jordanAmendment) {
      setJordanAmendment(sharedTopic.amendment);
      if (!jordanAction) setJordanAction('amend');
    }
    // Advance/clamp the step based on shared signatures
    const bothSigned = !!(sharedTopic.signedA && sharedTopic.signedB);
    if (bothSigned) {
      setSignedA(true); setSignedB(true); setDone(true);
      if (step < 6) setStep(6);
      return;
    }
    if (isGuest) {
      // Guest flow: if host hasn't signed, park on step 4 (but render waiting screen).
      // If host signed, open step 4 for response; if guest already signed but not host's turn, step 5.
      if (sharedTopic.signedA && !sharedTopic.signedB) {
        if (step < 4) setStep(4);
      }
    } else if (isHost) {
      // Host flow: if A is signed and B isn't, park on step 3 (render waiting).
      if (sharedTopic.signedA && !sharedTopic.signedB) {
        setSignedA(true);
        if (step < 3) setStep(3);
      }
    }
  }, [mp, sharedTopic, isGuest, isHost]);

  // Presence: broadcast sub-phase activity
  React.useEffect(() => {
    if (!mp) return;
    let activity = null;
    const sA = sharedTopic && sharedTopic.signedA;
    const sB = sharedTopic && sharedTopic.signedB;
    if (sA && sB) activity = 'reviewing · locked';
    else if (isHost) {
      if (!sA) activity = step === 2 ? 'picking framing' : 'drafting topic';
      else activity = `waiting on ${nameB}`;
    } else if (isGuest) {
      if (!sA) activity = `waiting on ${nameA}`;
      else activity = step === 5 ? 'signing' : 'reviewing proposal';
    }
    if (activity) { try { CG.session.setCursor && CG.session.setCursor({ activity }); } catch (e) {} }
  }, [mp, isHost, isGuest, step, sharedTopic, nameA, nameB]);

  // Reset
  const reset = () => {
    setStep(1);
    setMayaDraft("");
    setChoice(1);
    setCustomEdit("");
    setEditingOption(false);
    setJordanAction(null);
    setJordanAmendment("");
    setSignedA(false);
    setSignedB(false);
    setDone(false);
    setResetKey(k => k + 1);
    setPlaying(true);
  };

  // Sim timeline (canned mode only)
  React.useEffect(() => {
    if (!playing || liveMode) return;
    const timers = [];
    const at = (ms, fn) => timers.push(setTimeout(fn, ms));

    // step 1: type draft (handled by typewriter) — advance after type done
    at(2800, () => setStep(2));
    // step 2: highlight choice 1, then choice 2
    at(3400, () => setChoice(0));
    at(4400, () => setChoice(2));
    at(5400, () => setChoice(1));
    at(6400, () => setStep(3));
    // step 3: maya signs
    at(7200, () => setSignedA(true));
    at(7800, () => setStep(4));
    // step 4: jordan arrives, picks amend
    at(8600, () => setJordanAction('amend'));
    // amendment types during this step via typewriter
    at(11200, () => setStep(5));
    // step 5: jordan signs
    at(12000, () => setSignedB(true));
    at(12600, () => { setStep(6); setDone(true); setPlaying(false); });

    return () => timers.forEach(clearTimeout);
  }, [playing, resetKey]);

  return (
    <div style={{ padding: '22px 40px 60px', maxWidth: 920, margin: '0 auto' }}>
      <div className="tiny">phase 01 · pre-room</div>
      <h1 style={{ marginTop: 6, marginBottom: 6 }}>lock the topic before the room opens.</h1>
      <p style={{ maxWidth: 640, color: 'var(--ink-2)', marginBottom: 14 }}>
        a vague opener becomes a fight about everything. a locked, co-signed topic becomes the axis everything else is measured against. <span className="hand" style={{ color: 'var(--blue)' }}>both of you have to sign.</span>
      </p>

      {!liveMode && (
        <SimBar
          playing={playing}
          onPlay={() => setPlaying(true)}
          onPause={() => setPlaying(false)}
          onReset={reset}
          done={done}
          label={
            step === 1 ? "maya is typing her raw draft…" :
            step === 2 ? "refiner proposes three framings…" :
            step === 3 ? "maya signs and sends…" :
            step === 4 ? "jordan amends with 'what counts as last-minute'…" :
            step === 5 ? "jordan signs, room unlocks…" :
            "topic is locked · both signed."
          }
        />
      )}

      {/* step rail */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 0,
        padding: '10px 0', marginBottom: 18,
        borderTop: '1px dashed var(--rule)',
        borderBottom: '1px dashed var(--rule)',
        overflowX: 'auto',
      }}>
        {stepDef.map((s, i) => {
          const active = step === s.n;
          const doneStep = step > s.n;
          let unlocked = (s.who === 'A' && isA) || (s.who === 'B' && isB) || s.who === 'both';
          // In live MP, additionally constrain: host can navigate 1-3 & 6; guest can nav 4-5 & 6.
          if (mp) {
            if (isHost) unlocked = s.n <= 3 || s.n === 6;
            else if (isGuest) unlocked = s.n === 4 || s.n === 5 || s.n === 6;
          }
          return (
            <React.Fragment key={s.n}>
              <div
                onClick={() => { if (unlocked && !playing) setStep(s.n); }}
                style={{
                  display: 'flex', alignItems: 'center', gap: 8,
                  padding: '4px 10px',
                  cursor: unlocked && !playing ? 'pointer' : 'default',
                  opacity: unlocked ? 1 : 0.4,
                  background: active ? 'var(--paper-2)' : 'transparent',
                  border: active ? '1px solid var(--ink)' : '1px solid transparent',
                }}
              >
                <div className={`phase-pip ${doneStep ? 'done' : active ? 'active' : ''}`} style={{ width: 18, height: 18, fontSize: '0.6rem' }}>
                  {s.n}
                </div>
                <div className="mono" style={{ fontSize: '0.7rem', textTransform: 'uppercase', letterSpacing: '0.04em', color: active ? 'var(--ink)' : 'var(--ink-3)' }}>
                  {s.label}
                </div>
              </div>
              {i < stepDef.length - 1 && <div style={{ width: 10, borderTop: '1px dashed var(--rule)' }} />}
            </React.Fragment>
          );
        })}
      </div>

      {/* Live MP · guest: park on a waiting view until host signs, and again if host reopens. */}
      {mp && isGuest && (!sharedTopic || !sharedTopic.signedA) && (
        <div className="paper-card p-4 ink-in" style={{ borderLeft: `3px solid ${S.parties.A.accent}` }}>
          <div className="tiny mb-2" style={{ color: S.parties.A.accent }}>waiting on {nameA}</div>
          <h3 style={{ margin: '6px 0 10px', fontWeight: 500 }}>{nameA} is drafting the topic.</h3>
          <p style={{ color: 'var(--ink-2)', lineHeight: 1.5, marginBottom: 10 }}>
            the host writes a rough version of what this is about, then the refiner proposes three neutral framings.
            you'll see their signed proposal here the moment it lands — then it's your turn to accept, amend, or counter-propose.
          </p>
          <div className="mono" style={{ fontSize: '0.72rem', color: 'var(--ink-3)' }}>
            <ThinkingInk label={`${nameA} is drafting…`} />
          </div>
        </div>
      )}

      {/* Live MP · host: after signing, park on a waiting view until guest responds. */}
      {mp && isHost && sharedTopic && sharedTopic.signedA && !sharedTopic.signedB && step === 3 && (
        <div className="paper-card p-4 ink-in" style={{ borderLeft: `3px solid ${S.parties.A.accent}` }}>
          <div className="tiny mb-2" style={{ color: S.parties.A.accent }}>sent · waiting on {nameB}</div>
          <div style={{ padding: 14, background: 'var(--paper-2)', marginBottom: 14 }}>
            <div className="tiny mb-2">your signed proposal</div>
            <div style={{ fontSize: '1.05rem', fontStyle: 'italic', lineHeight: 1.45 }}>
              <span className="highlight">{sharedTopic.text || pickedTopic}</span>
            </div>
          </div>
          <p style={{ color: 'var(--ink-2)', lineHeight: 1.5, marginBottom: 10 }}>
            {nameB} can accept as-is, amend with one appended clarifier, or counter-propose. you'll see their response here.
          </p>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <span className="stamp stamp-green">signed · {nameA}</span>
            <span className="mono" style={{ fontSize: '0.72rem' }}>
              <ThinkingInk label={`${nameB} is deciding…`} />
            </span>
            <div style={{ flex: 1 }} />
            <button className="btn btn-sm" onClick={() => {
              // withdraw: clear the topic so we can re-propose
              setSignedA(false);
              try { CG.session.setShared({ topic: null }); } catch (e) {}
              setStep(1);
            }}>withdraw · re-propose</button>
          </div>
        </div>
      )}

      {/* Steps 1-3: only render the authoring UI for host (solo = always host). */}
      {(!mp || isHost) && !(mp && isHost && sharedTopic && sharedTopic.signedA && !sharedTopic.signedB && step === 3) && step === 1 && (
        <div className="paper-card p-4 ink-in" style={{ borderLeft: `3px solid ${S.parties.A.accent}` }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}>
            <div className="tiny" style={{ color: S.parties.A.accent }}>{nameA}'s draft · private to coach · not yet sent</div>
            <AgentBadge agent="coachA" />
          </div>
          <p className="micro" style={{ textTransform: 'none', letterSpacing: 0, color: 'var(--ink-3)', lineHeight: 1.4, marginBottom: 10 }}>
            say it however you need to — raw, half-formed, unfair. this will not be shown to {nameB}.
          </p>
          <textarea
            className="paper-input"
            rows={3}
            placeholder="what's this really about, in your own words?"
            value={mayaDraft}
            onChange={e => setMayaDraft(e.target.value)}
            style={{ marginBottom: 10 }}
          />
          {refinerError && (
            <div style={{ padding: '8px 10px', background: 'oklch(93% 0.05 25)', borderLeft: '2px solid var(--red)', marginBottom: 10, fontSize: '0.82rem' }}>
              refiner error · {refinerError}
            </div>
          )}
          <div className="dashed" />
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <div className="hand" style={{ color: 'var(--blue)', fontSize: '1.1rem' }}>
              {refinerLoading ? "calling the refiner…" :
                mayaDraft.length < 10 ? "a few more words and i'll have something to work with…" : "got it. want me to try some framings?"}
            </div>
            <button
              className="btn btn-primary"
              disabled={mayaDraft.trim().length < 10 || refinerLoading}
              onClick={() => { if (liveMode) callRefiner(); else setStep(2); }}
            >
              {refinerLoading ? 'refining…' : 'refine →'} <Icons.Arrow size={10} />
            </button>
          </div>
        </div>
      )}

      {(!mp || isHost) && step === 2 && (
        <div className="ink-in">
          <div className="paper-card p-3 mb-3" style={{ opacity: 0.75 }}>
            <div className="tiny mb-1" style={{ color: S.parties.A.accent }}>{nameA}'s raw draft</div>
            <div style={{ fontStyle: 'italic', color: 'var(--ink-2)' }}>"{mayaDraft}"</div>
          </div>
          <div className="paper-card p-4 mb-3">
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
              <div className="tiny">
                refiner · {streaming ? 'drafting…' : 'pick one, or edit it'}
                {liveMode && liveFramings && <span style={{ color: 'var(--green)', marginLeft: 6 }}>· live from claude</span>}
              </div>
              <AgentBadge agent="mediator" />
            </div>
            {streaming && (
              <div style={{ marginBottom: 12, paddingLeft: 2 }}>
                <ThinkingInk label="finding three ways to name this…" />
              </div>
            )}
            {displayFramings.map((o, i) => {
              const kindLabel =
                o.kind === 'still-characterization' ? { text: '[still a characterization]', color: 'var(--red)' } :
                o.kind === 'structural-reframe' ? { text: '[structural reframe]', color: 'var(--ink-3)' } :
                { text: '[neutral · specific]', color: 'var(--green)' };
              const isPending = !!o.pending || (streaming && !o.text);
              return (
                <div
                  key={o.kind || i}
                  onClick={() => { if (!isPending) { setChoice(i); setEditingOption(false); } }}
                  style={{
                    padding: '14px 16px', marginBottom: 8,
                    cursor: isPending ? 'default' : 'pointer',
                    border: choice === i && !editingOption && !isPending ? '1.5px solid var(--ink)' : '1px dashed var(--rule)',
                    background: choice === i && !editingOption && !isPending ? 'var(--paper-2)' : 'transparent',
                    display: 'flex', gap: 12, alignItems: 'flex-start',
                    transition: 'background 0.2s, border-color 0.2s, opacity 0.3s',
                    opacity: isPending ? 0.7 : 1,
                  }}
                >
                  <div className="mono" style={{ fontSize: '0.7rem', color: 'var(--ink-3)', marginTop: 3 }}>0{i+1}</div>
                  <div style={{ flex: 1, lineHeight: 1.45 }}>
                    <div style={{ fontStyle: o.kind === 'still-characterization' ? 'italic' : 'normal', color: o.kind === 'still-characterization' ? 'var(--ink-3)' : 'var(--ink)' }}>
                      <span className="mono" style={{ fontSize: '0.65rem', marginRight: 8, color: kindLabel.color }}>{kindLabel.text}</span>
                      {isPending
                        ? <span style={{ display: 'inline-block', verticalAlign: 'middle', minWidth: 220 }}><SkelLines lines={1} widths={['95%']} /></span>
                        : <StreamingText text={o.text} />}
                    </div>
                    {isPending ? (
                      <div style={{ marginTop: 6, maxWidth: 260 }}>
                        <SkelLines lines={1} widths={['60%']} />
                      </div>
                    ) : o.tradeoff && (
                      <div className="micro" style={{ textTransform: 'none', letterSpacing: 0, marginTop: 4, color: 'var(--ink-3)', lineHeight: 1.35 }}>
                        — <StreamingText text={o.tradeoff} />
                      </div>
                    )}
                  </div>
                  {choice === i && !editingOption && !isPending && <ScribbleCheck />}
                </div>
              );
            })}
          </div>
          <div style={{ display: 'flex', justifyContent: 'space-between', gap: 8 }}>
            {liveMode && !streaming && (
              <button className="btn btn-sm" disabled={refinerLoading} onClick={callRefiner} title="re-run refiner on the same draft">
                {refinerLoading ? 'refining…' : '↻ try again'}
              </button>
            )}
            <div style={{ flex: 1 }} />
            <button className="btn btn-primary" disabled={choice < 0 || streaming} onClick={() => setStep(3)}>
              use this framing <Icons.Arrow size={10} />
            </button>
          </div>
        </div>
      )}

      {(!mp || isHost) && step === 3 && !(mp && sharedTopic && sharedTopic.signedA && !sharedTopic.signedB) && (
        <div className="paper-card p-4 ink-in" style={{ borderLeft: `3px solid ${S.parties.A.accent}` }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}>
            <div className="tiny" style={{ color: S.parties.A.accent }}>{nameA} · sign &amp; send to {nameB}</div>
            <AgentBadge agent="mediator" />
          </div>
          <div style={{ padding: 14, background: 'var(--paper-2)', marginBottom: 14 }}>
            <div className="tiny mb-2">topic (proposed)</div>
            <div style={{ fontSize: '1.1rem', fontStyle: 'italic' }}>
              <span className="highlight">{pickedTopic}</span>
            </div>
          </div>
          {!signedA ? (
            <button className="btn btn-primary" onClick={() => {
              setSignedA(true);
              if (liveMode) {
                try {
                  CG.session.setShared({
                    topic: {
                      text: pickedTopic,
                      amendment: '',
                      signedA: true,
                      signedB: false,
                      signedAt: new Date().toISOString(),
                    }
                  });
                } catch (e) { console.warn('[phase01] could not persist topic', e); }
              }
              // In live MP, host must NOT advance to step 4 — that's the guest's screen.
              // Host stays on step 3 and renders a "waiting on {nameB}" overlay.
              if (!mp) setTimeout(() => setStep(4), 400);
            }}>
              <Icons.Stamp size={12} /> sign as {nameA} &amp; send
            </button>
          ) : (
            <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
              <span className="stamp stamp-green">signed · {nameA} · now</span>
              <span className="mono" style={{ color: 'var(--ink-3)' }}>
                {mp ? `waiting on ${nameB}…` : `sent to ${nameB}…`}
              </span>
            </div>
          )}
        </div>
      )}

      {(!mp || isGuest) && step === 4 && (
        <div className="ink-in">
          <div className="paper-card p-3 mb-3" style={{ background: 'var(--paper-2)' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
              <div className="tiny">{nameA} proposed · signed</div>
              <div className="mono" style={{ color: 'var(--ink-3)' }}>received just now</div>
            </div>
            <div style={{ fontSize: '1.05rem', fontStyle: 'italic', lineHeight: 1.45 }}>
              <span className="highlight">{pickedTopic}</span>
            </div>
          </div>

          <div className="paper-card p-4" style={{ borderLeft: `3px solid ${S.parties.B.accent}` }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}>
              <div className="tiny" style={{ color: S.parties.B.accent }}>{nameB} · your response</div>
              <AgentBadge agent="coachB" />
            </div>
            <div style={{ display: 'flex', gap: 6, marginBottom: 14 }}>
              {[
                { v: 'accept', l: 'accept as-is' },
                { v: 'amend', l: 'accept + amend' },
                { v: 'counter', l: 'counter-propose' },
              ].map(o => (
                <div
                  key={o.v}
                  className={`seat-chip ${jordanAction === o.v ? 'active' : ''}`}
                  onClick={() => setJordanAction(o.v)}
                >{o.l}</div>
              ))}
            </div>
            {jordanAction === 'amend' && (
              <>
                <div className="tiny mb-2">your amendment · appended to the topic</div>
                <input
                  className="paper-input"
                  placeholder="…and what counts as 'last-minute'?"
                  value={jordanAmendment}
                  onChange={e => setJordanAmendment(e.target.value)}
                />
              </>
            )}
            <div className="dashed" />
            <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
              <button className="btn btn-primary" disabled={!jordanAction} onClick={() => setStep(5)}>
                continue <Icons.Arrow size={10} />
              </button>
            </div>
          </div>
        </div>
      )}

      {(!mp || isGuest) && step === 5 && (
        <div className="paper-card p-4 ink-in" style={{ borderLeft: `3px solid ${S.parties.B.accent}` }}>
          <div className="tiny mb-2">the topic · as it will lock</div>
          <div style={{ padding: 14, background: 'var(--paper-2)', marginBottom: 14, fontSize: '1.1rem', fontStyle: 'italic', lineHeight: 1.5 }}>
            <span className="highlight">{pickedTopic}</span>
            {jordanAction === 'amend' && jordanAmendment.trim() && (
              <> <span style={{ background: 'oklch(90% 0.1 240)', padding: '0 4px', color: 'var(--ink-2)' }}>
                {jordanAmendment.startsWith('…') ? jordanAmendment : '…' + jordanAmendment}
              </span></>
            )}
          </div>
          {!signedB ? (
            <button className="btn btn-primary" onClick={() => {
              setSignedB(true);
              // In live mode, persist the locked topic to shared state
              if (liveMode) {
                try {
                  CG.session.setShared({
                    topic: {
                      text: pickedTopic,
                      amendment: jordanAction === 'amend' ? jordanAmendment : '',
                      signedA: true,
                      signedB: true,
                      signedAt: new Date().toISOString(),
                    }
                  });
                } catch (e) { console.warn('[phase01] could not persist topic', e); }
              }
              setTimeout(() => { setStep(6); setDone(true); setPlaying(false); }, 400);
            }}>
              <Icons.Stamp size={12} /> sign as {nameB} · open the room
            </button>
          ) : (
            <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
              <span className="stamp stamp-green">signed · {nameB} · now</span>
              <span className="mono" style={{ color: 'var(--ink-3)' }}>locking…</span>
            </div>
          )}
        </div>
      )}

      {step === 6 && (
        <div className="ink-in paper-card p-4" style={{ position: 'relative', background: 'var(--paper-2)' }}>
          <div className="tape" style={{ top: -8, left: 36, transform: 'rotate(-3deg)' }} />
          <div className="tape" style={{ top: -8, right: 50, transform: 'rotate(2deg)' }} />
          <div style={{ textAlign: 'center' }}>
            <div className="tiny" style={{ color: 'var(--green)' }}>topic locked</div>
            <div style={{ fontSize: '1.25rem', fontStyle: 'italic', lineHeight: 1.4, margin: '10px auto', maxWidth: 640 }}>
              <span className="highlight">{pickedTopic}</span>
              {jordanAction === 'amend' && jordanAmendment.trim() && (
                <> <span style={{ background: 'oklch(90% 0.1 240)', padding: '0 4px', color: 'var(--ink-2)' }}>
                  {jordanAmendment.startsWith('…') ? jordanAmendment : '…' + jordanAmendment}
                </span></>
              )}
            </div>
            <div style={{ display: 'flex', gap: 10, justifyContent: 'center', marginTop: 16 }}>
              <span className="stamp stamp-green">signed · {nameA}</span>
              <span className="stamp stamp-green">signed · {nameB}</span>
            </div>
            <div className="hand" style={{ color: 'var(--blue)', fontSize: '1.2rem', marginTop: 14 }}>
              now — optionally — each of you tells claude privately what a good outcome would look like.
            </div>
            <button className="btn btn-primary mt-3" onClick={() => onPhase && onPhase('intake')}>
              continue → phase 02 · private intake <Icons.Arrow size={10} />
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

function IntakePhase({ seat, godView, onPhase }) {
  const S = window.SCENARIO;
  const party = seat === 'B' ? S.parties.B : S.parties.A;
  const otherParty = seat === 'B' ? S.parties.A : S.parties.B;
  const intakeScript = seat === 'B' ? S.intakeB : S.intakeA;

  const [playing, setPlaying] = React.useState(true);
  const [resetKey, setResetKey] = React.useState(0);
  const [visibleCount, setVisibleCount] = React.useState(0);
  const [dossierRevealed, setDossierRevealed] = React.useState(false);
  const [goalRevealed, setGoalRevealed] = React.useState(false);
  const [done, setDone] = React.useState(false);
  const [partnerProgress, setPartnerProgress] = React.useState(0); // 0..1
  const [partnerDone, setPartnerDone] = React.useState(false);
  const [youSubmitted, setYouSubmitted] = React.useState(false);

  const reset = () => {
    setVisibleCount(0);
    setDossierRevealed(false);
    setGoalRevealed(false);
    setDone(false);
    setPartnerProgress(0);
    setPartnerDone(false);
    setYouSubmitted(false);
    setResetKey(k => k + 1);
    setPlaying(true);
  };

  React.useEffect(() => {
    if (!playing) return;
    const timers = [];
    const at = (ms, fn) => timers.push(setTimeout(fn, ms));

    // your script
    intakeScript.forEach((m, i) => {
      at(500 + i * 1400, () => setVisibleCount(i + 1));
    });
    const endYours = 500 + intakeScript.length * 1400;
    at(endYours + 400, () => setGoalRevealed(true));
    at(endYours + 1200, () => setDossierRevealed(true));
    at(endYours + 1800, () => { setDone(true); });

    // partner progresses in parallel but a little behind
    const otherLen = (seat === 'B' ? S.intakeA : S.intakeB).length;
    for (let i = 1; i <= otherLen; i++) {
      at(900 + i * 1500, () => setPartnerProgress(i / otherLen));
    }
    at(900 + otherLen * 1500 + 1400, () => setPartnerDone(true));

    at(endYours + 2400, () => setPlaying(false));
    return () => timers.forEach(clearTimeout);
  }, [playing, resetKey, seat]);

  const lastTyped = useTypewriter(
    playing && visibleCount > 0 ? intakeScript[visibleCount - 1].text : "",
    0, playing, resetKey + ':' + visibleCount
  );

  const bothReady = done && partnerDone && youSubmitted;

  return (
    <div style={{ padding: '22px 40px 60px', maxWidth: 1100, margin: '0 auto' }}>
      <div className="tiny">phase 02 · pretext · private + parallel</div>
      <h1 style={{ marginTop: 6, marginBottom: 6 }}>two rooms. one conversation with yourself, not them.</h1>
      <p style={{ maxWidth: 640, color: 'var(--ink-2)', marginBottom: 14 }}>
        nothing posted. your coach asks open questions and quietly reads for defensiveness, catastrophizing, willingness to self-reflect. <span className="hand" style={{ color: 'var(--blue)' }}>the dossier calibrates how they handle you next. it goes nowhere else.</span>
      </p>

      <SimBar
        playing={playing}
        onPlay={() => setPlaying(true)}
        onPause={() => setPlaying(false)}
        onReset={reset}
        done={done}
        label={
          !done ?
            (visibleCount < intakeScript.length
              ? `coach is interviewing ${party.name.toLowerCase()}… (${visibleCount}/${intakeScript.length})`
              : goalRevealed ? "surfacing the good-outcome anchor…" : "wrapping up…")
            : (!youSubmitted ? "your intake is ready · submit when you're done"
              : !partnerDone ? `waiting on ${otherParty.name.toLowerCase()}…`
              : "both intakes in · ready for blind openings")
        }
      />

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 280px', gap: 18 }}>
        <div className="paper-card p-4" style={{ position: 'relative' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 12 }}>
            <div className="tiny">intake · {party.name.toLowerCase()} · your room</div>
            <AgentBadge agent={seat === 'B' ? 'coachB' : 'coachA'} />
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10, minHeight: 280 }}>
            {intakeScript.slice(0, visibleCount).map((m, i) => {
              const isLast = i === visibleCount - 1;
              const typing = playing && isLast && lastTyped !== m.text;
              return (
                <div key={i + '-' + resetKey} className="fade-up" style={{
                  padding: '10px 12px',
                  background: m.role === 'user' ? 'var(--paper-2)' : 'transparent',
                  borderLeft: m.role === 'coach' ? '2px solid var(--ink-3)' : '0',
                  fontStyle: m.role === 'user' ? 'italic' : 'normal',
                }}>
                  <div className="micro mb-1">{m.role === 'coach' ? 'coach' : party.name.toLowerCase()}</div>
                  <div style={{ lineHeight: 1.45 }}>
                    {typing ? lastTyped : m.text}
                    {typing && <span style={{ opacity: 0.5 }}>▍</span>}
                  </div>
                </div>
              );
            })}
          </div>

          <div className="dashed" style={{ margin: '14px 0 10px' }} />
          <div className="tiny mb-2">your turn · still private</div>
          <textarea
            className="paper-input"
            rows={2}
            placeholder="keep going, or stop here and submit — both are fine…"
          />
          <div style={{ display: 'flex', gap: 8, marginTop: 10 }}>
            <button className="btn btn-sm btn-primary" disabled={youSubmitted}>send to coach</button>
            <button
              className="btn btn-sm"
              disabled={youSubmitted || !done}
              onClick={() => setYouSubmitted(true)}
              style={{ opacity: (!done || youSubmitted) ? 0.5 : 1 }}
            >
              {youSubmitted ? "✓ submitted" : "i'm done · submit intake"}
            </button>
          </div>

          {goalRevealed && (
            <div className="fade-up">
              <div className="dashed" style={{ margin: '16px 0 10px' }} />
              <div className="tiny mb-2">what a good outcome looks like · surfaced from the conversation</div>
              <div style={{ fontFamily: 'var(--font-hand)', fontSize: '1.2rem', lineHeight: 1.2, color: party.accent }}>
                "{party.goal}"
              </div>
            </div>
          )}
        </div>

        {/* partner status panel */}
        <div>
          <div className="paper-card p-3" style={{
            borderLeft: `3px solid ${otherParty.accent}`,
            position: 'sticky', top: 10,
          }}>
            <div className="tiny mb-2" style={{ color: otherParty.accent }}>
              {otherParty.name.toLowerCase()}'s room · firewalled
            </div>
            <div style={{
              minHeight: 110,
              background: 'repeating-linear-gradient(135deg, transparent 0 8px, var(--paper-3) 8px 9px)',
              padding: 12,
              marginBottom: 10,
              position: 'relative',
            }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 8 }}>
                <Icons.Lock size={11} />
                <span className="micro">contents sealed</span>
              </div>
              <div className="hand" style={{ fontSize: '1rem', color: 'var(--ink-3)', lineHeight: 1.3 }}>
                you can't read what {otherParty.name.toLowerCase()} is saying. you only see the progress.
              </div>
            </div>

            <div className="micro mb-1">progress</div>
            <div style={{ height: 6, background: 'var(--paper-3)', marginBottom: 6, position: 'relative' }}>
              <div style={{
                position: 'absolute', left: 0, top: 0, bottom: 0,
                width: `${partnerProgress * 100}%`,
                background: otherParty.accent,
                transition: 'width 0.8s ease-out',
              }} />
            </div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
              {!partnerDone ? (
                <>
                  <span className="blink-dot" style={{
                    width: 7, height: 7, borderRadius: '50%',
                    background: otherParty.accent, display: 'inline-block',
                    animation: 'blink 1.4s infinite',
                  }} />
                  <span className="mono" style={{ fontSize: '0.7rem', color: 'var(--ink-3)' }}>
                    in progress · {Math.round(partnerProgress * 100)}%
                  </span>
                </>
              ) : (
                <span className="stamp stamp-green" style={{ fontSize: '0.6rem' }}>intake complete</span>
              )}
            </div>

            <div className="dashed" style={{ margin: '12px 0' }} />
            <div className="micro mb-2">coach sees</div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                <span className="mono" style={{ fontSize: '0.7rem', color: 'var(--ink-3)' }}>you</span>
                <span className="mono" style={{ fontSize: '0.7rem' }}>{done ? "✓ done" : "in progress"}</span>
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                <span className="mono" style={{ fontSize: '0.7rem', color: 'var(--ink-3)' }}>{otherParty.name.toLowerCase()}</span>
                <span className="mono" style={{ fontSize: '0.7rem' }}>{partnerDone ? "✓ done" : "in progress"}</span>
              </div>
            </div>
          </div>

          {/* continue gate */}
          <div className="paper-card p-3 mt-3" style={{
            background: bothReady ? 'var(--green-soft)' : 'var(--paper-2)',
            borderLeft: `2px solid ${bothReady ? 'var(--green)' : 'var(--rule)'}`,
            transition: 'background 0.4s, border-color 0.4s',
          }}>
            <div className="tiny mb-2" style={{ color: bothReady ? 'var(--green)' : 'var(--ink-3)' }}>
              gate to phase 03
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginBottom: 10 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                {youSubmitted ? <ScribbleCheck /> : <div style={{ width: 14, height: 14, border: '1.5px dashed var(--ink-3)', borderRadius: '50%' }} />}
                <span className="mono" style={{ fontSize: '0.72rem' }}>you submitted</span>
              </div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                {partnerDone ? <ScribbleCheck /> : <div style={{ width: 14, height: 14, border: '1.5px dashed var(--ink-3)', borderRadius: '50%' }} />}
                <span className="mono" style={{ fontSize: '0.72rem' }}>{otherParty.name.toLowerCase()} submitted</span>
              </div>
            </div>
            <button
              className="btn btn-sm btn-primary"
              disabled={!bothReady}
              onClick={() => onPhase && onPhase('opening')}
              style={{ width: '100%', opacity: bothReady ? 1 : 0.5 }}
            >
              {bothReady ? "open phase 03 → blind openings" : "waiting for both to finish…"}
            </button>
          </div>
        </div>
      </div>

      {dossierRevealed && (
        <div className="mt-6 fade-up">
          <div className="tiny mb-2">dossier · coach's private read · never shown publicly</div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 10 }}>
            {[
              { label: 'defensiveness', val: seat === 'B' ? 0.45 : 0.55 },
              { label: 'self-reflection', val: seat === 'B' ? 0.7 : 0.75 },
              { label: 'catastrophizing', val: seat === 'B' ? 0.25 : 0.6 },
              { label: 'specificity', val: seat === 'B' ? 0.5 : 0.85 },
            ].map((d, i) => (
              <div key={i} className="paper-card p-3" style={{ boxShadow: 'none', border: '1px solid var(--rule)' }}>
                <div className="micro mb-2">{d.label}</div>
                <div style={{ height: 6, background: 'var(--paper-3)', position: 'relative' }}>
                  <div style={{ position: 'absolute', left: 0, top: 0, bottom: 0, width: `${d.val * 100}%`, background: 'var(--ink)', transition: 'width 0.8s ease-out' }} />
                </div>
                <div className="hand mt-1" style={{ fontSize: '1rem', color: 'var(--ink-3)' }}>
                  {d.val < 0.3 ? 'low' : d.val < 0.6 ? 'moderate' : d.val < 0.8 ? 'notable' : 'high'}
                </div>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

function OpeningPhase({ seat, godView, onPhase }) {
  const S = window.SCENARIO;
  const otherSeat = seat === 'B' ? 'A' : 'B';
  const you = S.parties[seat === 'god' ? 'A' : seat];
  const other = seat === 'god' ? S.parties.B : S.parties[otherSeat];

  // stages:
  // 1 drafting (both typing, neither sees other)
  // 2 both submitted, mediator working
  // 3 synthesis delivered, each approves own
  // 4 openings posted publicly
  const [stage, setStage] = React.useState(1);
  const [playing, setPlaying] = React.useState(true);
  const [resetKey, setResetKey] = React.useState(0);
  const [done, setDone] = React.useState(false);

  const [mayaDraft, setMayaDraft] = React.useState("");
  const [jordanDraft, setJordanDraft] = React.useState("");
  const [mayaSubmitted, setMayaSubmitted] = React.useState(false);
  const [jordanSubmitted, setJordanSubmitted] = React.useState(false);
  const [mayaProgress, setMayaProgress] = React.useState(0);
  const [jordanProgress, setJordanProgress] = React.useState(0);

  const [mayaSynthesis, setMayaSynthesis] = React.useState("");
  const [jordanSynthesis, setJordanSynthesis] = React.useState("");
  const [mayaSynthesisReady, setMayaSynthesisReady] = React.useState(false);
  const [jordanSynthesisReady, setJordanSynthesisReady] = React.useState(false);
  const [mayaApproved, setMayaApproved] = React.useState(false);
  const [jordanApproved, setJordanApproved] = React.useState(false);

  // Typewriters for drafts
  const mayaTyped = useTypewriter(stage >= 1 && playing ? S.openings.A.raw : "", 400, playing, resetKey + ':ma');
  const jordanTyped = useTypewriter(stage >= 1 && playing ? S.openings.B.raw : "", 1100, playing, resetKey + ':jo');
  const mayaSynthTyped = useTypewriter(mayaSynthesisReady && playing ? S.openings.A.synthesis : "", 200, playing && mayaSynthesisReady, resetKey + ':mas');
  const jordanSynthTyped = useTypewriter(jordanSynthesisReady && playing ? S.openings.B.synthesis : "", 400, playing && jordanSynthesisReady, resetKey + ':jos');

  React.useEffect(() => {
    if (playing) {
      setMayaDraft(mayaTyped);
      setMayaProgress(mayaTyped.length / Math.max(S.openings.A.raw.length, 1));
    }
  }, [mayaTyped, playing]);
  React.useEffect(() => {
    if (playing) {
      setJordanDraft(jordanTyped);
      setJordanProgress(jordanTyped.length / Math.max(S.openings.B.raw.length, 1));
    }
  }, [jordanTyped, playing]);
  React.useEffect(() => {
    if (playing && mayaSynthesisReady) setMayaSynthesis(mayaSynthTyped);
  }, [mayaSynthTyped, playing, mayaSynthesisReady]);
  React.useEffect(() => {
    if (playing && jordanSynthesisReady) setJordanSynthesis(jordanSynthTyped);
  }, [jordanSynthTyped, playing, jordanSynthesisReady]);

  const reset = () => {
    setStage(1);
    setMayaDraft(""); setJordanDraft("");
    setMayaSubmitted(false); setJordanSubmitted(false);
    setMayaProgress(0); setJordanProgress(0);
    setMayaSynthesis(""); setJordanSynthesis("");
    setMayaSynthesisReady(false); setJordanSynthesisReady(false);
    setMayaApproved(false); setJordanApproved(false);
    setDone(false);
    setResetKey(k => k + 1);
    setPlaying(true);
  };

  React.useEffect(() => {
    if (!playing) return;
    const timers = [];
    const at = (ms, fn) => timers.push(setTimeout(fn, ms));

    // Drafting duration ≈ longest typewriter
    const mayaEnd = 400 + Math.min(Math.max(S.openings.A.raw.length * 18, 400), 2600);
    const jordanEnd = 1100 + Math.min(Math.max(S.openings.B.raw.length * 18, 400), 2600);
    const draftsEnd = Math.max(mayaEnd, jordanEnd);

    at(mayaEnd + 300, () => setMayaSubmitted(true));
    at(jordanEnd + 600, () => setJordanSubmitted(true));
    at(draftsEnd + 1000, () => setStage(2));

    // mediator works — stagger synthesis arrivals
    at(draftsEnd + 2200, () => { setStage(3); setMayaSynthesisReady(true); });
    at(draftsEnd + 3200, () => setJordanSynthesisReady(true));

    // auto-approvals (simulated)
    const mayaSynthEnd = draftsEnd + 2200 + Math.min(Math.max(S.openings.A.synthesis.length * 18, 400), 2600);
    const jordanSynthEnd = draftsEnd + 3200 + Math.min(Math.max(S.openings.B.synthesis.length * 18, 400), 2600);
    at(mayaSynthEnd + 700, () => setMayaApproved(true));
    at(jordanSynthEnd + 700, () => setJordanApproved(true));

    const finalAt = Math.max(mayaSynthEnd, jordanSynthEnd) + 1500;
    at(finalAt, () => { setStage(4); setDone(true); setPlaying(false); });

    return () => timers.forEach(clearTimeout);
  }, [playing, resetKey]);

  const bothSubmitted = mayaSubmitted && jordanSubmitted;
  const bothApproved = mayaApproved && jordanApproved;

  const youSide = seat === 'B' ? 'B' : 'A';
  const canSeeMaya = seat === 'A' || seat === 'god';
  const canSeeJordan = seat === 'B' || seat === 'god';

  return (
    <div style={{ padding: '22px 40px 60px', maxWidth: 1200, margin: '0 auto' }}>
      <div className="tiny">phase 03 · blind opening + neutral synthesis</div>
      <h1 style={{ marginTop: 6, marginBottom: 6 }}>submit blind. the mediator speaks you back in neutral.</h1>
      <p style={{ maxWidth: 720, color: 'var(--ink-2)', marginBottom: 14 }}>
        neither of you sees the other's draft until both submit. then the mediator rewrites each opening in non-inflammatory language. <span className="hand" style={{ color: 'var(--blue)' }}>you sign off on your own synthesis before it posts.</span>
      </p>

      <SimBar
        playing={playing}
        onPlay={() => setPlaying(true)}
        onPause={() => setPlaying(false)}
        onReset={reset}
        done={done}
        label={
          stage === 1 ? (
            !mayaSubmitted && !jordanSubmitted ? "both drafting in parallel · firewalled from each other…"
            : !bothSubmitted ? `waiting on ${!mayaSubmitted ? 'maya' : 'jordan'}…`
            : "both submitted · handing to mediator"
          ) : stage === 2 ? "mediator is rewriting each opening in non-inflammatory language…"
            : stage === 3 ? (
              !bothApproved ? "syntheses delivered · each party approves their own…"
              : "both approved · posting to the room"
            ) : "openings posted · ready for steel-man gate"
        }
      />

      {/* Top status strip: both submission states */}
      <div style={{
        display: 'grid', gridTemplateColumns: '1fr auto 1fr',
        gap: 14, marginBottom: 16, alignItems: 'center',
        padding: '10px 16px',
        border: '1px dashed var(--rule)',
      }}>
        <SideStatus
          party={S.parties.A}
          progress={mayaProgress}
          submitted={mayaSubmitted}
          synthesisReady={mayaSynthesisReady}
          approved={mayaApproved}
          stage={stage}
          align="left"
        />
        <div style={{ textAlign: 'center' }}>
          {stage === 1 && <div className="mono" style={{ fontSize: '0.7rem', color: 'var(--ink-3)' }}>FIREWALL</div>}
          {stage === 1 && <div style={{ width: 1, height: 30, margin: '4px auto', background: 'repeating-linear-gradient(to bottom, var(--ink-3) 0 3px, transparent 3px 6px)' }} />}
          {stage === 2 && (
            <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4 }}>
              <AgentBadge agent="mediator" />
              <div className="mono" style={{ fontSize: '0.65rem' }}>working…</div>
            </div>
          )}
          {stage === 3 && <div className="hand" style={{ color: 'var(--blue)', fontSize: '0.95rem' }}>↓ synthesis</div>}
          {stage === 4 && <span className="stamp stamp-green" style={{ fontSize: '0.6rem' }}>posted</span>}
        </div>
        <SideStatus
          party={S.parties.B}
          progress={jordanProgress}
          submitted={jordanSubmitted}
          synthesisReady={jordanSynthesisReady}
          approved={jordanApproved}
          stage={stage}
          align="right"
        />
      </div>

      {/* Main two columns */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18 }}>
        <OpeningColumn
          party={S.parties.A}
          canSee={canSeeMaya}
          draft={mayaDraft}
          submitted={mayaSubmitted}
          stage={stage}
          synthesis={mayaSynthesis}
          synthesisReady={mayaSynthesisReady}
          approved={mayaApproved}
          onApprove={() => setMayaApproved(true)}
          onSubmit={() => setMayaSubmitted(true)}
          canDrive={seat === 'A' || seat === 'god'}
          godView={godView}
          godNote={S.godNotes.synthesis_A}
        />
        <OpeningColumn
          party={S.parties.B}
          canSee={canSeeJordan}
          draft={jordanDraft}
          submitted={jordanSubmitted}
          stage={stage}
          synthesis={jordanSynthesis}
          synthesisReady={jordanSynthesisReady}
          approved={jordanApproved}
          onApprove={() => setJordanApproved(true)}
          onSubmit={() => setJordanSubmitted(true)}
          canDrive={seat === 'B' || seat === 'god'}
          godView={godView}
          godNote={S.godNotes.synthesis_B}
        />
      </div>

      {stage === 4 && (
        <div className="mt-6 fade-up" style={{ textAlign: 'center' }}>
          <div className="hand" style={{ color: 'var(--green)', fontSize: '1.3rem', marginBottom: 10 }}>
            both openings are now in the room. each party knows exactly what the other said — and each signed off on how it was said.
          </div>
          <button className="btn btn-primary" onClick={() => onPhase && onPhase('steelman')}>
            continue → phase 04 · steel-man gate <Icons.Arrow size={10} />
          </button>
        </div>
      )}
    </div>
  );
}

function SideStatus({ party, progress, submitted, synthesisReady, approved, stage, align }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: align === 'right' ? 'flex-end' : 'flex-start', gap: 4 }}>
      <div className="micro" style={{ color: party.accent }}>{party.name.toLowerCase()}</div>
      <div style={{ width: '100%', maxWidth: 220, height: 5, background: 'var(--paper-3)', position: 'relative' }}>
        <div style={{
          position: 'absolute', left: 0, top: 0, bottom: 0,
          width: `${(approved ? 1 : synthesisReady ? 0.85 : submitted ? 0.6 : progress * 0.5) * 100}%`,
          background: party.accent,
          transition: 'width 0.6s ease-out',
        }} />
      </div>
      <div className="mono" style={{ fontSize: '0.68rem', color: 'var(--ink-3)' }}>
        {approved ? "✓ approved synthesis" :
         synthesisReady ? "reading synthesis…" :
         submitted ? "submitted · waiting" :
         stage === 1 ? `drafting · ${Math.round(progress * 100)}%` : "…"}
      </div>
    </div>
  );
}

function OpeningColumn({ party, canSee, draft, submitted, stage, synthesis, synthesisReady, approved, onApprove, onSubmit, canDrive, godView, godNote }) {
  return (
    <div>
      <div className="tiny mb-2" style={{ color: party.accent }}>
        {party.name.toLowerCase()}'s opening · {submitted ? "submitted" : "private draft"}
      </div>
      <div className="paper-card p-3 mb-3" style={{ borderLeft: `3px solid ${party.accent}`, minHeight: 150 }}>
        {canSee ? (
          <>
            <div style={{ fontStyle: 'italic', color: 'var(--ink-2)', lineHeight: 1.5, minHeight: 100, whiteSpace: 'pre-wrap' }}>
              {draft || <span style={{ color: 'var(--ink-4)' }}>(empty)</span>}
              {!submitted && draft && <span style={{ opacity: 0.5 }}>▍</span>}
            </div>
            {submitted && <div className="mt-2"><span className="stamp stamp-green" style={{ fontSize: '0.6rem' }}>submitted</span></div>}
          </>
        ) : (
          <div style={{
            background: 'repeating-linear-gradient(135deg, transparent 0 8px, var(--paper-3) 8px 9px)',
            padding: 16, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8, minHeight: 100, justifyContent: 'center',
          }}>
            <Icons.Lock size={13} />
            <div className="mono" style={{ fontSize: '0.7rem', color: 'var(--ink-3)' }}>
              {submitted ? "submitted · sealed until both in" : "drafting · not visible to you"}
            </div>
          </div>
        )}
      </div>

      {/* Synthesis */}
      {(stage >= 3) && (
        <div className="fade-up">
          <div className="tiny mb-2" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <span>mediator's synthesis</span>
            {approved && <span className="stamp stamp-green" style={{ fontSize: '0.58rem' }}>{party.name.toLowerCase()} approved</span>}
          </div>
          <div className="paper-card p-4" style={{ background: 'var(--paper-2)', position: 'relative', minHeight: 110 }}>
            {synthesisReady ? (
              <>
                <div style={{ lineHeight: 1.55 }}>{synthesis}</div>
                <div style={{ position: 'absolute', top: 10, right: 12 }}><AgentBadge agent="mediator" /></div>
                {!approved && synthesis === (party.accent === '#b94b4b' /*dummy*/ ? '' : synthesis) && (
                  <div className="dashed mt-3" />
                )}
                {!approved && (
                  <div style={{ display: 'flex', gap: 8, marginTop: 12 }}>
                    <button className="btn btn-sm btn-primary" onClick={onApprove} disabled={!canDrive} style={{ opacity: canDrive ? 1 : 0.5 }}>
                      <Icons.Stamp size={10} /> approve &amp; post
                    </button>
                    <button className="btn btn-sm" disabled={!canDrive} style={{ opacity: canDrive ? 1 : 0.5 }}>
                      request edit
                    </button>
                  </div>
                )}
              </>
            ) : (
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, color: 'var(--ink-3)' }}>
                <div className="mono" style={{ fontSize: '0.7rem' }}>mediator rewriting…</div>
                <div style={{ display: 'flex', gap: 3 }}>
                  <span className="blink-dot" style={{ width: 4, height: 4, background: 'var(--ink-3)', borderRadius: '50%', animation: 'blink 1.4s infinite' }} />
                  <span className="blink-dot" style={{ width: 4, height: 4, background: 'var(--ink-3)', borderRadius: '50%', animation: 'blink 1.4s infinite 0.2s' }} />
                  <span className="blink-dot" style={{ width: 4, height: 4, background: 'var(--ink-3)', borderRadius: '50%', animation: 'blink 1.4s infinite 0.4s' }} />
                </div>
              </div>
            )}
          </div>
          {godView && synthesisReady && (
            <div className="god-panel mt-2" style={{ fontSize: '0.85rem' }}>
              <b>synthesis reasoning · </b>{godNote}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

function SteelmanPhase({ seat, godView }) {
  const S = window.SCENARIO;
  const [attempt, setAttempt] = React.useState(
    "When you change plans last-minute, you're not doing it to hurt me — you're doing it because real things have come up and you'd rather be honest than show up badly. What's been making that worse is my response afterward: a long quiet period that reads to you as a verdict on your character, not space to cool off. You need to be able to flex without me treating every adjustment as evidence you don't value us."
  );
  const [submitted, setSubmitted] = React.useState(false);
  const [rating, setRating] = React.useState(0);
  const [note, setNote] = React.useState("");
  const isA = seat === 'A' || seat === 'god';
  const isB = seat === 'B' || seat === 'god';

  return (
    <div style={{ padding: '28px 40px', maxWidth: 880, margin: '0 auto' }}>
      <div className="tiny">phase 04 · steel-man gate</div>
      <h1 style={{ marginTop: 6, marginBottom: 6 }}>restate the other's position. they grade you. nobody moves until they say yes.</h1>
      <p style={{ maxWidth: 640, color: 'var(--ink-2)', marginBottom: 24 }}>
        most fights are two people arguing with their imagined version of the other. <span className="hand" style={{ color: 'var(--blue)' }}>this mechanic alone resolves a lot of them.</span>
      </p>

      <div className="paper-card p-4 mb-4">
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
          <div className="tiny">maya restates jordan · attempt {submitted ? 2 : 1}</div>
          <AgentBadge agent="coachA" />
        </div>
        <textarea className="paper-input" rows={6} value={attempt} onChange={e => setAttempt(e.target.value)} disabled={!isA || submitted} style={{ fontStyle: 'italic', color: 'var(--ink-2)' }} />
        {!submitted ? (
          <button className="btn btn-sm btn-primary mt-2" disabled={!isA} onClick={() => setSubmitted(true)} style={{ opacity: !isA ? 0.4 : 1 }}>send to jordan for grading</button>
        ) : (
          <div className="mono mt-2" style={{ color: 'var(--ink-3)' }}>sent · awaiting jordan's grade</div>
        )}
      </div>

      {submitted && (
        <div className="paper-card p-4 mb-4">
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
            <div className="tiny">jordan grades · private</div>
            <AgentBadge agent="coachB" />
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
            {[1, 2, 3, 4, 5].map(n => (
              <div key={n} className={`rating-dot ${n <= rating ? 'filled' : ''}`} onClick={() => { if (isB) setRating(n); }} style={{ cursor: isB ? 'pointer' : 'not-allowed' }} />
            ))}
            <span className="mono" style={{ color: 'var(--ink-3)', marginLeft: 8 }}>
              {rating === 0 ? 'rate 1–5' : `${rating} of 5 · ${rating >= 4 ? 'unlocks the room' : 'try again'}`}
            </span>
          </div>
          <textarea className="paper-input" rows={2} value={note} onChange={e => setNote(e.target.value)} disabled={!isB} placeholder="what did maya miss?" />
          {rating >= 4 && (
            <div style={{ marginTop: 14, padding: 12, background: 'var(--green-soft)', borderLeft: '2px solid var(--green)' }}>
              <div className="tiny mb-1" style={{ color: 'var(--green)' }}>gate unlocked · now reverse</div>
              <div className="hand" style={{ fontSize: '1.2rem', color: 'var(--ink-2)' }}>jordan's turn to restate maya.</div>
            </div>
          )}
        </div>
      )}

      {godView && (
        <div className="god-panel mt-3">
          <b>why this gate is before the room · </b>without steel-manning, the rebuttal round devolves. once each party has confirmed the other <i>got them</i>, disagreement stops feeling like misunderstanding and becomes workable.
        </div>
      )}
    </div>
  );
}

Object.assign(window, { TopicLockPhase, IntakePhase, OpeningPhase, SteelmanPhase });
