// 7play — modals, toasts, cmd+k palette
// All UI that overlays the workspace lives here.

// =====================================================
// Root modal dispatcher — renders whatever modal is open
// =====================================================
function ModalHost() {
  const { state, actions } = useStore();
  const m = state.modal;

  // ESC to close
  React.useEffect(() => {
    if (!m) return;
    const onKey = (e) => {
      if (e.key === 'Escape') {
        e.preventDefault();
        actions.closeModal();
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [m, actions]);

  if (!m) return null;

  const comp = {
    approval: ApprovalModal,
    preview: PreviewModal,
    settings: SettingsModal,
    cmdk: CmdKPalette,
    share: ShareModal,
    history: HistoryModal,
    qa: QAReportModal,
    compare: CompareModal,
    export_bundle: BundleModal,
    confirm: ConfirmModal,
    metadata: MetadataPopover,
    budget: BudgetModal,
  }[m.kind];
  if (!comp) return null;
  const Comp = comp;
  return (
    <div className="modal-scrim" onClick={(e) => { if (e.target === e.currentTarget) actions.closeModal(); }}>
      <Comp data={m.data || {}} />
    </div>
  );
}

// =====================================================
// Approval Modal — for paid_asset_generation + export
// (banners for strategy / missing_assets live inline in stages)
// =====================================================
function ApprovalModal({ data }) {
  const { actions, state } = useStore();
  const { kind, sessionId, onApprove, onReject } = data;
  const [reason, setReason] = React.useState('');
  const copy = {
    paid_asset_generation: {
      title: 'Approve paid asset generation',
      lede: 'Co-pilot needs 2 assets it cannot synthesize from your reference pack.',
      items: [
        { label: 'Winning animation loop (2s, 512×512)', cost: '$0.12', model: 'video-gen · internal' },
        { label: 'Localized CTA string (3 variants · en-US)', cost: '$0.06', model: 'claude-haiku-4-5' },
      ],
      total: '$0.18',
      warn: null,
      cta: 'Approve & generate',
      ctaIcon: 'sparkle',
    },
    export: {
      title: 'Export to AppLovin',
      lede: 'Push approved variants to AppLovin\'s ad-review API. AppLovin runs its own review — nothing publishes automatically.',
      items: [
        { label: 'Variant A · Near-miss hook', cost: 'free', model: 'mraid v3.0' },
        { label: 'Variant B · Reward-first', cost: 'free', model: 'mraid v3.0' },
      ],
      total: '0 credits',
      warn: null,
      cta: 'Push 2 bundles to AppLovin',
      ctaIcon: 'upload',
    },
    paid_regen: {
      title: 'Regenerate strategy',
      lede: 'Discards the current draft. Previous runs remain in history and can be restored.',
      items: [
        { label: 'New strategist pass · 4 variants', cost: '$0.32', model: 'claude-sonnet-4-5' },
      ],
      total: '$0.32',
      warn: 'Your approved strategy will be superseded. You\'ll need to re-approve.',
      cta: 'Regenerate strategy',
      ctaIcon: 'refresh',
    },
  }[kind] || {};

  return (
    <div className="modal lg" onClick={e => e.stopPropagation()}>
      <div className="modal-head">
        <div className="modal-title">
          <Icon name="lock" size={14} /> {copy.title}
        </div>
        <div className="modal-close" onClick={actions.closeModal}>×</div>
      </div>
      <div className="modal-body">
        <p className="lede" style={{ margin: '0 0 14px', fontSize: 13 }}>{copy.lede}</p>
        <div className="approval-items">
          {copy.items && copy.items.map((it, i) => (
            <div key={i} className="approval-item">
              <div>
                <div className="ai-label">{it.label}</div>
                <div className="ai-model mono">{it.model}</div>
              </div>
              <div className="ai-cost mono">{it.cost}</div>
            </div>
          ))}
        </div>
        <div className="approval-total">
          <span className="spec">ESTIMATED COST</span>
          <span className="mono" style={{ fontSize: 14, fontWeight: 600 }}>{copy.total}</span>
        </div>
        {copy.warn && (
          <div className="banner warn small" style={{ marginTop: 14 }}>
            <Icon name="alert" size={14} />
            <div style={{ fontSize: 12 }}>{copy.warn}</div>
          </div>
        )}

        <div className="approval-why">
          <div className="spec">WHY IS THIS GATED?</div>
          <div style={{ fontSize: 12, color: 'var(--ink-muted)', marginTop: 4 }}>
            Paid operations and external publishes always require explicit approval. Nothing runs — or leaves — your workspace without you confirming.
          </div>
        </div>

        <details className="approval-reject">
          <summary>Reject or ask for changes</summary>
          <textarea
            className="input"
            rows={2}
            placeholder="Tell co-pilot what to do differently (optional)"
            value={reason}
            onChange={e => setReason(e.target.value)}
          />
        </details>
      </div>
      <div className="modal-foot">
        <div className="spec">ESC to cancel · ⏎ to approve</div>
        <div style={{ display: 'flex', gap: 8 }}>
          <button className="btn" onClick={() => { if (onReject) onReject(reason); actions.closeModal(); }}>
            Reject{reason ? ' with reason' : ''}
          </button>
          <button
            className="btn accent lg"
            onClick={() => { if (onApprove) onApprove(); actions.closeModal(); }}
          >
            <Icon name={copy.ctaIcon || 'check'} size={14} /> {copy.cta}
          </button>
        </div>
      </div>
    </div>
  );
}

// =====================================================
// Preview Modal — fullscreen variant preview
// Play/pause, viewport, rotate, revision timeline, compare, QA
// =====================================================
function PreviewModal({ data }) {
  const { state, actions } = useStore();
  const { sessionId, variantId } = data;
  const session = state.sessions[sessionId];
  const variant = session?.variants.find(v => v.id === variantId);
  const game = window.GAMES.find(g => g.id === session?.gameId);
  const [playing, setPlaying] = React.useState(true);
  const [viewport, setViewport] = React.useState('portrait'); // portrait | landscape | tablet
  const [shownRev, setShownRev] = React.useState(variant?.currentRev || 1);

  if (!variant || !game) return null;

  const rev = variant.revisions.find(r => r.rev === shownRev) || variant.revisions[variant.revisions.length - 1];
  const size = viewport === 'portrait' ? { w: 288, h: 580 } : viewport === 'landscape' ? { w: 580, h: 326 } : { w: 440, h: 580 };

  return (
    <div className="modal xl preview-modal" onClick={e => e.stopPropagation()}>
      <div className="modal-head">
        <div className="modal-title" style={{ gap: 10 }}>
          <span className={`chip ${variant.color}`}>VAR {variant.key}</span>
          {variant.name}
          <span className="chip" style={{ fontSize: 10 }}>r{variant.currentRev}</span>
          {variant.state === 'approved' && <span className="chip lime" style={{ fontSize: 10 }}><Icon name="check" size={9} /> approved</span>}
        </div>
        <div style={{ display: 'flex', gap: 4 }}>
          <button className="btn ghost sm" onClick={() => actions.openModal({ kind: 'qa', data: { sessionId, variantId } })}><Icon name="shield" size={12} /> QA</button>
          <button className="btn ghost sm" onClick={() => actions.openModal({ kind: 'compare', data: { sessionId, variantId } })}><Icon name="compare" size={12} /> Compare</button>
          <button className="btn ghost sm" onClick={() => { actions.pushToast({ kind: 'info', title: 'Preview HTML downloaded', text: `variant_${variant.key}_r${variant.currentRev}.html` }); }}><Icon name="download" size={12} /> HTML</button>
          <div className="modal-close" onClick={actions.closeModal}>×</div>
        </div>
      </div>

      <div className="preview-body">
        {/* Preview stage */}
        <div className="preview-stage">
          <div className="vp-tabs">
            <button className={viewport === 'portrait' ? 'active' : ''} onClick={() => setViewport('portrait')}><Icon name="phone" size={12} /> Portrait</button>
            <button className={viewport === 'landscape' ? 'active' : ''} onClick={() => setViewport('landscape')}><Icon name="rotate" size={12} /> Landscape</button>
            <button className={viewport === 'tablet' ? 'active' : ''} onClick={() => setViewport('tablet')}><Icon name="tablet" size={12} /> Tablet</button>
          </div>

          <div className="preview-frame-wrap">
            <div className="preview-phone" style={{ width: size.w, height: size.h }}>
              {playing && <PlayablePreview game={game} variant={variant.key} state="playing" />}
              {!playing && (
                <div style={{ position: 'absolute', inset: 6, borderRadius: 12, background: 'var(--ink)', display: 'grid', placeItems: 'center', color: 'var(--ink-faint)', fontFamily: 'var(--font-mono)', fontSize: 11 }}>
                  paused · t=3.2s
                </div>
              )}
              {rev?.failed && (
                <div style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.6)', display: 'grid', placeItems: 'center', color: '#fff', fontSize: 11, letterSpacing: '0.06em', borderRadius: 16 }}>
                  <div style={{ textAlign: 'center' }}>
                    <div style={{ fontWeight: 700, marginBottom: 4 }}>REVISION FAILED</div>
                    <div style={{ fontSize: 10, color: 'var(--ink-faint)' }}>{variant.qa?.notes}</div>
                  </div>
                </div>
              )}
            </div>
          </div>

          <div className="preview-controls">
            <button className="btn sm" onClick={() => setPlaying(p => !p)}>
              <Icon name={playing ? 'pause' : 'play'} size={12} /> {playing ? 'Pause' : 'Play'}
            </button>
            <button className="btn sm" onClick={() => { setPlaying(false); setTimeout(() => setPlaying(true), 60); }}>
              <Icon name="refresh" size={12} /> Replay
            </button>
            <div className="mono" style={{ color: 'var(--ink-faint)', fontSize: 11, marginLeft: 8 }}>
              {size.w} × {size.h} · 60fps · {variant.runtime}
            </div>
          </div>
        </div>

        {/* Right rail */}
        <div className="preview-rail">
          <div className="pr-section">
            <div className="spec">HYPOTHESIS</div>
            <div style={{ fontSize: 13, marginTop: 5, color: 'var(--ink)' }}>{variant.hypothesis}</div>
          </div>

          <div className="pr-section">
            <div className="spec" style={{ display: 'flex', justifyContent: 'space-between' }}>
              <span>REVISION TIMELINE</span>
              <span style={{ color: 'var(--ink-faint)' }}>{variant.revisions.length} total</span>
            </div>
            <div className="rev-timeline">
              {variant.revisions.map(r => (
                <div
                  key={r.rev}
                  className={`rev-row ${r.rev === shownRev ? 'shown' : ''} ${r.current ? 'current' : ''} ${r.failed ? 'failed' : ''} ${r.approved ? 'approved' : ''}`}
                  onClick={() => setShownRev(r.rev)}
                >
                  <div className="rev-dot" />
                  <div style={{ flex: 1 }}>
                    <div className="rev-title">
                      r{r.rev} · {r.note}
                      {r.current && <span className="chip" style={{ fontSize: 9, marginLeft: 6 }}>CURRENT</span>}
                      {r.approved && <span className="chip lime" style={{ fontSize: 9, marginLeft: 6 }}>APPROVED</span>}
                    </div>
                    <div className="rev-meta mono">{r.time}</div>
                  </div>
                  {r.rev !== variant.currentRev && (
                    <button className="btn ghost sm" onClick={(e) => {
                      e.stopPropagation();
                      actions.updateVariant(sessionId, variantId, {
                        currentRev: r.rev,
                        revisions: variant.revisions.map(x => ({ ...x, current: x.rev === r.rev })),
                      });
                      actions.pushToast({ kind: 'info', title: `Restored ${variant.name} to r${r.rev}`, text: 'Previous revisions remain viewable.' });
                    }}><Icon name="revert" size={11} /></button>
                  )}
                </div>
              ))}
            </div>
          </div>

          <div className="pr-section">
            <div className="spec">QA</div>
            <div className="qa-row"><span className={variant.qa?.staticOk ? 'ok' : 'fail'}><Icon name={variant.qa?.staticOk ? 'check' : 'alert'} size={11} /></span> Static compliance</div>
            <div className="qa-row"><span className={variant.qa?.runtimeOk ? 'ok' : 'fail'}><Icon name={variant.qa?.runtimeOk ? 'check' : 'alert'} size={11} /></span> Runtime smoke</div>
            <div className="qa-row"><span className={variant.qa?.heuristicsOk ? 'ok' : 'fail'}><Icon name={variant.qa?.heuristicsOk ? 'check' : 'alert'} size={11} /></span> UX heuristics</div>
            <div className="qa-row"><span className={variant.qa?.applovinOk ? 'ok' : 'fail'}><Icon name={variant.qa?.applovinOk ? 'check' : 'alert'} size={11} /></span> AppLovin MRAID v3</div>
            {variant.qa?.notes && <div className="qa-note">{variant.qa.notes}</div>}
          </div>

          <div className="pr-actions">
            {variant.state !== 'approved' && variant.qa?.applovinOk && (
              <button className="btn accent" onClick={() => {
                actions.updateVariant(sessionId, variantId, { state: 'approved' });
                actions.pushToast({ kind: 'success', title: `${variant.name} approved for export`, text: `⇧A hotkey works too` });
              }}><Icon name="check" size={13} /> Approve for export</button>
            )}
            {variant.state === 'approved' && (
              <button className="btn" onClick={() => {
                actions.updateVariant(sessionId, variantId, { state: 'ready' });
                actions.pushToast({ kind: 'info', title: 'Approval removed' });
              }}><Icon name="revert" size={12} /> Remove approval</button>
            )}
            <button className="btn" onClick={() => {
              const msg = {
                kind: 'user', time: 'now',
                text: `Revise Variant ${variant.key} (${variant.name}) — `,
              };
              actions.addChat(sessionId, 3, msg);
              actions.updateVariant(sessionId, variantId, { state: 'needs_revision' });
              actions.pushToast({ kind: 'info', title: `Drafted revision request`, text: 'Finish your message in chat → submit spawns r' + (variant.currentRev + 1) });
              actions.closeModal();
            }}><Icon name="chat" size={12} /> Revise via chat</button>
            <button className="btn" onClick={() => {
              const newVar = {
                ...variant,
                id: 'v' + Date.now(),
                key: variant.key + "'",
                state: 'building',
                currentRev: 1,
                revisions: [{ rev: 1, time: 'now', note: `cloned from ${variant.name}`, current: true }],
              };
              const updated = [...session.variants, newVar];
              actions.updateSession(sessionId, { variants: updated });
              actions.pushToast({ kind: 'info', title: `Duplicated as new lineage`, text: `Variant ${newVar.key} queued` });
              actions.closeModal();
            }}><Icon name="copy" size={12} /> Duplicate as new lineage</button>
            <button className="btn danger" onClick={() => {
              actions.openModal({ kind: 'confirm', data: {
                title: `Archive Variant ${variant.key}?`,
                body: 'Archived variants are hidden from Review and cannot be exported. You can restore them from the History modal.',
                cta: 'Archive',
                danger: true,
                onConfirm: () => {
                  actions.updateVariant(sessionId, variantId, { archived: true, state: 'archived' });
                  actions.pushToast({ kind: 'info', title: `Variant ${variant.key} archived` });
                  actions.closeModal();
                },
              }});
            }}><Icon name="archive" size={12} /> Archive variant</button>
          </div>
        </div>
      </div>
    </div>
  );
}

// =====================================================
// QA Report Modal
// =====================================================
function QAReportModal({ data }) {
  const { state, actions } = useStore();
  const { sessionId, variantId } = data;
  const session = state.sessions[sessionId];
  const variant = session?.variants.find(v => v.id === variantId);
  if (!variant) return null;
  const q = variant.qa || {};
  return (
    <div className="modal lg" onClick={e => e.stopPropagation()}>
      <div className="modal-head">
        <div className="modal-title"><Icon name="shield" size={14} /> QA report — {variant.name}</div>
        <div className="modal-close" onClick={actions.closeModal}>×</div>
      </div>
      <div className="modal-body">
        <div className="qa-section">
          <div className="qa-section-h">Static compliance</div>
          <div className={`qa-line ${q.staticOk ? 'ok' : 'fail'}`}>
            <Icon name={q.staticOk ? 'check' : 'alert'} size={12} /> Bundle stays within size budget (500kb)
          </div>
          <div className={`qa-line ${q.staticOk ? 'ok' : 'fail'}`}>
            <Icon name={q.staticOk ? 'check' : 'alert'} size={12} /> No external network calls
          </div>
          <div className={`qa-line ${q.staticOk ? 'ok' : 'fail'}`}>
            <Icon name={q.staticOk ? 'check' : 'alert'} size={12} /> All click-throughs resolve to the configured store URL
          </div>
        </div>
        <div className="qa-section">
          <div className="qa-section-h">Runtime smoke</div>
          <div className={`qa-line ${q.runtimeOk ? 'ok' : 'fail'}`}>
            <Icon name={q.runtimeOk ? 'check' : 'alert'} size={12} /> Plays through to win or CTA state (18s cap)
          </div>
          <div className={`qa-line ${q.runtimeOk ? 'ok' : 'fail'}`}>
            <Icon name={q.runtimeOk ? 'check' : 'alert'} size={12} /> No unhandled errors in headless runner
          </div>
          <div className={`qa-line ${q.runtimeOk ? 'ok' : 'fail'}`}>
            <Icon name={q.runtimeOk ? 'check' : 'alert'} size={12} /> 60fps maintained on baseline device profile
          </div>
        </div>
        <div className="qa-section">
          <div className="qa-section-h">UX heuristics</div>
          <div className={`qa-line ${q.heuristicsOk ? 'ok' : 'fail'}`}>
            <Icon name={q.heuristicsOk ? 'check' : 'alert'} size={12} /> Hook resolved in first 3 seconds
          </div>
          <div className={`qa-line ${q.heuristicsOk ? 'ok' : 'fail'}`}>
            <Icon name={q.heuristicsOk ? 'check' : 'alert'} size={12} /> CTA appears within the playable moment
          </div>
          <div className={`qa-line ${q.heuristicsOk ? 'ok' : 'fail'}`}>
            <Icon name={q.heuristicsOk ? 'check' : 'alert'} size={12} /> Audio loudness within −14 LUFS ±2
          </div>
        </div>
        <div className="qa-section">
          <div className="qa-section-h">AppLovin contract</div>
          <div className={`qa-line ${q.applovinOk ? 'ok' : 'fail'}`}>
            <Icon name={q.applovinOk ? 'check' : 'alert'} size={12} /> MRAID v3.0 compliant
          </div>
          <div className={`qa-line ${q.applovinOk ? 'ok' : 'fail'}`}>
            <Icon name={q.applovinOk ? 'check' : 'alert'} size={12} /> Single-file inline HTML, no external refs
          </div>
        </div>
        {q.notes && (
          <div className="qa-notes">
            <div className="spec">AGENT NOTE</div>
            <div style={{ marginTop: 5, fontSize: 12 }}>{q.notes}</div>
          </div>
        )}
      </div>
      <div className="modal-foot">
        <div className="spec">Every check is recorded in the run manifest.</div>
        <button className="btn" onClick={actions.closeModal}>Close</button>
      </div>
    </div>
  );
}

// =====================================================
// Compare Modal — side-by-side revisions
// =====================================================
function CompareModal({ data }) {
  const { state, actions } = useStore();
  const { sessionId, variantId } = data;
  const session = state.sessions[sessionId];
  const variant = session?.variants.find(v => v.id === variantId);
  const game = window.GAMES.find(g => g.id === session?.gameId);
  const [left, setLeft] = React.useState(Math.max(1, (variant?.currentRev || 2) - 1));
  const [right, setRight] = React.useState(variant?.currentRev || 1);
  if (!variant || !game) return null;

  return (
    <div className="modal xl" onClick={e => e.stopPropagation()}>
      <div className="modal-head">
        <div className="modal-title"><Icon name="compare" size={14} /> Compare revisions — {variant.name}</div>
        <div className="modal-close" onClick={actions.closeModal}>×</div>
      </div>
      <div className="modal-body">
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18 }}>
          {[{ side: 'L', val: left, setVal: setLeft }, { side: 'R', val: right, setVal: setRight }].map(({ side, val, setVal }) => {
            const rev = variant.revisions.find(r => r.rev === val);
            return (
              <div key={side}>
                <div className="compare-head">
                  <span className="chip">{side === 'L' ? 'BEFORE' : 'AFTER'} · r{val}</span>
                  <select className="mini-select" value={val} onChange={e => setVal(+e.target.value)}>
                    {variant.revisions.map(r => <option key={r.rev} value={r.rev}>r{r.rev} — {r.note}</option>)}
                  </select>
                </div>
                <div className="compare-frame">
                  <div className="preview-phone" style={{ width: 240, height: 480, margin: '0 auto' }}>
                    <PlayablePreview game={game} variant={variant.key + (side === 'L' ? '' : '-r')} state="playing" />
                  </div>
                </div>
                <div className="compare-meta">
                  <div className="spec">{rev?.time}</div>
                  <div style={{ fontSize: 12, marginTop: 4 }}>{rev?.note}</div>
                </div>
              </div>
            );
          })}
        </div>
        <div className="hr" />
        <div className="spec">DIFF SUMMARY</div>
        <div className="diff-list">
          <div className="diff-row added">+ CTA pulse duration 1.2s → 1.8s</div>
          <div className="diff-row changed">~ Coin-rain VFX layer opacity 0.9 → 0.72</div>
          <div className="diff-row unchanged">= Scene graph, audio master, character sprite</div>
        </div>
      </div>
      <div className="modal-foot">
        <div className="spec">Revisions are immutable. Compare loads the archived runtime for each.</div>
        <button className="btn" onClick={actions.closeModal}>Close</button>
      </div>
    </div>
  );
}

// =====================================================
// Settings Modal
// =====================================================
function SettingsModal({ data }) {
  const { state, actions } = useStore();
  const { sessionId } = data;
  const session = state.sessions[sessionId];
  const [draft, setDraft] = React.useState({
    authenticity: session?.authenticity || 'true_to_game',
    maxUsd: session?.budget?.maxUsd || 5.00,
    maxMin: session?.budget?.maxMin || 20,
  });
  if (!session) return null;

  return (
    <div className="modal md" onClick={e => e.stopPropagation()}>
      <div className="modal-head">
        <div className="modal-title"><Icon name="settings" size={14} /> Session settings</div>
        <div className="modal-close" onClick={actions.closeModal}>×</div>
      </div>
      <div className="modal-body">
        <div className="form-group">
          <label className="spec">AUTHENTICITY LEVEL</label>
          <div className="auth-grid">
            {[
              { k: 'true_to_game', t: 'True-to-game', s: 'Uses your brand assets verbatim.' },
              { k: 'inspired_by', t: 'Inspired-by', s: 'Style-referenced, no literal brand marks.' },
              { k: 'stylized_marketing', t: 'Stylized marketing', s: 'Full creative freedom.' },
            ].map(o => (
              <div
                key={o.k}
                className={`auth-opt ${draft.authenticity === o.k ? 'on' : ''}`}
                onClick={() => setDraft(d => ({ ...d, authenticity: o.k }))}
              >
                <div className="auth-t">{o.t}</div>
                <div className="auth-s">{o.s}</div>
              </div>
            ))}
          </div>
        </div>

        <div className="form-group">
          <label className="spec">BUDGET CAP</label>
          <div style={{ display: 'flex', gap: 10 }}>
            <div style={{ flex: 1 }}>
              <div className="spec">MAX SPEND</div>
              <input className="input" type="number" step="0.5" value={draft.maxUsd} onChange={e => setDraft(d => ({ ...d, maxUsd: +e.target.value }))} />
            </div>
            <div style={{ flex: 1 }}>
              <div className="spec">MAX WALL-TIME (MIN)</div>
              <input className="input" type="number" value={draft.maxMin} onChange={e => setDraft(d => ({ ...d, maxMin: +e.target.value }))} />
            </div>
          </div>
        </div>

        <div className="form-group">
          <label className="spec">RUNTIME & EXPORT (LOCKED V1)</label>
          <div className="locked-row">
            <div>2D canvas runtime</div>
            <span className="chip">locked</span>
          </div>
          <div className="locked-row">
            <div>AppLovin export first</div>
            <span className="chip">locked</span>
          </div>
          <div className="locked-row">
            <div>claude-haiku-4-5 · reference pack, QA</div>
            <span className="chip">default</span>
          </div>
        </div>
      </div>
      <div className="modal-foot">
        <button className="btn" onClick={actions.closeModal}>Cancel</button>
        <button className="btn accent" onClick={() => {
          actions.updateSession(sessionId, {
            authenticity: draft.authenticity,
            budget: { ...session.budget, maxUsd: draft.maxUsd, maxMin: draft.maxMin },
          });
          actions.pushToast({ kind: 'success', title: 'Settings saved' });
          actions.closeModal();
        }}><Icon name="check" size={13} /> Save settings</button>
      </div>
    </div>
  );
}

// =====================================================
// Cmd+K Palette
// =====================================================
function CmdKPalette() {
  const { state, actions } = useStore();
  const [q, setQ] = React.useState('');
  const ref = React.useRef(null);
  React.useEffect(() => { ref.current?.focus(); }, []);

  const activeSessionId = state.route.kind === 'session' ? state.route.sessionId : null;

  const commands = [
    { id: 'new', label: 'New session', sub: 'Create from scratch', icon: 'plus', run: () => { actions.closeModal(); actions.nav('new'); } },
    { id: 'sessions', label: 'Go to sessions', sub: 'All sessions', icon: 'layers', run: () => { actions.closeModal(); actions.nav('home'); } },
    ...Object.values(state.sessions).slice(0, 8).map(s => {
      const g = window.GAMES.find(x => x.id === s.gameId);
      return { id: 'open-' + s.id, label: `Open: ${g?.name || s.name}`, sub: `Stage ${s.stage + 1}/5 · ${s.updatedLabel}`, icon: 'arrowRight', run: () => { actions.closeModal(); actions.nav('session', s.id); } };
    }),
    activeSessionId && { id: 'settings', label: 'Session settings', sub: 'Authenticity, budget cap', icon: 'settings', run: () => actions.openModal({ kind: 'settings', data: { sessionId: activeSessionId } }) },
    activeSessionId && { id: 'share', label: 'Copy shareable link', sub: 'Signed URL, 24h', icon: 'share', run: () => actions.openModal({ kind: 'share', data: { sessionId: activeSessionId } }) },
    activeSessionId && { id: 'budget', label: 'Open budget panel', sub: 'Spend + wall-time', icon: 'bolt', run: () => { actions.closeModal(); actions.toggleBudgetPanel(); } },
    { id: 'tweaks', label: 'Toggle tweaks panel', sub: 'Stepper variants', icon: 'sparkle', run: () => { actions.closeModal(); window.parent.postMessage({ type: '__toggle_edit_mode' }, '*'); } },
  ].filter(Boolean);

  const filtered = commands.filter(c => !q || (c.label + ' ' + c.sub).toLowerCase().includes(q.toLowerCase()));
  return (
    <div className="modal cmdk" onClick={e => e.stopPropagation()}>
      <div className="cmdk-input">
        <Icon name="search" size={14} />
        <input ref={ref} placeholder="Type a command or search…" value={q} onChange={e => setQ(e.target.value)}
          onKeyDown={e => { if (e.key === 'Enter' && filtered[0]) filtered[0].run(); }} />
        <span className="spec">⌘K</span>
      </div>
      <div className="cmdk-body">
        {filtered.map((c, i) => (
          <div key={c.id} className={`cmdk-row ${i === 0 ? 'sel' : ''}`} onClick={c.run}>
            <Icon name={c.icon} size={13} />
            <div>
              <div className="cmdk-label">{c.label}</div>
              <div className="cmdk-sub">{c.sub}</div>
            </div>
            <span className="spec">↵</span>
          </div>
        ))}
        {!filtered.length && <div className="cmdk-empty">No matching commands</div>}
      </div>
    </div>
  );
}

// =====================================================
// Share Modal
// =====================================================
function ShareModal({ data }) {
  const { actions } = useStore();
  const { sessionId } = data;
  const url = `https://7play.ai/p/${sessionId}?sig=${Math.random().toString(36).slice(2, 10)}`;
  const [copied, setCopied] = React.useState(false);
  return (
    <div className="modal md" onClick={e => e.stopPropagation()}>
      <div className="modal-head">
        <div className="modal-title"><Icon name="share" size={14} /> Share session</div>
        <div className="modal-close" onClick={actions.closeModal}>×</div>
      </div>
      <div className="modal-body">
        <p className="lede" style={{ fontSize: 13, margin: '0 0 10px' }}>
          Read-only preview link. Previews & revisions are visible; settings and export are not.
        </p>
        <div className="copy-url">
          <input className="input mono" readOnly value={url} onClick={e => e.target.select()} />
          <button className="btn" onClick={() => {
            navigator.clipboard?.writeText(url);
            setCopied(true);
            actions.pushToast({ kind: 'success', title: 'Copied to clipboard', text: 'Link expires in 24 hours.' });
            setTimeout(() => setCopied(false), 1500);
          }}>{copied ? <><Icon name="check" size={12} /> Copied</> : <><Icon name="copy" size={12} /> Copy</>}</button>
        </div>
        <div className="spec" style={{ marginTop: 10 }}>EXPIRES IN 24 HOURS · SIGNED</div>
      </div>
      <div className="modal-foot">
        <button className="btn" onClick={actions.closeModal}>Close</button>
      </div>
    </div>
  );
}

// =====================================================
// History Modal (strategy revisions + archived variants)
// =====================================================
function HistoryModal({ data }) {
  const { state, actions } = useStore();
  const { sessionId } = data;
  const s = state.sessions[sessionId];
  if (!s) return null;
  return (
    <div className="modal lg" onClick={e => e.stopPropagation()}>
      <div className="modal-head">
        <div className="modal-title"><Icon name="history" size={14} /> Revision history</div>
        <div className="modal-close" onClick={actions.closeModal}>×</div>
      </div>
      <div className="modal-body">
        <div className="spec">STRATEGY</div>
        <div className="hist-list">
          {s.strategyHistory.map(h => (
            <div key={h.rev} className={`hist-row ${h.current ? 'current' : ''}`}>
              <div className="hist-dot" />
              <div style={{ flex: 1 }}>
                <div className="hist-t">Strategy r{h.rev} · {h.label}</div>
                <div className="hist-m mono">{h.time}</div>
              </div>
              {h.approved && <span className="chip lime" style={{ fontSize: 9 }}>APPROVED</span>}
              {!h.current && <button className="btn ghost sm">View</button>}
            </div>
          ))}
        </div>

        <div className="hr" />
        <div className="spec">VARIANT REVISIONS</div>
        <div className="hist-list">
          {s.variants.flatMap(v => v.revisions.map(r => ({ v, r }))).map(({ v, r }, i) => (
            <div key={v.id + r.rev} className={`hist-row ${r.current ? 'current' : ''} ${r.failed ? 'failed' : ''}`}>
              <span className={`chip ${v.color}`} style={{ fontSize: 9 }}>VAR {v.key}</span>
              <div style={{ flex: 1 }}>
                <div className="hist-t">r{r.rev} · {r.note}</div>
                <div className="hist-m mono">{r.time}</div>
              </div>
              {r.approved && <span className="chip lime" style={{ fontSize: 9 }}>APPROVED</span>}
              {r.failed && <span className="chip warn" style={{ fontSize: 9 }}>FAILED</span>}
            </div>
          ))}
        </div>

        {s.variants.some(v => v.archived) && (
          <>
            <div className="hr" />
            <div className="spec">ARCHIVED</div>
            <div className="hist-list">
              {s.variants.filter(v => v.archived).map(v => (
                <div key={v.id} className="hist-row">
                  <span className={`chip ${v.color}`} style={{ fontSize: 9 }}>VAR {v.key}</span>
                  <div style={{ flex: 1 }}><div className="hist-t">{v.name}</div></div>
                  <button className="btn ghost sm" onClick={() => {
                    actions.updateVariant(sessionId, v.id, { archived: false, state: 'ready' });
                  }}><Icon name="revert" size={11} /> Restore</button>
                </div>
              ))}
            </div>
          </>
        )}
      </div>
      <div className="modal-foot">
        <div className="spec">Every revision is immutable. Restoring re-points the current pointer, the archived runtime stays.</div>
        <button className="btn" onClick={actions.closeModal}>Close</button>
      </div>
    </div>
  );
}

// =====================================================
// Bundle Modal — export bundle details
// =====================================================
function BundleModal({ data }) {
  const { actions } = useStore();
  const { variant } = data;
  return (
    <div className="modal md" onClick={e => e.stopPropagation()}>
      <div className="modal-head">
        <div className="modal-title"><Icon name="download" size={14} /> Export bundle — Variant {variant.key}</div>
        <div className="modal-close" onClick={actions.closeModal}>×</div>
      </div>
      <div className="modal-body">
        <div className="bundle-row"><span className="spec">BUNDLE</span><span className="mono">var_{variant.key.toLowerCase()}_r{variant.currentRev}.zip</span></div>
        <div className="bundle-row"><span className="spec">SIZE</span><span className="mono">{variant.runtime}</span></div>
        <div className="bundle-row"><span className="spec">HASH</span><span className="mono">a19f7c…b42e</span></div>
        <div className="bundle-row"><span className="spec">CREATED</span><span className="mono">2026-04-18T15:18Z</span></div>
        <div className="bundle-row"><span className="spec">MRAID</span><span className="mono">v3.0</span></div>
        <div className="bundle-row"><span className="spec">SIGNED URL</span><span className="mono" style={{ fontSize: 10 }}>https://cdn.7play.ai/…expires=1h</span></div>
        <div className="hr" />
        <div className="spec">CONTENTS</div>
        <div className="tree">
          <div>├ index.html <span className="mono" style={{ color: 'var(--ink-faint)' }}>· inlined</span></div>
          <div>├ manifest.json</div>
          <div>├ qa/report.json</div>
          <div>├ provenance/assets.json</div>
          <div>└ logs/build.jsonl</div>
        </div>
      </div>
      <div className="modal-foot">
        <button className="btn" onClick={actions.closeModal}>Close</button>
        <button className="btn" onClick={() => {
          actions.pushToast({ kind: 'success', title: `Signed URL copied`, text: 'Expires in 1 hour.' });
        }}><Icon name="copy" size={12} /> Copy signed URL</button>
        <button className="btn accent" onClick={() => {
          actions.pushToast({ kind: 'success', title: 'Download started', text: `var_${variant.key.toLowerCase()}_r${variant.currentRev}.zip` });
        }}><Icon name="download" size={12} /> Download ZIP</button>
      </div>
    </div>
  );
}

// =====================================================
// Confirm Modal (generic)
// =====================================================
function ConfirmModal({ data }) {
  const { actions } = useStore();
  const { title, body, cta, danger, onConfirm } = data;
  return (
    <div className="modal sm" onClick={e => e.stopPropagation()}>
      <div className="modal-head">
        <div className="modal-title">{title}</div>
        <div className="modal-close" onClick={actions.closeModal}>×</div>
      </div>
      <div className="modal-body">
        <p style={{ fontSize: 13, margin: 0, color: 'var(--ink-muted)' }}>{body}</p>
      </div>
      <div className="modal-foot">
        <button className="btn" onClick={actions.closeModal}>Cancel</button>
        <button className={`btn ${danger ? 'danger' : 'accent'}`} onClick={() => { onConfirm && onConfirm(); actions.closeModal(); }}>
          {cta || 'Confirm'}
        </button>
      </div>
    </div>
  );
}

// =====================================================
// Budget Modal — expanded panel
// =====================================================
function BudgetModal({ data }) {
  const { state, actions } = useStore();
  const { sessionId } = data;
  const s = state.sessions[sessionId];
  if (!s) return null;
  const b = s.budget;
  const spendPct = Math.min(100, (b.spent / b.maxUsd) * 100);
  const timePct = Math.min(100, (b.elapsedMin / b.maxMin) * 100);
  return (
    <div className="modal md" onClick={e => e.stopPropagation()}>
      <div className="modal-head">
        <div className="modal-title"><Icon name="bolt" size={14} /> Budget</div>
        <div className="modal-close" onClick={actions.closeModal}>×</div>
      </div>
      <div className="modal-body">
        <div className="budget-big">
          <div>
            <div className="spec">SPENT</div>
            <div className="mono" style={{ fontSize: 20 }}>${b.spent.toFixed(2)} <span style={{ color: 'var(--ink-faint)', fontSize: 13 }}>/ ${b.maxUsd.toFixed(2)}</span></div>
            <div className="bar"><div className="bar-fill" style={{ width: spendPct + '%' }} /></div>
          </div>
          <div>
            <div className="spec">WALL-TIME</div>
            <div className="mono" style={{ fontSize: 20 }}>{b.elapsedMin}m <span style={{ color: 'var(--ink-faint)', fontSize: 13 }}>/ {b.maxMin}m</span></div>
            <div className="bar"><div className="bar-fill" style={{ width: timePct + '%' }} /></div>
          </div>
        </div>
        <div className="spec" style={{ marginTop: 18 }}>WHAT SPEND INCLUDES</div>
        <div className="budget-breakdown">
          <div><span>Reference pack analysis</span><span className="mono">$0.14</span></div>
          <div><span>Strategy run × 1</span><span className="mono">$0.32</span></div>
          <div><span>Variant builds × 4</span><span className="mono">$1.24</span></div>
          <div><span>QA sweeps</span><span className="mono">$0.44</span></div>
        </div>
      </div>
      <div className="modal-foot">
        <button className="btn" onClick={() => actions.openModal({ kind: 'settings', data: { sessionId } })}><Icon name="settings" size={12} /> Increase cap</button>
        <button className="btn accent" onClick={actions.closeModal}>Close</button>
      </div>
    </div>
  );
}

// =====================================================
// Metadata Popover (session header)
// =====================================================
function MetadataPopover({ data }) {
  const { state, actions } = useStore();
  const s = state.sessions[data.sessionId];
  const g = s && window.GAMES.find(x => x.id === s.gameId);
  if (!s) return null;
  return (
    <div className="modal sm" onClick={e => e.stopPropagation()}>
      <div className="modal-head">
        <div className="modal-title"><Icon name="info" size={14} /> Session metadata</div>
        <div className="modal-close" onClick={actions.closeModal}>×</div>
      </div>
      <div className="modal-body">
        <dl className="kv">
          <dt>Session</dt><dd className="mono">{s.id}</dd>
          <dt>Game</dt><dd>{g?.name}</dd>
          <dt>Genre</dt><dd>{g?.genre}</dd>
          <dt>Authenticity</dt><dd>{s.authenticity.replace(/_/g, '-')}</dd>
          <dt>Export target</dt><dd>AppLovin · MRAID v3</dd>
          <dt>Created</dt><dd className="mono">{s.created || '—'}</dd>
          <dt>Cost to date</dt><dd className="mono">${s.budget.spent.toFixed(2)}</dd>
        </dl>
      </div>
      <div className="modal-foot">
        <button className="btn" onClick={() => actions.openModal({ kind: 'settings', data: { sessionId: s.id } })}><Icon name="settings" size={12} /> Edit settings</button>
        <button className="btn accent" onClick={actions.closeModal}>Close</button>
      </div>
    </div>
  );
}

// =====================================================
// Toasts
// =====================================================
function ToastHost() {
  const { state, actions } = useStore();
  if (!state.toasts.length) return null;
  return (
    <div className="toast-host">
      {state.toasts.map(t => (
        <div key={t.id} className={`toast ${t.kind || 'info'}`}>
          <div className="toast-ic">
            <Icon name={t.kind === 'conflict' ? 'alert' : t.kind === 'budget' ? 'bolt' : t.kind === 'success' ? 'check' : 'info'} size={13} />
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div className="toast-t">{t.title}</div>
            {t.text && <div className="toast-x">{t.text}</div>}
            {t.actions && (
              <div className="toast-actions">
                {t.actions.map((a, i) => (
                  <button key={i} className="mini" onClick={() => { a.run(); actions.dismissToast(t.id); }}>{a.label}</button>
                ))}
              </div>
            )}
          </div>
          <button className="toast-close" onClick={() => actions.dismissToast(t.id)}>×</button>
        </div>
      ))}
    </div>
  );
}

window.ModalHost = ModalHost;
window.ToastHost = ToastHost;
