// Phase 07 — Apology on-ramp. Private editable draft, live coach flags, real Claude feedback.
// Quiet, narrow, ritual.
console.log('[phases-apology] loading');

function ApologyPhase({ seat, godView, onPhase }) {
  const S = window.SCENARIO;
  const isJordan = seat === 'B';
  const isMaya = seat === 'A';
  const isGod = seat === 'god';

  // Receiver view: maya doesn't see jordan's draft. We show a waiting panel.
  if (isMaya) return <ApologyReceiverView onPhase={onPhase} />;

  // Jordan (seat B) or God view — show the drafting runway.
  return <ApologyDraftingView seat={seat} godView={godView} onPhase={onPhase} showBothSeats={isGod} />;
}

// ————————————————————————————————————————————————————————————————————————————
// Receiver (Maya) — private, "something is happening, you'll see it if/when it's sent"
// ————————————————————————————————————————————————————————————————————————————

function ApologyReceiverView({ onPhase }) {
  const S = window.SCENARIO;
  const [dots, setDots] = React.useState(1);
  React.useEffect(() => {
    const t = setInterval(() => setDots(d => (d % 3) + 1), 700);
    return () => clearInterval(t);
  }, []);

  return (
    <div style={{ padding: '28px 40px 60px', maxWidth: 780, margin: '0 auto' }}>
      <div className="tiny">phase 07 · apology on-ramp · your view · maya</div>
      <h1 style={{ marginTop: 6, marginBottom: 6, fontSize: '1.55rem' }}>the runway is private. this is what that looks like from your side.</h1>
      <p style={{ color: 'var(--ink-2)', maxWidth: 680, marginBottom: 28, fontSize: '0.95rem' }}>
        jordan's coach noticed a softening signal and offered to help them draft something. the content is firewalled. you'll see it only if jordan chooses to send it into the room — on their time, not yours.
      </p>

      <div className="paper-card" style={{ padding: '28px 32px', marginBottom: 22, background: 'var(--paper-2)', borderLeft: '3px solid var(--blue)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 14 }}>
          <Icons.Lock size={14} />
          <div className="mono" style={{ fontSize: '0.72rem', textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--ink-3)' }}>firewalled · content private</div>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
          <span style={{ position: 'relative', display: 'inline-block', width: 10, height: 10 }}>
            <span style={{ position: 'absolute', inset: 0, borderRadius: '50%', background: S.parties.B.accent, opacity: 0.35, animation: 'pulse 1.8s ease-in-out infinite' }} />
            <span style={{ position: 'absolute', inset: 2, borderRadius: '50%', background: S.parties.B.accent }} />
          </span>
          <div className="hand" style={{ fontSize: '1.3rem', color: 'var(--ink)', lineHeight: 1.3 }}>
            jordan is drafting something{'.'.repeat(dots)}
          </div>
        </div>
        <div className="dashed" style={{ margin: '16px 0' }} />
        <div className="micro" style={{ textTransform: 'none', letterSpacing: 0, color: 'var(--ink-3)', lineHeight: 1.5 }}>
          you will not see <em>what</em> they're writing, <em>how long</em> they take, or <em>how many</em> drafts. this runway belongs to jordan. if and when they hit send, the final version lands in the center and becomes public record.
        </div>
      </div>

      <div style={{ display: 'flex', gap: 18, marginBottom: 26 }}>
        <div className="paper-card" style={{ padding: '14px 16px', flex: 1, background: 'var(--paper)' }}>
          <div className="tiny mb-2">your options while you wait</div>
          <ul style={{ listStyle: 'none', padding: 0, margin: 0 }}>
            <li style={{ marginBottom: 8, paddingLeft: 18, position: 'relative', fontSize: '0.9rem', lineHeight: 1.45 }}>
              <span style={{ position: 'absolute', left: 0 }}>·</span>
              keep reviewing the map / established truths on your side
            </li>
            <li style={{ marginBottom: 8, paddingLeft: 18, position: 'relative', fontSize: '0.9rem', lineHeight: 1.45 }}>
              <span style={{ position: 'absolute', left: 0 }}>·</span>
              talk to your coach privately — it's ok not to wait
            </li>
            <li style={{ paddingLeft: 18, position: 'relative', fontSize: '0.9rem', lineHeight: 1.45 }}>
              <span style={{ position: 'absolute', left: 0 }}>·</span>
              park the room entirely — come back tomorrow
            </li>
          </ul>
        </div>
        <div className="paper-card" style={{ padding: '14px 16px', flex: 1, background: 'var(--paper)' }}>
          <div className="tiny mb-2" style={{ color: S.parties.A.accent }}>your coach · private</div>
          <div style={{ fontSize: '0.9rem', lineHeight: 1.5, fontStyle: 'italic', color: 'var(--ink-2)' }}>
            "you're not owed this. if it lands, you can take any piece of it into your acknowledgment column — or none of it. the apology is theirs to write; what you do with it is yours."
          </div>
        </div>
      </div>

      <div className="dashed" style={{ margin: '10px 0 20px' }} />

      <div style={{ textAlign: 'center' }}>
        <div className="hand" style={{ color: 'var(--ink-3)', fontSize: '1.1rem', marginBottom: 14, maxWidth: 520, margin: '0 auto 14px', lineHeight: 1.4 }}>
          want to see what's happening on jordan's side right now? switch to their seat — or see both at once in god view.
        </div>
        <div style={{ display: 'inline-flex', gap: 8 }}>
          <button className="btn btn-sm" onClick={() => { localStorage.setItem('cg_seat', 'B'); location.reload(); }}>switch to jordan's seat →</button>
          <button className="btn btn-sm btn-ghost" onClick={() => { localStorage.setItem('cg_seat', 'god'); location.reload(); }}>god view</button>
        </div>
      </div>

      <style>{`
        @keyframes pulse {
          0%, 100% { transform: scale(1); opacity: 0.35; }
          50% { transform: scale(2.2); opacity: 0; }
        }
      `}</style>
    </div>
  );
}

// ————————————————————————————————————————————————————————————————————————————
// Drafter (Jordan) — the real editor
// ————————————————————————————————————————————————————————————————————————————

// Stages: 'offer' → user accepts coach offer to draft → 'editing' → user iterates → 'confirm' → modal → 'sending' → animation → 'landed'
function ApologyDraftingView({ seat, godView, onPhase, showBothSeats }) {
  const S = window.SCENARIO;
  const [stage, setStage] = React.useState(() => localStorage.getItem('cg_apology_stage') || 'offer');
  const [draft, setDraft] = React.useState(() => localStorage.getItem('cg_apology_draft') || S.apologyDraftB.rough);
  const [flags, setFlags] = React.useState([]);
  const [flagsLoading, setFlagsLoading] = React.useState(false);
  const [deepFeedback, setDeepFeedback] = React.useState(null);
  const [deepLoading, setDeepLoading] = React.useState(false);
  const [showConfirm, setShowConfirm] = React.useState(false);
  const [landedAt, setLandedAt] = React.useState(() => localStorage.getItem('cg_apology_landed') || null);

  React.useEffect(() => { localStorage.setItem('cg_apology_stage', stage); }, [stage]);
  React.useEffect(() => { localStorage.setItem('cg_apology_draft', draft); }, [draft]);
  React.useEffect(() => { if (landedAt) localStorage.setItem('cg_apology_landed', landedAt); }, [landedAt]);

  // Live coach flags — debounced, calls claude with a tight prompt
  React.useEffect(() => {
    if (stage !== 'editing' || !draft.trim()) { setFlags([]); return; }
    const t = setTimeout(async () => {
      setFlagsLoading(true);
      const res = await callCoachFlags(draft);
      setFlags(res);
      setFlagsLoading(false);
    }, 900);
    return () => clearTimeout(t);
  }, [draft, stage]);

  const resetAll = () => {
    localStorage.removeItem('cg_apology_stage');
    localStorage.removeItem('cg_apology_draft');
    localStorage.removeItem('cg_apology_landed');
    setStage('offer'); setDraft(S.apologyDraftB.rough); setFlags([]); setDeepFeedback(null); setLandedAt(null);
  };

  const onAskDeep = async () => {
    setDeepLoading(true);
    setDeepFeedback(null);
    const res = await callDeepFeedback(draft);
    setDeepFeedback(res);
    setDeepLoading(false);
  };

  const onSend = () => setShowConfirm(true);
  const onConfirmSend = () => {
    setShowConfirm(false);
    setStage('sending');
    setTimeout(() => {
      const ts = new Date();
      const stamp = `apr 15 · ${ts.getHours() % 12 || 12}:${String(ts.getMinutes()).padStart(2,'0')}${ts.getHours() >= 12 ? 'pm' : 'am'}`;
      setLandedAt(stamp);
      setStage('landed');
    }, 1800);
  };

  return (
    <div style={{ padding: '28px 40px 80px', maxWidth: showBothSeats ? 1180 : 880, margin: '0 auto' }}>
      <ApologyHeader seat={seat} showBothSeats={showBothSeats} onReset={resetAll} hasProgress={stage !== 'offer' || landedAt} />

      {showBothSeats ? (
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 320px', gap: 28, alignItems: 'flex-start' }}>
          <ApologyDrafter
            stage={stage} setStage={setStage}
            draft={draft} setDraft={setDraft}
            flags={flags} flagsLoading={flagsLoading}
            deepFeedback={deepFeedback} deepLoading={deepLoading} onAskDeep={onAskDeep}
            showConfirm={showConfirm} setShowConfirm={setShowConfirm} onSend={onSend} onConfirmSend={onConfirmSend}
            landedAt={landedAt}
            godView={godView}
            onPhase={onPhase}
          />
          <ApologyMayaSide landedAt={landedAt} draft={draft} />
        </div>
      ) : (
        <ApologyDrafter
          stage={stage} setStage={setStage}
          draft={draft} setDraft={setDraft}
          flags={flags} flagsLoading={flagsLoading}
          deepFeedback={deepFeedback} deepLoading={deepLoading} onAskDeep={onAskDeep}
          showConfirm={showConfirm} setShowConfirm={setShowConfirm} onSend={onSend} onConfirmSend={onConfirmSend}
          landedAt={landedAt}
          godView={godView}
          onPhase={onPhase}
        />
      )}
    </div>
  );
}

// —— header ——
function ApologyHeader({ seat, showBothSeats, onReset, hasProgress }) {
  return (
    <div style={{ marginBottom: 26 }}>
      <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 20 }}>
        <div>
          <div className="tiny">phase 07 · apology on-ramp · private · {showBothSeats ? 'god view · both seats' : 'your view · jordan'}</div>
          <h1 style={{ marginTop: 6, marginBottom: 6, fontSize: '1.55rem', fontWeight: 500 }}>no apologies with a gun to the head. the runway is yours.</h1>
          <p style={{ color: 'var(--ink-2)', maxWidth: 620, fontSize: '0.92rem', lineHeight: 1.55 }}>
            write freely. your coach reads along and flags the patterns that turn apologies into their opposite. nothing lands until you hit send — and then it's permanent.
          </p>
        </div>
        {hasProgress && (
          <button className="btn btn-sm btn-ghost" onClick={onReset} title="reset the runway — back to the rough draft">
            reset
          </button>
        )}
      </div>
    </div>
  );
}

// —— drafter (main column) ——
function ApologyDrafter(props) {
  const { stage } = props;
  if (stage === 'offer') return <ApologyOfferCard {...props} />;
  if (stage === 'landed') return <ApologyLandedCard {...props} />;
  // 'editing' | 'sending'
  return <ApologyEditor {...props} />;
}

// —— stage: offer (before user accepts to draft) ——
function ApologyOfferCard({ setStage, setDraft }) {
  const S = window.SCENARIO;
  return (
    <div>
      <div className="tiny mb-2">softening signal · your coach noticed</div>
      <div className="msg msg-b" style={{ background: 'var(--paper)', marginBottom: 16, maxWidth: 640 }}>
        <div className="micro mb-1" style={{ color: S.parties.B.accent }}>you · chat c9 · earlier</div>
        <div style={{ lineHeight: 1.5 }}>
          "i can sit with the pattern read. <span className="highlight">the recent three — yeah. they've been closer together than usual.</span>"
        </div>
      </div>

      <div className="paper-card" style={{ padding: '20px 24px', marginBottom: 20, maxWidth: 640, background: 'var(--paper-2)', borderLeft: `3px solid ${S.parties.B.accent}`, position: 'relative' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
          <AgentBadge agent="coachB" />
          <div className="tiny" style={{ color: 'var(--ink-3)' }}>private · only you see this</div>
        </div>
        <div className="hand" style={{ fontSize: '1.2rem', lineHeight: 1.45, color: 'var(--ink)', marginBottom: 12 }}>
          you just did something a lot of people can't do. you named a pattern you're inside of.
        </div>
        <div style={{ fontSize: '0.92rem', lineHeight: 1.55, color: 'var(--ink-2)', marginBottom: 16 }}>
          if you want, i can help you draft something specific — not a performance, not a sandwich. just the thing that's true. you'd keep full edit control. maya sees nothing until you decide.
        </div>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          <button className="btn btn-sm btn-primary" onClick={() => setStage('editing')}>yes, help me draft →</button>
          <button className="btn btn-sm" onClick={() => { setDraft(''); setStage('editing'); }}>i'll start from scratch</button>
          <button className="btn btn-sm btn-ghost">not now · check on me later</button>
        </div>
      </div>

      <div className="micro" style={{ textTransform: 'none', letterSpacing: 0, color: 'var(--ink-3)', maxWidth: 560, lineHeight: 1.5 }}>
        the "check on me later" option is real. if you're not ready, the coach backs off and re-surfaces the offer at a softer moment — never in front of maya, never with a timer.
      </div>
    </div>
  );
}

// —— stage: editing ——
function ApologyEditor({
  draft, setDraft, flags, flagsLoading,
  deepFeedback, deepLoading, onAskDeep,
  showConfirm, setShowConfirm, onSend, onConfirmSend,
  stage, godView
}) {
  const S = window.SCENARIO;
  const taRef = React.useRef(null);
  const [lastSaved, setLastSaved] = React.useState(null);
  React.useEffect(() => {
    const t = setTimeout(() => setLastSaved(new Date()), 500);
    return () => clearTimeout(t);
  }, [draft]);

  const wordCount = draft.trim().split(/\s+/).filter(Boolean).length;

  const checklist = buildChecklist(draft, flags);
  const passRate = checklist.filter(c => c.pass).length;
  const readyToSend = passRate >= 3 && flags.filter(f => f.severity === 'red').length === 0 && wordCount >= 8;

  const sending = stage === 'sending';

  return (
    <div style={{ position: 'relative' }}>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: 18, maxWidth: 720 }}>
        <div>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 10, marginBottom: 8 }}>
            <div className="tiny">your draft · private</div>
            <div style={{ flex: 1 }} />
            <div className="micro" style={{ textTransform: 'none', letterSpacing: 0, color: 'var(--ink-4)' }}>
              {wordCount} {wordCount === 1 ? 'word' : 'words'} · saved
            </div>
          </div>
          <div style={{ position: 'relative', transition: 'transform 1.6s cubic-bezier(.7,0,.3,1), opacity 1.6s', transform: sending ? 'translateX(480px) translateY(-40px) scale(0.85)' : 'none', opacity: sending ? 0 : 1 }}>
            <textarea
              ref={taRef}
              value={draft}
              onChange={e => setDraft(e.target.value)}
              placeholder="write what's true. specifics land; generalities don't."
              style={{
                width: '100%', minHeight: 170, padding: '18px 20px',
                fontFamily: 'var(--font-prose)', fontSize: '1.05rem', lineHeight: 1.6,
                border: `1px solid var(--rule)`, borderLeft: `3px solid ${flags.some(f=>f.severity==='red') ? 'var(--red)' : flags.some(f=>f.severity==='yellow') ? 'oklch(65% 0.14 85)' : S.parties.B.accent}`,
                background: 'var(--paper)', color: 'var(--ink)', resize: 'vertical',
                outline: 'none', boxShadow: 'inset 0 1px 0 rgba(0,0,0,0.02)',
              }}
            />
            <AnnotatedText text={draft} flags={flags} />
          </div>

          {/* Live flags strip */}
          <div style={{ marginTop: 10, minHeight: 28 }}>
            {flagsLoading && draft.trim() ? (
              <div className="micro" style={{ color: 'var(--ink-4)', textTransform: 'none', letterSpacing: 0 }}>coach is reading<span style={{ opacity: 0.5 }}>…</span></div>
            ) : flags.length === 0 && draft.trim() ? (
              <div className="micro" style={{ color: 'var(--green)', textTransform: 'none', letterSpacing: 0 }}>✓ no pattern flags right now</div>
            ) : (
              <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                {flags.map((f, i) => <FlagChip key={i} flag={f} />)}
              </div>
            )}
          </div>

          <div style={{ display: 'flex', gap: 8, marginTop: 14, flexWrap: 'wrap', alignItems: 'center' }}>
            <button
              className={`btn btn-sm ${readyToSend ? 'btn-primary' : ''}`}
              disabled={!readyToSend || sending}
              onClick={onSend}
              title={!readyToSend ? 'coach: not ready yet — check the signals below' : 'commit · becomes permanent public record'}
              style={{ opacity: readyToSend ? 1 : 0.5 }}
            >
              send to center →
            </button>
            <button className="btn btn-sm" onClick={onAskDeep} disabled={deepLoading || !draft.trim()}>
              {deepLoading ? 'coach is reading deeply…' : 'ask coach for deeper feedback'}
            </button>
            <button className="btn btn-sm btn-ghost" onClick={() => setDraft(S.apologyDraftB.refined)} title="see what a finished version might look like — you can edit it">
              show me an example
            </button>
            <div style={{ flex: 1 }} />
            <button className="btn btn-sm btn-ghost">hold · not ready</button>
          </div>
        </div>

        {/* Checklist — the ritual scorecard */}
        <div className="paper-card" style={{ padding: '16px 18px', background: 'var(--paper-2)', maxWidth: 720 }}>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 10, marginBottom: 10 }}>
            <div className="tiny">the ritual · what makes an apology actually land</div>
            <div style={{ flex: 1 }} />
            <div className="micro" style={{ textTransform: 'none', letterSpacing: 0, color: 'var(--ink-3)' }}>{passRate} of {checklist.length}</div>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px 18px' }}>
            {checklist.map((c, i) => (
              <div key={i} style={{ display: 'flex', gap: 8, alignItems: 'flex-start', fontSize: '0.88rem', lineHeight: 1.4, opacity: c.pass ? 1 : 0.55 }}>
                <span style={{ flexShrink: 0, width: 16, display: 'inline-flex', justifyContent: 'center', marginTop: 2 }}>
                  {c.pass ? <ScribbleCheck /> : <span style={{ color: 'var(--ink-4)', fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>○</span>}
                </span>
                <div>
                  <div style={{ color: c.pass ? 'var(--ink)' : 'var(--ink-3)' }}>{c.label}</div>
                  {c.hint && !c.pass && <div className="micro" style={{ textTransform: 'none', letterSpacing: 0, color: 'var(--ink-4)', marginTop: 2 }}>{c.hint}</div>}
                </div>
              </div>
            ))}
          </div>
        </div>

        {/* Deep feedback panel (only when asked) */}
        {(deepFeedback || deepLoading) && (
          <div className="paper-card" style={{ padding: '18px 22px', background: 'var(--paper)', borderLeft: `3px solid ${S.parties.B.accent}`, maxWidth: 720 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
              <AgentBadge agent="coachB" />
              <div className="tiny">deep feedback · private</div>
            </div>
            {deepLoading && <div className="micro" style={{ textTransform: 'none', letterSpacing: 0, color: 'var(--ink-3)' }}>reading carefully…</div>}
            {deepFeedback && <DeepFeedbackPanel feedback={deepFeedback} />}
          </div>
        )}

        {godView && (
          <div className="god-panel" style={{ maxWidth: 720 }}>
            <b>apology reasoning · </b>{S.godNotes.apology_b}
          </div>
        )}
      </div>

      {showConfirm && <SendConfirmModal draft={draft} onCancel={() => setShowConfirm(false)} onConfirm={onConfirmSend} />}
    </div>
  );
}

// —— Inline annotation overlay: subtle wavy underlines for flagged spans ——
function AnnotatedText({ text, flags }) {
  // Non-interactive overlay hint — subtle marks below the textarea that point at flagged phrases.
  const spans = flags.flatMap(f => (f.spans || []).map(s => ({ ...s, severity: f.severity, label: f.label }))).filter(s => s && s.text);
  if (!spans.length) return null;
  return (
    <div style={{ marginTop: 8, fontSize: '0.78rem', color: 'var(--ink-3)', fontFamily: 'var(--font-mono)', display: 'flex', gap: 14, flexWrap: 'wrap' }}>
      <span style={{ color: 'var(--ink-4)' }}>flagged phrases:</span>
      {spans.slice(0, 5).map((s, i) => (
        <span key={i} style={{
          textDecoration: 'underline',
          textDecorationStyle: 'wavy',
          textDecorationColor: s.severity === 'red' ? 'var(--red)' : 'oklch(65% 0.14 85)',
          textUnderlineOffset: '2px',
          color: 'var(--ink-2)',
        }}>"{s.text}"</span>
      ))}
    </div>
  );
}

// —— flag chip (in the strip below the textarea) ——
function FlagChip({ flag }) {
  const color = flag.severity === 'red' ? 'var(--red)' : flag.severity === 'yellow' ? 'oklch(55% 0.14 70)' : 'var(--blue)';
  const bg = flag.severity === 'red' ? 'var(--red-soft)' : flag.severity === 'yellow' ? 'oklch(95% 0.06 85)' : 'var(--paper-2)';
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 6,
      padding: '4px 10px', background: bg,
      border: `1px solid ${color}`,
      fontSize: '0.75rem', fontFamily: 'var(--font-mono)', color,
      borderRadius: 2,
    }} title={flag.note || ''}>
      <span style={{ width: 6, height: 6, borderRadius: '50%', background: color }} />
      {flag.label}
    </span>
  );
}

// —— checklist logic (fast, local, deterministic) ——
function buildChecklist(draft, flags) {
  const t = draft.trim();
  const low = t.toLowerCase();
  const hasDate = /\b(apr|may|jun|jul|aug|sep|oct|nov|dec|jan|feb|mar)(il|ch|uary|ust|e|y|tember|ober|ember)?\s*\d/i.test(t) || /\bon\s+\w+day\b/i.test(low);
  const hasBehavior = /\b(i (was|did|told|bailed|dismissed|cancelled|canceled|ignored|avoided|lied|hid)|that was (cowardly|careless|unfair|dishonest|wrong)|i (dismissed|avoided))\b/i.test(low);
  const hasCommit = /\b(i('ll| will) |i commit|going forward|next time|starting )/i.test(low);
  const nonApology = /\bsorry (you|that you|if)\b/i.test(low) || /\bdidn'?t (mean|intend)\b/i.test(low);
  const hasBut = /\bbut\b|\bhowever\b/i.test(low);
  const noMinimize = !/\b(overreact|dramatic|blowing .* out|not a big deal|whatever)\b/i.test(low);
  const hasFirstPerson = /\bi\b/i.test(low);
  return [
    { label: 'specific — names a time or event', pass: hasDate, hint: 'e.g. "on Apr 9" or "Saturday night"' },
    { label: 'names the behavior in your own words', pass: hasBehavior, hint: 'what did you do, not how they felt' },
    { label: 'commits to something different going forward', pass: hasCommit, hint: '"I\'ll..." or "going forward..."' },
    { label: 'no "sorry you felt" / "didn\'t mean to"', pass: !nonApology, hint: 'shifts ownership back to the other person' },
    { label: 'no sandwich (no "but", no "however")', pass: !hasBut, hint: 'the "but" undoes everything in front of it' },
    { label: "doesn't minimize the other side", pass: noMinimize, hint: 'words like "overreact" / "dramatic" unwind the apology' },
  ];
}

// —— send confirmation modal ——
function SendConfirmModal({ draft, onCancel, onConfirm }) {
  const S = window.SCENARIO;
  return (
    <div style={{
      position: 'fixed', inset: 0, background: 'rgba(20,15,10,0.35)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 100,
      backdropFilter: 'blur(2px)',
    }} onClick={onCancel}>
      <div className="paper-card" onClick={e => e.stopPropagation()} style={{
        maxWidth: 520, padding: '28px 32px', background: 'var(--paper)',
        borderLeft: `4px solid ${S.parties.B.accent}`,
      }}>
        <div className="tiny mb-2">final check · before it lands</div>
        <h2 style={{ fontFamily: 'var(--font-prose)', fontSize: '1.3rem', marginTop: 0, marginBottom: 12, fontWeight: 500, lineHeight: 1.35 }}>
          this becomes public record. maya sees it. it stays in the room's archive with your name on it.
        </h2>
        <div style={{ padding: '14px 16px', background: 'var(--paper-2)', borderLeft: `2px solid ${S.parties.B.accent}`, marginBottom: 18, fontSize: '0.92rem', lineHeight: 1.55, fontStyle: 'italic' }}>
          "{draft}"
        </div>
        <ul style={{ listStyle: 'none', padding: 0, margin: '0 0 18px', fontSize: '0.88rem', lineHeight: 1.55, color: 'var(--ink-2)' }}>
          <li style={{ paddingLeft: 18, position: 'relative', marginBottom: 4 }}><span style={{ position: 'absolute', left: 0 }}>·</span> maya can accept, partially acknowledge, or let it sit — her call</li>
          <li style={{ paddingLeft: 18, position: 'relative', marginBottom: 4 }}><span style={{ position: 'absolute', left: 0 }}>·</span> a redaction request is possible, but creates its own record</li>
          <li style={{ paddingLeft: 18, position: 'relative' }}><span style={{ position: 'absolute', left: 0 }}>·</span> you can't un-send; you <em>can</em> follow up</li>
        </ul>
        <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end' }}>
          <button className="btn btn-sm btn-ghost" onClick={onCancel}>not yet</button>
          <button className="btn btn-sm btn-primary" onClick={onConfirm}>send to center · permanent →</button>
        </div>
      </div>
    </div>
  );
}

// —— stage: landed ——
function ApologyLandedCard({ draft, landedAt, onPhase }) {
  const S = window.SCENARIO;
  return (
    <div style={{ maxWidth: 720 }}>
      <div className="tiny mb-2" style={{ color: 'var(--green)' }}>sent · landed in center · on the record</div>
      <div className="paper-card fade-up" style={{
        padding: '24px 28px', position: 'relative',
        borderLeft: `3px solid ${S.parties.B.accent}`,
        marginBottom: 20,
      }}>
        <div style={{ position: 'absolute', top: -14, right: 20 }}>
          <span className="stamp stamp-blue">on record · {landedAt || 'apr 15 · 10:14pm'}</span>
        </div>
        <div className="micro mb-2" style={{ color: S.parties.B.accent }}>jordan · apology</div>
        <div style={{ fontFamily: 'var(--font-prose)', fontSize: '1.05rem', lineHeight: 1.65, color: 'var(--ink)' }}>
          {draft}
        </div>
        <div className="dashed" />
        <div className="tiny mb-2">what this triggers</div>
        <ul style={{ listStyle: 'none', padding: 0, margin: 0 }}>
          <li style={{ display: 'flex', gap: 10, alignItems: 'flex-start', marginBottom: 6, fontSize: '0.9rem', lineHeight: 1.5 }}>
            <ScribbleCheck />
            <div>proposed as a new established truth · awaiting maya's response</div>
          </li>
          <li style={{ display: 'flex', gap: 10, alignItems: 'flex-start', marginBottom: 6, fontSize: '0.9rem', lineHeight: 1.5 }}>
            <ScribbleCheck />
            <div>maya can drag sub-pieces into her acknowledgment column · or none at all</div>
          </li>
          <li style={{ display: 'flex', gap: 10, alignItems: 'flex-start', fontSize: '0.9rem', lineHeight: 1.5 }}>
            <ScribbleCheck />
            <div>if an action-commitment is in the text, it lands as a draft action item for phase 08</div>
          </li>
        </ul>
      </div>

      <div className="hand fade-up" style={{ color: 'var(--ink-3)', fontSize: '1.1rem', textAlign: 'center', marginBottom: 18, lineHeight: 1.4, maxWidth: 520, margin: '0 auto 18px' }}>
        the weight shifts. not away — distributed.
      </div>

      <div style={{ textAlign: 'center' }}>
        <button className="btn btn-primary" onClick={() => onPhase && onPhase('artifact')}>
          continue → phase 08 · artifact <Icons.Arrow size={10} />
        </button>
      </div>
    </div>
  );
}

// —— Maya-side view (only shown in god view, alongside drafter) ——
function ApologyMayaSide({ landedAt, draft }) {
  const S = window.SCENARIO;
  const [dots, setDots] = React.useState(1);
  React.useEffect(() => {
    if (landedAt) return;
    const t = setInterval(() => setDots(d => (d % 3) + 1), 700);
    return () => clearInterval(t);
  }, [landedAt]);

  return (
    <div style={{ position: 'sticky', top: 14 }}>
      <div className="tiny mb-2" style={{ color: S.parties.A.accent }}>meanwhile · maya's seat</div>
      <div className="paper-card" style={{ padding: '16px 18px', background: 'var(--paper-2)', borderLeft: `3px solid ${S.parties.A.accent}` }}>
        {!landedAt ? (
          <>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
              <Icons.Lock size={12} />
              <div className="micro" style={{ color: 'var(--ink-3)' }}>firewalled · content hidden</div>
            </div>
            <div className="hand" style={{ fontSize: '1rem', lineHeight: 1.4, color: 'var(--ink-2)' }}>
              jordan is drafting something{'.'.repeat(dots)}
            </div>
            <div className="dashed" />
            <div className="micro" style={{ textTransform: 'none', letterSpacing: 0, color: 'var(--ink-4)', lineHeight: 1.5 }}>
              maya sees nothing of the content, the edits, or the time taken. only the status.
            </div>
          </>
        ) : (
          <>
            <div className="micro mb-2" style={{ color: 'var(--green)' }}>✓ landed at {landedAt}</div>
            <div style={{ fontSize: '0.85rem', lineHeight: 1.55, color: 'var(--ink-2)' }}>
              maya sees the apology in the center. she can acknowledge, let it sit, or request a redaction. pressure to reciprocate is explicitly off.
            </div>
            <div className="dashed" />
            <div className="micro" style={{ textTransform: 'none', letterSpacing: 0, color: 'var(--ink-3)', lineHeight: 1.5 }}>
              <em>no</em> "read at" timestamp on her side. <em>no</em> reply countdown.
            </div>
          </>
        )}
      </div>
    </div>
  );
}

// —— Deep feedback panel ——
function DeepFeedbackPanel({ feedback }) {
  if (feedback.error) {
    return <div className="micro" style={{ textTransform: 'none', letterSpacing: 0, color: 'var(--red)', lineHeight: 1.5 }}>{feedback.error}</div>;
  }
  return (
    <div>
      {feedback.summary && <div style={{ fontSize: '0.95rem', lineHeight: 1.6, color: 'var(--ink)', marginBottom: 14, fontStyle: 'italic' }}>{feedback.summary}</div>}
      {feedback.strengths?.length > 0 && (
        <div style={{ marginBottom: 12 }}>
          <div className="micro mb-2" style={{ color: 'var(--green)' }}>what's working</div>
          {feedback.strengths.map((s, i) => (
            <div key={i} style={{ display: 'flex', gap: 8, marginBottom: 4, fontSize: '0.88rem', lineHeight: 1.5 }}>
              <span style={{ color: 'var(--green)', flexShrink: 0 }}>✓</span>
              <span>{s}</span>
            </div>
          ))}
        </div>
      )}
      {feedback.suggestions?.length > 0 && (
        <div>
          <div className="micro mb-2" style={{ color: 'oklch(55% 0.14 70)' }}>things to consider</div>
          {feedback.suggestions.map((s, i) => (
            <div key={i} style={{ display: 'flex', gap: 8, marginBottom: 8, fontSize: '0.88rem', lineHeight: 1.5 }}>
              <span style={{ color: 'oklch(55% 0.14 70)', flexShrink: 0, fontFamily: 'var(--font-mono)' }}>→</span>
              <span>{s}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ————————————————————————————————————————————————————————————————————————————
// Claude calls
// ————————————————————————————————————————————————————————————————————————————

async function callCoachFlags(draft) {
  try {
    const prompt = `You are a communication coach reviewing a draft apology. Identify specific patterns that weaken apologies. Be concise and concrete.

Draft:
"""
${draft}
"""

Return ONLY a JSON array of flag objects. Each object has:
- "label": short label (2-4 words, lowercase), e.g. "non-apology", "sandwich", "vague", "mind-reading", "over-qualifying"
- "severity": "red" (strongly undermines) | "yellow" (weakens) 
- "note": one short sentence explaining the flag
- "spans": array of exact quoted substrings from the draft that triggered this flag (1-3 phrases)

Only flag real patterns. If the draft is clean, return [].
Limit to 4 flags max. Focus on the most important.

Return ONLY valid JSON, no prose.`;
    const text = await window.claude.complete(prompt);
    return parseJsonArray(text);
  } catch (e) {
    console.warn('coach flags failed', e);
    return [];
  }
}

async function callDeepFeedback(draft) {
  try {
    const prompt = `You are a communication coach giving warm, specific feedback on a draft apology. The person is drafting privately. They want honest, concrete guidance — not validation.

Draft:
"""
${draft}
"""

Return ONLY a JSON object with these keys:
- "summary": 1-2 sentences, your overall read. Human, not clinical. Lowercase ok.
- "strengths": array of 0-3 specific things that work (short phrases).
- "suggestions": array of 0-3 specific things to consider — concrete, not generic. Not commands; invitations.

Return ONLY valid JSON.`;
    const text = await window.claude.complete(prompt);
    return parseJsonObject(text) || { error: 'coach went quiet. try again?' };
  } catch (e) {
    console.warn('deep feedback failed', e);
    return { error: 'coach went quiet. try again?' };
  }
}

function parseJsonArray(text) {
  if (!text) return [];
  const match = text.match(/\[[\s\S]*\]/);
  if (!match) return [];
  try {
    const arr = JSON.parse(match[0]);
    return Array.isArray(arr) ? arr.filter(x => x && x.label) : [];
  } catch { return []; }
}
function parseJsonObject(text) {
  if (!text) return null;
  const match = text.match(/\{[\s\S]*\}/);
  if (!match) return null;
  try { return JSON.parse(match[0]); } catch { return null; }
}

// Expose globally so the component file imports work across script scopes
Object.assign(window, { ApologyPhase });
console.log('[phases-apology] loaded, ApologyPhase on window:', typeof window.ApologyPhase);
