const TabAnunciosFull = ({ campanha, copy, prompts }) => {
  const proposta    = campanha?.proposta_json || {};
  const commPlan    = proposta.comm_plan || [];
  const canaisSetup = campanha?.canais_setup ? (typeof campanha.canais_setup === 'string' ? JSON.parse(campanha.canais_setup) : campanha.canais_setup) : null;
  const CHAN_NORM   = { linkedin:'linkedin_ads', google_ads:'google_ads_search', instagram:'meta_ads', facebook:'meta_ads', site:'website', led:'muppi_led' };

  const paidItems = commPlan.filter(item => {
    const norm = CHAN_NORM[item.canal] || item.canal;
    return PAID_CHANNELS.has(norm) || PAID_CHANNELS.has(item.canal);
  });

  if (paidItems.length === 0) return (
    <div style={{ padding: '48px 0', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}>
      <div style={{ fontSize: 13, color: 'var(--text-muted,#64748b)', textAlign: 'center' }}>
        Sem canais de anúncios pagos no plano de comunicação.<br/>Selecciona Meta Ads, LinkedIn Ads ou Google Ads no briefing.
      </div>
    </div>
  );

  // Agrupar items por canal normalizado
  const byChannel = {};
  paidItems.forEach(item => {
    const ch = CHAN_NORM[item.canal] || item.canal;
    if (!byChannel[ch]) byChannel[ch] = [];
    byChannel[ch].push(item);
  });

  const lSt = { fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--text-dim,#94a3b8)', fontFamily: 'var(--font-mono,monospace)', marginBottom: 4 };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      {Object.entries(byChannel).map(([ch, items]) => {
        const col   = CANAL_COLORS[ch] || '#3859D0';
        const setup = canaisSetup?.canais?.[ch];
        const audiencias = setup?.audiencias || [];

        return (
          <div key={ch} style={{ background: 'var(--bg-elev,#fff)', border: '1px solid var(--border,#e2e8f0)', borderLeft: `4px solid ${col}`, borderRadius: 10, overflow: 'hidden' }}>
            {/* Channel header */}
            <div style={{ padding: '14px 20px', borderBottom: '1px solid var(--border,#e2e8f0)', display: 'flex', alignItems: 'center', gap: 12 }}>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 13, fontWeight: 700, color, fontFamily: 'Montserrat,sans-serif' }}>{PAID_LABELS[ch] || ch}</div>
                {setup?.objectivo_campanha && <div style={{ fontSize: 11, color: 'var(--text-muted,#64748b)', marginTop: 2 }}>{setup.objectivo_campanha}</div>}
              </div>
              {setup?.budget_sugerido?.percentagem_total && (
                <div style={{ fontSize: 11, fontWeight: 700, padding: '3px 10px', borderRadius: 99, background: col+'14', color: col, fontFamily: 'monospace' }}>
                  {setup.budget_sugerido.percentagem_total}% budget
                </div>
              )}
            </div>

            {/* Ad Sets (audiences) */}
            {audiencias.length > 0 && (
              <div style={{ padding: '12px 20px', borderBottom: '1px solid var(--border,#e2e8f0)', display: 'flex', flexWrap: 'wrap', gap: 6 }}>
                <div style={{ ...lSt, width: '100%', marginBottom: 8 }}>Ad Sets</div>
                {audiencias.map((aud, i) => (
                  <div key={i} style={{ padding: '6px 12px', borderRadius: 8, background: 'var(--bg-sunken,#f8fafc)', border: '1px solid var(--border,#e2e8f0)', fontSize: 11 }}>
                    <span style={{ fontWeight: 600, color: col }}>{aud.nome}</span>
                    <span style={{ fontSize: 9, fontWeight: 600, marginLeft: 6, padding: '1px 5px', borderRadius: 3, background: col+'18', color: col, fontFamily: 'monospace' }}>{aud.tipo}</span>
                    {aud.descricao && <div style={{ fontSize: 10, color: 'var(--text-muted,#64748b)', marginTop: 2 }}>{aud.descricao}</div>}
                  </div>
                ))}
              </div>
            )}

            {/* Ads (comm_plan items com copy e prompt) */}
            <div style={{ padding: '12px 20px' }}>
              <div style={{ ...lSt, marginBottom: 10 }}>Anúncios ({items.length})</div>
              {items.map((item, i) => {
                const itemCopy    = (copy || []).find(c => c.comm_plan_item_index === commPlan.indexOf(item));
                const itemPrompts = (prompts || []).filter(p => p.comm_plan_item_index === commPlan.indexOf(item));
                const bgPrompt    = itemPrompts.find(p => p.tipo === 'background' || p.tipo === 'video_produto');
                const compPrompt  = itemPrompts.find(p => p.tipo === 'composicao' || p.tipo === 'composicao_video');

                return (
                  <div key={i} style={{ border: '1px solid var(--border,#e2e8f0)', borderRadius: 8, padding: '12px 14px', marginBottom: 8, background: i % 2 === 0 ? 'var(--bg-sunken,#f8fafc)' : 'var(--bg-elev,#fff)' }}>
                    <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10, marginBottom: 8 }}>
                      <div style={{ width: 20, height: 20, borderRadius: 4, background: col+'18', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 9, fontWeight: 700, color: col, fontFamily: 'monospace', flexShrink: 0 }}>{i+1}</div>
                      <div style={{ flex: 1 }}>
                        <div style={{ fontSize: 11.5, fontWeight: 700, color: 'var(--text,#1d2e38)', fontFamily: 'var(--font-mono, monospace)' }}>{item.titulo || `Ad ${i+1}`}</div>
                        {item.subtitulo && <div style={{ fontSize: 11.5, color: 'var(--text,#1d2e38)', marginTop: 2, fontWeight: 600 }}>{item.subtitulo}</div>}
                        <div style={{ fontSize: 10, color: 'var(--text-dim,#94a3b8)', marginTop: 2 }}>{item.content_type} · Stage {item.awareness_stage} · {item.angulo_persona}</div>
                      </div>
                      <div style={{ display: 'flex', gap: 4 }}>
                        {itemCopy && <span style={{ fontSize: 9, padding: '2px 6px', borderRadius: 3, background: 'rgba(34,197,94,.12)', color: '#15803d', fontFamily: 'monospace' }}>Copy ✓</span>}
                        {bgPrompt && <span style={{ fontSize: 9, padding: '2px 6px', borderRadius: 3, background: `${col}14`, color: col, fontFamily: 'monospace' }}>{bgPrompt.plataforma || 'Visual'} ✓</span>}
                      </div>
                    </div>

                    {/* Hook */}
                    {item.hook && <div style={{ fontSize: 11, color: 'var(--text,#1d2e38)', fontStyle: 'italic', marginBottom: 8, paddingLeft: 30 }}>"{item.hook}"</div>}

                    {/* Copy aprovado */}
                    {itemCopy && (
                      <div style={{ paddingLeft: 30, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 8 }}>
                        {itemCopy.headline && <div><div style={lSt}>Headline</div><div style={{ fontSize: 12, color: 'var(--text,#1d2e38)', fontWeight: 600 }}>{itemCopy.headline}</div></div>}
                        {itemCopy.primary_text && <div><div style={lSt}>Primary Text</div><div style={{ fontSize: 11, color: 'var(--text,#1d2e38)', lineHeight: 1.4 }}>{itemCopy.primary_text?.slice(0, 80)}{itemCopy.primary_text?.length > 80 ? '…' : ''}</div></div>}
                      </div>
                    )}

                    {/* Prompt visual */}
                    {bgPrompt && (
                      <div style={{ paddingLeft: 30 }}>
                        <div style={lSt}>{bgPrompt.plataforma} — {bgPrompt.tipo}</div>
                        <div style={{ fontSize: 10, color: 'var(--text-muted,#64748b)', lineHeight: 1.5 }}>{bgPrompt.prompt_texto?.slice(0,120)}…</div>
                      </div>
                    )}
                  </div>
                );
              })}
            </div>
          </div>
        );
      })}
    </div>
  );
};

// Awareness stages (Eugene Schwartz) — usado pelo conceito para mapear personas ↔ mensagens
const AWARENESS_COLOR = {
  1: '#94a3b8',  // Unaware
  2: '#ea580c',  // Problem-Aware
  3: '#d97706',  // Solution-Aware
  4: '#3859D0',  // Product-Aware
  5: '#059669',  // Most Aware
};
const AWARENESS_LABEL = {
  1: 'Unaware', 2: 'Problem', 3: 'Solution', 4: 'Product', 5: 'Most Aware',
};
const toAwarenessNum = (v) => {
  if (v == null) return null;
  if (typeof v === 'number') return v;
  const s = String(v).toLowerCase().trim();
  if (!s) return null;
  if (s.includes('unaware'))  return 1;
  if (s.includes('problem'))  return 2;
  if (s.includes('solution')) return 3;
  if (s.includes('product'))  return 4;
  if (s.includes('most'))     return 5;
  const n = parseInt(s, 10);
  return isNaN(n) ? null : n;
};
const StageBadge = ({ stage, n }) => {
  const s = n || stage;
  if (!s) return null;
  const num   = typeof s === 'number' ? s : toAwarenessNum(s);
  const label = AWARENESS_LABEL[num] || String(s);
  const color = AWARENESS_COLOR[num] || '#94a3b8';
  return (
    <span style={{
      fontSize: 9, fontWeight: 700, padding: '2px 7px', borderRadius: 99,
      background: color + '15', color, border: `1px solid ${color}30`,
      fontFamily: 'var(--font-mono, monospace)', letterSpacing: '0.04em', whiteSpace: 'nowrap', flexShrink: 0,
    }}>{label}</span>
  );
};

// ─── _CtdPromessaBaselineCard — inline view + edit para core_promise + comparison_baseline ───
const _CtdPromessaBaselineCard = ({ campanha, proposta, onSaved }) => {
  const cp = proposta?.core_promise || '';
  const cb = proposta?.comparison_baseline || {};
  const hasData = (typeof cp === 'string' && cp.trim().length > 0)
    || (cb && (cb.primary || (Array.isArray(cb.alt_terms) && cb.alt_terms.length) || (Array.isArray(cb.forbidden_confusions) && cb.forbidden_confusions.length)));
  const [editing, setEditing] = React.useState(false);
  const [saving, setSaving]   = React.useState(false);
  const [corePromise, setCorePromise] = React.useState(cp);
  const [basePrimary, setBasePrimary] = React.useState(cb.primary || '');
  const [baseAlt, setBaseAlt]         = React.useState(Array.isArray(cb.alt_terms) ? cb.alt_terms.join(', ') : '');
  const [baseForb, setBaseForb]       = React.useState(Array.isArray(cb.forbidden_confusions) ? cb.forbidden_confusions.join(', ') : '');

  const handleSave = async () => {
    setSaving(true);
    try {
      const _altList  = baseAlt.split(',').map(s => s.trim()).filter(Boolean);
      const _forbList = baseForb.split(',').map(s => s.trim()).filter(Boolean);
      const _baselineOut = (basePrimary.trim() || _altList.length || _forbList.length)
        ? { primary: basePrimary.trim() || null, alt_terms: _altList, forbidden_confusions: _forbList }
        : null;
      await CampAPI.patchConceito(campanha.id, {
        action: 'editar',
        core_promise: corePromise.trim() || null,
        comparison_baseline: _baselineOut,
      });
      setEditing(false);
      if (onSaved) onSaved();
    } catch (e) { alert('Erro ao guardar: ' + (e.message || 'desconhecido')); }
    setSaving(false);
  };

  const handleCancel = () => {
    setCorePromise(cp);
    setBasePrimary(cb.primary || '');
    setBaseAlt(Array.isArray(cb.alt_terms) ? cb.alt_terms.join(', ') : '');
    setBaseForb(Array.isArray(cb.forbidden_confusions) ? cb.forbidden_confusions.join(', ') : '');
    setEditing(false);
  };

  const _labelSt = { fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', fontFamily: 'var(--font-mono, monospace)', marginBottom: 4, display: 'block' };
  const _hintSt  = { fontSize: 10, color: 'var(--text-dim, #94a3b8)', fontFamily: 'var(--font-body, Inter, sans-serif)', marginBottom: 6 };
  const _inpSt   = { width: '100%', padding: '8px 10px', fontSize: 12.5, border: '1px solid var(--border-2, #E3E6EB)', borderRadius: 6, background: 'var(--bg-surface, #fff)', color: 'var(--text, #1d2e38)', fontFamily: 'var(--font-body, Inter, sans-serif)', boxSizing: 'border-box' };

  return (
    <div style={{ background: '#fff', border: '1px solid var(--border, #e2e8f0)', borderRadius: 10, padding: '20px 24px' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, paddingBottom: 14, borderBottom: '1px solid var(--border, #e2e8f0)', marginBottom: 18 }}>
        <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--text-dim, #94a3b8)', fontFamily: 'var(--font-mono, monospace)' }}>Promessa & Baseline</div>
        <span style={{ fontSize: 8, fontWeight: 700, padding: '2px 6px', borderRadius: 3, background: 'rgba(34,197,94,.15)', color: '#166534', fontFamily: 'var(--font-mono, monospace)', letterSpacing: '0.05em' }}>ÂNCORA UNIVERSAL · COPIES</span>
        <div style={{ marginLeft: 'auto', display: 'flex', gap: 6 }}>
          {!editing && (
            <button onClick={() => setEditing(true)} className="btn" style={{ fontSize: 11 }}>
              {hasData ? 'Editar' : 'Preencher'}
            </button>
          )}
          {editing && (
            <>
              <button onClick={handleCancel} disabled={saving} className="btn" style={{ fontSize: 11 }}>Cancelar</button>
              <button onClick={handleSave} disabled={saving} className="btn btn-ai" style={{ fontSize: 11 }}>{saving ? 'A guardar…' : 'Guardar'}</button>
            </>
          )}
        </div>
      </div>

      {!editing && !hasData && (
        <div style={{ padding: '20px', textAlign: 'center', color: 'var(--text-muted, #4a5e7a)', fontSize: 12, fontStyle: 'italic', background: 'var(--bg-subtle, #FAFAFA)', border: '1px dashed var(--border-2, #E3E6EB)', borderRadius: 8 }}>
          Sem promessa/baseline definidos. Clica <b>Preencher</b> para configurar a âncora universal que todos os copies vão respeitar.
        </div>
      )}

      {!editing && hasData && (
        <>
          {corePromise && (
            <div style={{ background: 'var(--bg-app, #F5F6F8)', borderLeft: '3px solid #22c55e', border: '1px solid var(--border, #e2e8f0)', borderRadius: '0 10px 10px 0', padding: '14px 16px', marginBottom: (basePrimary || baseAlt || baseForb) ? 14 : 0 }}>
              <div style={{ ..._labelSt, color: '#22c55e' }}>Core Promise</div>
              <div style={_hintSt}>Tese central destilada — reforçada por todos os copies com ângulos diferentes</div>
              <p style={{ margin: 0, fontSize: 13, color: 'var(--text, #1d2e38)', lineHeight: 1.55, fontFamily: 'var(--font-body, Inter, sans-serif)', fontStyle: 'italic', fontWeight: 500 }}>"{corePromise}"</p>
            </div>
          )}
          {(basePrimary || baseAlt || baseForb) && (
            <div style={{ background: 'var(--bg-app, #F5F6F8)', borderLeft: '3px solid #8b5cf6', border: '1px solid var(--border, #e2e8f0)', borderRadius: '0 10px 10px 0', padding: '14px 16px' }}>
              <div style={{ ..._labelSt, color: '#8b5cf6' }}>Comparison Baseline</div>
              <div style={_hintSt}>Produto/método contra o qual todos os claims relativos são medidos</div>
              {basePrimary && <div style={{ fontSize: 13, color: 'var(--text, #1d2e38)', fontWeight: 600, marginBottom: 6 }}>{basePrimary}</div>}
              {baseAlt && (
                <div style={{ fontSize: 11, color: 'var(--text-muted, #4a5e7a)', marginBottom: 4 }}>
                  <span style={{ fontFamily: 'var(--font-mono, monospace)', fontSize: 9, color: 'var(--text-dim, #94a3b8)', letterSpacing: '0.06em' }}>SINÓNIMOS ACEITÁVEIS: </span>
                  {baseAlt}
                </div>
              )}
              {baseForb && (
                <div style={{ fontSize: 11, color: '#991B1B' }}>
                  <span style={{ fontFamily: 'var(--font-mono, monospace)', fontSize: 9, color: '#dc2626', letterSpacing: '0.06em' }}>⛔ NUNCA CONFUNDIR: </span>
                  {baseForb}
                </div>
              )}
            </div>
          )}
        </>
      )}

      {editing && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div>
            <label style={{ ..._labelSt, color: '#22c55e' }}>Core Promise</label>
            <div style={_hintSt}>Tese central destilada (8-14 palavras) — reforçada por todos os copies com ângulos diferentes</div>
            <textarea value={corePromise} onChange={e => setCorePromise(e.target.value)} rows={2}
              placeholder="Ex: Sustentável = mais rentável, mais fácil, mais competitivo vs vinil polimérico"
              style={{ ..._inpSt, resize: 'vertical' }} />
          </div>
          <div>
            <label style={{ ..._labelSt, color: '#8b5cf6' }}>Comparison Baseline · Primary</label>
            <div style={_hintSt}>Produto/método contra o qual todos os claims relativos comparam (vazio = claims absolutos)</div>
            <input value={basePrimary} onChange={e => setBasePrimary(e.target.value)}
              placeholder="Ex: vinil polimérico convencional" style={_inpSt} />
          </div>
          <div>
            <label style={{ ..._labelSt, color: 'var(--text-dim, #94a3b8)' }}>Alt Terms (CSV)</label>
            <div style={_hintSt}>Sinónimos aceitáveis do baseline, separados por vírgula</div>
            <input value={baseAlt} onChange={e => setBaseAlt(e.target.value)}
              placeholder="vinil polimérico, vinil PVC monomérico" style={_inpSt} />
          </div>
          <div>
            <label style={{ ..._labelSt, color: '#dc2626' }}>Forbidden Confusions (CSV)</label>
            <div style={_hintSt}>Termos proibidos por criarem confusão semântica</div>
            <input value={baseForb} onChange={e => setBaseForb(e.target.value)}
              placeholder="vinil monomérico, PVC-free, plástico genérico" style={_inpSt} />
          </div>
        </div>
      )}
    </div>
  );
};

// Fundação Criativa — 4 campos base do conceito (colunas em conteudo_campanhas)
const BASE_FIELDS_CFG = [
  { key: 'big_idea',       label: 'Big Idea',        desc: 'Ideia central em uma frase',            color: '#3859D0' },
  { key: 'posicionamento', label: 'Posicionamento',  desc: 'Como a marca/produto é percebido',       color: '#7C3AED' },
  { key: 'narrativa',      label: 'Narrativa',       desc: 'História que sustenta a campanha',       color: '#0EA5E9' },
  { key: 'tom_campanha',   label: 'Tom da Campanha', desc: 'Registo comunicacional',                 color: '#059669' },
];

// CONCEITO_STEPS definido no topo do ficheiro (junto ao PhaseLoadingState) —
// steps alinhados com o novo fluxo (fase 2) e filosofia B2B (integração 3-fases).

// Labels de canais (fallback quando CHAN_ABBR não cobre)
const CANAL_LABELS = {
  meta_ads: 'Meta Ads', linkedin_ads: 'LinkedIn Ads', google_ads_search: 'Google Search',
  google_ads_display: 'Google Display', email: 'Email', whatsapp: 'WhatsApp',
  website: 'Website', muppi_led: 'LED/Muppi',
  equipa_consumiveis: 'Equipa Consumíveis', equipa_equipamentos: 'Equipa Equipamentos', equipa_sdr: 'Equipa SDR',
  comercial: 'Equipa comercial', daily_prospect: 'Daily Prospect', distribuidores: 'Distribuidores', logistica: 'Logística',
  instagram: 'Instagram', facebook: 'Facebook', linkedin: 'LinkedIn', google_ads: 'Google Ads',
  site: 'Blog', led: 'LED', tiktok: 'TikTok', youtube: 'YouTube', social: 'Social',
  ads: 'Ads',
  email_interno: 'Email Interno', whatsapp_interno: 'WhatsApp Interno',
  portal_notificacao: 'Portal · Notificação',
};
const CANAL_COLORS = {
  meta_ads: '#0866FF', facebook: '#1877F2', instagram: '#E4405F', linkedin: '#0A66C2',
  linkedin_ads: '#0A66C2', google_ads: '#4285F4', google_ads_search: '#4285F4',
  google_ads_display: '#4285F4', youtube: '#FF0000', tiktok: '#000000',
  email: '#059669', whatsapp: '#25D366', website: '#3859D0', site: '#3859D0',
  muppi_led: '#F59E0B', led: '#F59E0B', social: '#8B5CF6', ads: '#0EA5E9',
  email_interno: '#059669', whatsapp_interno: '#25D366', portal_notificacao: '#3859D0',
};

// Bloco expansível — usado ao longo de várias tabs
const CollapsBlock = ({ title, subtitle, count, defaultOpen = false, accentColor = '#3859D0', children }) => {
  const [open, setOpen] = React.useState(!!defaultOpen);
  return (
    <div style={{ background: '#fff', border: '1px solid var(--border, #e2e8f0)', borderLeft: `3px solid ${accentColor}`, borderRadius: '0 10px 10px 0', overflow: 'hidden' }}>
      <button onClick={() => setOpen(o => !o)} style={{
        width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        background: 'transparent', border: 'none', padding: '12px 18px', cursor: 'pointer',
        fontFamily: 'inherit', color: 'inherit', textAlign: 'left',
      }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 10, flex: 1, minWidth: 0 }}>
          <span style={{ fontSize: 12, fontWeight: 700, color: accentColor, textTransform: 'uppercase', letterSpacing: '0.06em', fontFamily: 'var(--font-mono, monospace)' }}>{title}</span>
          {count    && <span style={{ fontSize: 10, fontWeight: 600, padding: '1px 8px', borderRadius: 99, background: `${accentColor}15`, color: accentColor, fontFamily: 'var(--font-mono, monospace)' }}>{count}</span>}
          {subtitle && <span style={{ fontSize: 11, color: 'var(--text-muted, #64748b)', fontStyle: 'italic' }}>{subtitle}</span>}
        </div>
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke={accentColor} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ transform: open ? 'rotate(90deg)' : 'rotate(0deg)', transition: 'transform 150ms', flexShrink: 0 }}>
          <polyline points="9 18 15 12 9 6"/>
        </svg>
      </button>
      {open && <div>{children}</div>}
    </div>
  );
};

// Timeline vertical simples — usada por CollapsBlock "Plano de Comunicação"
const CommPlanTimeline = ({ items = [], timelineStart = null }) => {
  if (!items.length) return null;
  const fmtDate = (d) => { try { return new Date(d).toLocaleDateString('pt-PT', { day: '2-digit', month: 'short' }); } catch { return d; } };
  return (
    <div style={{ padding: '10px 18px 18px' }}>
      {timelineStart && (
        <div style={{ fontSize: 10, color: '#94a3b8', fontFamily: 'var(--font-mono, monospace)', marginBottom: 8 }}>
          Arranque: {fmtDate(timelineStart)}
        </div>
      )}
      <div style={{ borderLeft: '2px solid #e2e8f0', paddingLeft: 14, display: 'flex', flexDirection: 'column', gap: 12 }}>
        {items.map((it, i) => {
          const canal = it.canal || it.channel || '—';
          const label = CANAL_LABELS[canal] || canal;
          const col   = CANAL_COLORS[canal] || '#3859D0';
          const when  = it.planned_date || it.planned_week || it.data || null;
          return (
            <div key={i} style={{ position: 'relative' }}>
              <div style={{ position: 'absolute', left: -21, top: 4, width: 10, height: 10, borderRadius: '50%', background: col, border: '2px solid #fff' }} />
              <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginBottom: 3 }}>
                <span style={{ fontSize: 11, fontWeight: 700, color: col, fontFamily: 'var(--font-mono, monospace)' }}>{label}</span>
                {when && <span style={{ fontSize: 10, color: '#94a3b8', fontFamily: 'var(--font-mono, monospace)' }}>{fmtDate(when)}</span>}
                {it.content_type && <span style={{ fontSize: 10, color: '#94a3b8' }}>· {it.content_type}</span>}
              </div>
              {(it.titulo || it.title || it.hook) && (
                <div style={{ fontSize: 12, color: '#1d2e38', lineHeight: 1.4 }}>{it.titulo || it.title || it.hook}</div>
              )}
              {it.copy_resumo && (
                <div style={{ fontSize: 11, color: '#64748b', marginTop: 3, lineHeight: 1.4, fontStyle: 'italic' }}>{it.copy_resumo}</div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
};

const TabConceitoFull = ({ campanha, onAction, generating, conceitoStep = 0, conceitoMsg = '', scheduleStep = 0, scheduleMsg = '', scheduleResult = null }) => {
  const hasConceito    = !!(campanha?.big_idea || campanha?.proposta_json?.big_idea);
  // Conceito considerado aprovado quando a campanha já passou desta fase (fluxo novo + legado)
  const isApproved     = [
    // Fluxo novo (pós-conceito)
    'orcamento_pendente','orcamento_gerado','orcamento_aprovado',
    'segmentacao_pendente','segmentacao_gerada','segmentacao_aprovada',
    'planeamento_pendente','planeamento_aprovado',
    'funil_pendente','funil_revisto',
    'pending_executive','em_aprovacao',
    // Legado (compat)
    'canais_pendente','canais_gerado','canais_aprovado',
    'copy_pendente','copy_gerado','copy_aprovado',
    'prompts_pendente','prompts_gerado','prompts_aprovado',
    'em_producao','publicado','concluida'
  ].includes(campanha?.status);
  const proposta       = campanha?.proposta_json || {};

  // ── Variantes por mercado (multi-country execution) ──
  const variants       = Array.isArray(proposta.variants_by_market) ? proposta.variants_by_market : [];
  const primaryVariant = variants.find(v => v.is_primary) || variants[0] || null;
  const [activeMarket, setActiveMarket] = React.useState(primaryVariant?.country || null);
  React.useEffect(() => {
    if (primaryVariant?.country && !variants.find(v => v.country === activeMarket)) {
      setActiveMarket(primaryVariant.country);
    }
  }, [primaryVariant?.country, variants.length]);
  const activeVariant  = variants.find(v => v.country === activeMarket) || primaryVariant;
  const useLocal       = variants.length > 1 && activeVariant && !activeVariant.is_primary;

  const allPersonas    = proposta.personas || (proposta.persona_principal ? [proposta.persona_principal] : []);
  const personasPool   = allPersonas.filter(p => p.nome || p.perfil);
  // Match tolerante: normaliza (lower + sem acentos) + compara as primeiras 2 palavras.
  // Resolve "Director de Produção" (PT) ↔ "Director de Producción" (ES) ↔ "Directeur de Production" (FR)
  const _norm2 = s => (s||'').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').split(/[^a-z0-9]+/).filter(Boolean).slice(0,2).join(' ');
  const priorityNames  = Array.isArray(activeVariant?.personas_priorizadas_local) ? activeVariant.personas_priorizadas_local : null;
  const personas = priorityNames && priorityNames.length
    ? priorityNames.map(name => {
        const target = _norm2(name);
        return personasPool.find(p => _norm2(p.nome) === target) || null;
      }).filter(Boolean).concat(
        personasPool.filter(p => !priorityNames.some(n => _norm2(n) === _norm2(p.nome)))
      )
    : personasPool;
  const msgAngles      = useLocal && activeVariant.messaging_angles_local ? activeVariant.messaging_angles_local : (proposta.messaging_angles || []);
  const hooks          = useLocal && activeVariant.hooks_local              ? activeVariant.hooks_local           : (proposta.hooks || null);
  const visualFormats  = proposta.visual_formats_recomendados || null;
  const commPlan       = useLocal && activeVariant.comm_plan_local          ? activeVariant.comm_plan_local       : (proposta.comm_plan || []);
  const adCopy         = useLocal && activeVariant.ad_copy_local            ? activeVariant.ad_copy_local         : (proposta.ad_copy || null);
  const imagePrompts   = proposta.image_prompts || [];
  const videoPrompts   = proposta.video_prompts || [];
  const narrativaProp  = useLocal && activeVariant.narrativa_estrategica_local ? activeVariant.narrativa_estrategica_local : (proposta.narrativa_proposta || null);
  const keyMessage     = useLocal && activeVariant.key_message_local        ? activeVariant.key_message_local     : (proposta.key_message || null);
  const tomLocal       = activeVariant ? (activeVariant.tom_local || proposta.tom_campanha) : proposta.tom_campanha;
  const anchorType     = proposta.anchor_type || null;
  const campanhaAnuncios = proposta.campanha_anuncios || null; // legado — mantido para compat mas não renderizado
  const abordagemCanal   = (useLocal && activeVariant.abordagem_por_canal_local) ? activeVariant.abordagem_por_canal_local : (proposta.abordagem_por_canal || null);
  const needsUpgrade   = hasConceito && msgAngles.length === 0;

  // ── Timer de progresso por step ──
  const stepStartRef    = React.useRef(null);
  const [stepPct, setStepPct] = React.useState(0);
  // Elapsed timer — obrigatório antes de qualquer early return (Rules of Hooks)
  const [conceitoElapsed, setConceitoElapsed] = React.useState(0);
  const conceitoElapsedRef = React.useRef(null);

  React.useEffect(() => {
    if (!generating.conceito || conceitoStep <= 0) { setStepPct(0); return; }
    stepStartRef.current = Date.now();
    setStepPct(0);
  }, [conceitoStep]);

  React.useEffect(() => {
    if (generating.conceito) {
      setConceitoElapsed(0);
      conceitoElapsedRef.current = setInterval(() => setConceitoElapsed(s => s + 1), 1000);
    } else {
      clearInterval(conceitoElapsedRef.current);
      setConceitoElapsed(0);
    }
    return () => clearInterval(conceitoElapsedRef.current);
  }, [generating.conceito]);

  React.useEffect(() => {
    if (!generating.conceito || conceitoStep <= 0) return;
    const def = CONCEITO_STEPS.find(s => s.step === conceitoStep);
    if (!def?.duration) return;
    const iv = setInterval(() => {
      const elapsed = Date.now() - (stepStartRef.current || Date.now());
      setStepPct(Math.min((elapsed / def.duration) * 90, 90));
    }, 80);
    return () => clearInterval(iv);
  }, [conceitoStep, generating.conceito]);

  const totalSteps   = CONCEITO_STEPS.length;
  const doneSteps    = CONCEITO_STEPS.filter(s => s.step < conceitoStep).length;
  const overallPct   = Math.round(((doneSteps + stepPct / 100) / totalSteps) * 100);

  // ── Loading state — ANTES do empty state check ──
  if (generating.conceito) {
    const fmtElapsed = (s) => s < 60 ? `${s}s` : `${Math.floor(s/60)}m${String(s%60).padStart(2,'0')}s`;
    const passo = CONCEITO_STEPS.find(s => s.step === conceitoStep);
    const mensagem = conceitoMsg || passo?.label || 'A gerar conceito…';
    return (
      <div>
        <div style={{ padding: 16, background: 'linear-gradient(135deg, #eef2ff 0%, #f5f3ff 100%)', border: '1px solid #c7d2fe', borderRadius: 8 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10, gap: 12 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, minWidth: 0, flex: 1 }}>
              <div style={{ display: 'flex', alignItems: 'flex-end', gap: 2, height: 20, flexShrink: 0 }}>
                {[0, 1, 2, 3, 4].map(i => (
                  <div key={i} style={{ width: 3, height: 16, background: '#6366f1', borderRadius: 2, animation: 'ctdWave 1s ease-in-out infinite', animationDelay: `${i * 0.12}s`, transformOrigin: 'bottom' }} />
                ))}
              </div>
              <div style={{ minWidth: 0 }}>
                <div style={{ fontSize: 13, color: '#3730a3', fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{mensagem}</div>
                {passo?.label && conceitoMsg && passo.label !== conceitoMsg && (
                  <div style={{ fontSize: 11, color: '#6366f1', marginTop: 2 }}>{passo.label}</div>
                )}
              </div>
            </div>
            <div style={{ fontSize: 12, color: '#6366f1', fontFamily: 'var(--font-mono, monospace)', fontWeight: 600, flexShrink: 0 }}>
              {conceitoStep > 0 ? `${conceitoStep}/${totalSteps} · ` : ''}{overallPct}% · {fmtElapsed(conceitoElapsed)}
            </div>
          </div>
          <div style={{ height: 6, background: '#e0e7ff', borderRadius: 3, overflow: 'hidden' }}>
            <div style={{ width: `${overallPct}%`, height: '100%', background: 'linear-gradient(90deg, #6366f1, #8b5cf6)', transition: 'width 0.3s ease' }} />
          </div>
          <style>{`@keyframes ctdWave { 0%,100%{transform:scaleY(0.3)} 50%{transform:scaleY(1)} }`}</style>
        </div>
      </div>
    );
  }

  if (!hasConceito) return (
    <PhaseEmptyState
      label="Conceito · fase 2"
      title="Ainda sem conceito criativo"
      description="Decisão criativa a partir do briefing e da estratégia aprovada. Big idea, um ângulo por USP, personas, hooks, direcção por canal e a lista de peças. A mensagem-chave e o tom vêm da campanha. O Planeamento põe as datas. O copy e os prompts nascem na produção."
      ctaLabel="Gerar Conceito →"
      onCta={() => onAction('generateConceito')}
      loading={!!generating.conceito}
    />
  );

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>

      {/* Header status + actions — padrão consistente com Estratégia */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 14px', background: 'var(--bg-surface,#fff)', borderRadius: 'var(--radius-md,8px)', boxShadow: 'var(--shadow-card)' }}>
        <span style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', padding: '3px 8px', borderRadius: 'var(--radius-xs,4px)', background: isApproved ? 'var(--green-100,#E1F7E6)' : 'var(--dd-blue-100,#EBEFF9)', color: isApproved ? 'var(--green-700,#1F8A52)' : 'var(--dd-primary-600,#3859D0)' }}>
          {isApproved ? 'Aprovado' : 'Gerado — aguarda aprovação'}
        </span>
        <span style={{ fontSize: 11, color: 'var(--fg-3, var(--text-muted))' }}>
          {msgAngles.length} ângulo{msgAngles.length !== 1 ? 's' : ''} · {personas.length} persona{personas.length !== 1 ? 's' : ''}
          {variants.length > 1 && ` · ${variants.length} mercados`}
          {campanha?.conceito_generated_at && ` · gerado ${new Date(campanha.conceito_generated_at).toLocaleString('pt-PT')}`}
          {campanha?.conceito_manually_edited_at && ` · editado ${new Date(campanha.conceito_manually_edited_at).toLocaleDateString('pt-PT')}`}
        </span>
        {isApproved && campanha?.conceito_approved_at && (
          <span style={{ fontSize: 11, color: 'var(--green-700, #1F8A52)' }}>
            · aprovada {new Date(campanha.conceito_approved_at).toLocaleString('pt-PT')}{campanha.conceito_approved_by ? ` por ${String(campanha.conceito_approved_by).split('@')[0]}` : ''}
          </span>
        )}
        <div style={{ marginLeft: 'auto', display: 'flex', gap: 6, alignItems: 'center' }}>
          <button
            onClick={() => onAction && onAction('generateConceito')}
            disabled={!!generating.conceito}
            className="btn"
            style={{ height: 28, padding: '0 12px', fontSize: 12 }}
            title="Regenerar conceito — invalida a aprovação"
          >Regenerar</button>
          {!isApproved && (
            <button onClick={() => onAction && onAction('aprovarConceito')} disabled={!!generating.approve} className="btn btn-ai" style={{ height: 28, padding: '0 14px', fontSize: 12 }}>
              Aprovar Conceito
            </button>
          )}
        </div>
      </div>

      {/* ── Chip picker por mercado (só quando há > 1 variante) ── */}
      {variants.length > 1 && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 14px', background: '#fff', border: '1px solid #ECEFF5', borderRadius: 10, boxShadow: 'var(--shadow-card, 0 1px 2px rgba(15,23,42,0.04))' }}>
          <span style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'var(--fg-3,#64748b)', fontFamily: 'var(--font-mono, monospace)' }}>Mercado</span>
          <div style={{ display: 'flex', gap: 6 }}>
            {variants.map(v => {
              const active = v.country === activeMarket;
              return (
                <button
                  key={v.country}
                  onClick={() => setActiveMarket(v.country)}
                  style={{
                    padding: '5px 12px', borderRadius: 999,
                    border: `1px solid ${active ? 'var(--dd-primary-600,#3859D0)' : '#ECEFF5'}`,
                    background: active ? 'var(--dd-primary-600,#3859D0)' : '#fff',
                    color: active ? '#fff' : 'var(--text,#1d2e38)',
                    fontSize: 12, fontWeight: 600, cursor: 'pointer',
                    fontFamily: 'var(--font-body, Inter, sans-serif)',
                    display: 'flex', alignItems: 'center', gap: 6,
                  }}
                >
                  {v.country}
                  {v.is_primary && (
                    <span style={{ fontSize: 9, fontWeight: 700, padding: '1px 5px', borderRadius: 3, background: active ? 'rgba(255,255,255,.2)' : 'rgba(56,89,208,.1)', color: active ? '#fff' : 'var(--dd-primary-600,#3859D0)', letterSpacing: '0.05em' }}>PRIMÁRIO</span>
                  )}
                </button>
              );
            })}
          </div>
          <span style={{ marginLeft: 'auto', fontSize: 11, color: 'var(--fg-3,#64748b)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>
            Conceito anterior com variantes por mercado. A geração nova fica em português de Portugal. A adaptação de idioma é a fase Idiomas.
          </span>
        </div>
      )}

      {/* ── Banner de aprovação ── */}
      {isApproved && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '14px 18px', background: 'rgba(21,128,61,.06)', border: '1px solid rgba(21,128,61,.25)', borderRadius: 10 }}>
          <div style={{ width: 32, height: 32, borderRadius: '50%', background: 'rgba(21,128,61,.12)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#15803d" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
          </div>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 13, fontWeight: 700, color: '#15803d', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>Conceito aprovado</div>
            <div style={{ fontSize: 11, color: '#166534', fontFamily: 'var(--font-body, Inter, sans-serif)', marginTop: 1 }}>A estratégia criativa está confirmada. Avança para o setup de canais.</div>
          </div>
        </div>
      )}

      {/* ── Upgrade banner ── */}
      {needsUpgrade && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '12px 16px', background: '#fffbeb', border: '1px solid #fde68a', borderRadius: 10 }}>
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#d97706" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 12, fontWeight: 600, color: '#92400e', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>Conceito incompleto — regeneração necessária</div>
            <div style={{ fontSize: 11, color: '#b45309', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>Faltam personas, ângulos de mensagem e hooks.</div>
          </div>
          <button onClick={() => onAction('generateConceito')} disabled={generating.conceito} className="btn" style={{ fontSize: 11, color: '#92400e', borderColor: '#fde68a', background: '#fef3c7', whiteSpace: 'nowrap', flexShrink: 0 }}>
            {generating.conceito ? 'A gerar...' : 'Actualizar estratégia'}
          </button>
        </div>
      )}

      {/* ── 0. Mensagem-chave — hero FormCard ── */}
      {keyMessage && (
        <div style={{ background: '#fff', border: '1px solid rgba(56,89,208,.25)', borderLeft: '4px solid var(--ai-500, #3859D0)', borderRadius: '0 10px 10px 0', padding: '20px 24px' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
            <span style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--ai-500, #3859D0)', fontFamily: 'var(--font-mono, monospace)' }}>Mensagem-chave</span>
            {useLocal && (
              <span style={{ fontSize: 9, fontWeight: 700, padding: '2px 8px', borderRadius: 99, background: 'rgba(56,89,208,.1)', color: 'var(--dd-primary-600,#3859D0)', fontFamily: 'var(--font-mono, monospace)', letterSpacing: '0.05em' }}>
                {activeMarket} · LOCAL
              </span>
            )}
            {anchorType && (
              <span style={{ fontSize: 9, fontWeight: 700, padding: '2px 8px', borderRadius: 99, background: anchorType === 'pain' ? 'rgba(220,38,38,.1)' : 'rgba(16,185,129,.1)', color: anchorType === 'pain' ? '#dc2626' : '#059669', fontFamily: 'var(--font-mono, monospace)', letterSpacing: '0.05em' }}>
                {anchorType === 'pain' ? 'PAIN ANCHOR' : 'DESIRE ANCHOR'}
              </span>
            )}
          </div>
          <p style={{ margin: 0, fontSize: 16, fontWeight: 600, color: 'var(--text, #1d2e38)', lineHeight: 1.55, fontFamily: 'var(--font-display, Montserrat, sans-serif)' }}>{keyMessage}</p>
        </div>
      )}

      {/* ── 1. Ângulos de Mensagem — FormCard wrapper ── */}
      {msgAngles.length > 0 && (
        <div style={{ background: '#fff', border: '1px solid var(--border, #e2e8f0)', borderRadius: 10, padding: '20px 24px' }}>
          {/* Título secção */}
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, paddingBottom: 14, borderBottom: '1px solid var(--border, #e2e8f0)', marginBottom: 18 }}>
            <span style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--text-dim, #94a3b8)', fontFamily: 'var(--font-mono, monospace)' }}>Ângulos de Mensagem</span>
            <span style={{ fontSize: 9, fontWeight: 700, background: 'rgba(0,0,0,.05)', borderRadius: 99, padding: '1px 7px', color: 'var(--text-dim, #94a3b8)', fontFamily: 'var(--font-mono, monospace)' }}>{msgAngles.length}</span>
          </div>
          {/* Cards — sem stripe no topo, border completo */}
          <div style={{ display: 'grid', gridTemplateColumns: msgAngles.length === 1 ? '1fr' : 'repeat(auto-fit, minmax(320px, 1fr))', gap: 14 }}>
            {msgAngles.map((a, i) => {
              const stg = toAwarenessNum(a.awareness_stage) || a.awareness_stage;
              const stgColor = AWARENESS_COLOR[stg] || '#94a3b8';
              return (
                <div key={i} style={{ background: 'var(--bg-app, #F5F6F8)', border: '1px solid var(--border, #e2e8f0)', borderRadius: 10, padding: '16px 18px', display: 'flex', flexDirection: 'column', gap: 10 }}>
                  {/* Persona + Stage badge no mesmo row */}
                  <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 8 }}>
                    <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--text, #1d2e38)', fontFamily: 'var(--font-body, Inter, sans-serif)', lineHeight: 1.4 }}>{a.persona}</span>
                    <StageBadge stage={stg} />
                  </div>
                  {/* Quote */}
                  <p style={{ margin: 0, fontSize: 13, fontWeight: 600, color: 'var(--text, #1d2e38)', lineHeight: 1.55, fontStyle: 'italic', fontFamily: 'var(--font-display, Montserrat, sans-serif)' }}>"{a.angulo}"</p>
                  {/* Rationale */}
                  {a.rationale && <p style={{ margin: 0, fontSize: 11, color: 'var(--text-muted, #64748b)', lineHeight: 1.6, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{a.rationale}</p>}
                  {/* Objecções — destaque âmbar */}
                  {a.objections && a.objections.length > 0 && (
                    <div style={{ background: 'rgba(245,158,11,.06)', border: '1px solid rgba(245,158,11,.2)', borderRadius: 7, padding: '10px 12px', marginTop: 2 }}>
                      <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em', color: '#d97706', fontFamily: 'var(--font-mono, monospace)', marginBottom: 6 }}>⚠ Objecções a antecipar</div>
                      <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                        {a.objections.map((obj, oi) => (
                          <div key={oi} style={{ fontSize: 11, color: 'var(--text-muted, #64748b)', lineHeight: 1.5, display: 'flex', gap: 6, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>
                            <span style={{ color: '#d97706', flexShrink: 0 }}>—</span>{obj}
                          </div>
                        ))}
                      </div>
                    </div>
                  )}
                </div>
              );
            })}
          </div>
        </div>
      )}

      {/* ── 2. Personas — FormCard wrapper ── */}
      {personas.length > 0 && (
        <div style={{ background: '#fff', border: '1px solid var(--border, #e2e8f0)', borderRadius: 10, padding: '20px 24px' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, paddingBottom: 14, borderBottom: '1px solid var(--border, #e2e8f0)', marginBottom: 18 }}>
            <span style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--text-dim, #94a3b8)', fontFamily: 'var(--font-mono, monospace)' }}>Personas</span>
            <span style={{ fontSize: 9, fontWeight: 700, background: 'rgba(0,0,0,.05)', borderRadius: 99, padding: '1px 7px', color: 'var(--text-dim, #94a3b8)', fontFamily: 'var(--font-mono, monospace)' }}>{personas.length}</span>
            <span style={{ fontSize: 8, fontWeight: 700, padding: '2px 6px', borderRadius: 3, background: 'rgba(148,163,184,.15)', color: 'var(--text-dim,#64748b)', fontFamily: 'var(--font-mono, monospace)', letterSpacing: '0.05em' }}>UNIVERSAL · cross-market</span>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: 14 }}>
            {personas.map((p, i) => {
              const initials = ((p.nome || p.perfil || '?').split(' ').slice(0,2).map(w => w[0]).join('').toUpperCase()) || '?';
              return (
                <div key={i} style={{ background: 'var(--bg-app, #F5F6F8)', border: '1px solid var(--border, #e2e8f0)', borderRadius: 10, padding: '16px 18px', display: 'flex', flexDirection: 'column', gap: 10 }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                    <div style={{ width: 36, height: 36, borderRadius: '50%', background: 'var(--ai-500, #3859D0)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                      <span style={{ fontSize: 12, fontWeight: 700, color: '#fff', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{initials}</span>
                    </div>
                    <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--text, #1d2e38)', lineHeight: 1.3, fontFamily: 'var(--font-display, Montserrat, sans-serif)' }}>{p.nome || 'Persona ' + (i+1)}</span>
                  </div>
                  {p.perfil && <p style={{ margin: 0, fontSize: 11, color: 'var(--text-muted, #64748b)', lineHeight: 1.6, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{p.perfil}</p>}
                  {p.como_vive_a_dor && <div style={{ fontSize: 11, color: '#c2410c', lineHeight: 1.5, borderTop: '1px solid var(--border, #e2e8f0)', paddingTop: 8, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{p.como_vive_a_dor}</div>}
                  {p.deepest_desire && <div style={{ fontSize: 11, color: '#059669', lineHeight: 1.5, borderTop: '1px solid var(--border, #e2e8f0)', paddingTop: 8, fontFamily: 'var(--font-body, Inter, sans-serif)' }}><span style={{ fontWeight: 700 }}>Desejo: </span>{p.deepest_desire}</div>}
                </div>
              );
            })}
          </div>
        </div>
      )}

      {/* ── 3. Fundação Criativa — FormCard wrapper ── */}
      {BASE_FIELDS_CFG.some(f => campanha[f.key]) && (
        <div style={{ background: '#fff', border: '1px solid var(--border, #e2e8f0)', borderRadius: 10, padding: '20px 24px' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, paddingBottom: 14, borderBottom: '1px solid var(--border, #e2e8f0)', marginBottom: 18 }}>
            <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--text-dim, #94a3b8)', fontFamily: 'var(--font-mono, monospace)' }}>Fundação Criativa</div>
            <span style={{ fontSize: 8, fontWeight: 700, padding: '2px 6px', borderRadius: 3, background: 'rgba(148,163,184,.15)', color: 'var(--text-dim,#64748b)', fontFamily: 'var(--font-mono, monospace)', letterSpacing: '0.05em' }}>UNIVERSAL · cross-market</span>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
            {BASE_FIELDS_CFG.map(f => {
              const isTom = f.key === 'tom_campanha';
              const value = isTom ? tomLocal : campanha[f.key];
              if (!value) return null;
              const localBadge = isTom && useLocal;
              return (
                <div key={f.key} style={{ background: 'var(--bg-app, #F5F6F8)', borderLeft: `3px solid ${f.color}`, border: '1px solid var(--border, #e2e8f0)', borderRadius: '0 10px 10px 0', padding: '14px 16px' }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 2 }}>
                    <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: f.color, fontFamily: 'var(--font-mono, monospace)' }}>{f.label}</div>
                    {localBadge && (
                      <span style={{ fontSize: 8, fontWeight: 700, padding: '1px 5px', borderRadius: 3, background: 'rgba(56,89,208,.1)', color: 'var(--dd-primary-600,#3859D0)', fontFamily: 'var(--font-mono, monospace)', letterSpacing: '0.05em' }}>{activeMarket}</span>
                    )}
                  </div>
                  <div style={{ fontSize: 9, color: 'var(--text-dim, #94a3b8)', fontFamily: 'var(--font-body, Inter, sans-serif)', marginBottom: 8 }}>{f.desc}</div>
                  <p style={{ margin: 0, fontSize: 12.5, color: 'var(--text, #1d2e38)', lineHeight: 1.65, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{value}</p>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {/* ── 3b. Promessa & Baseline — sempre visível, com edição inline ── */}
      <_CtdPromessaBaselineCard
        campanha={campanha}
        proposta={proposta}
        onSaved={() => onAction && onAction('refreshCampanha')}
      />
      {/* ── /Promessa & Baseline ── */}

      {/* ── 4. Abordagem por Canal — direcção criativa por canal do briefing ── */}
      {(() => {
        const abordagem = abordagemCanal;
        // Backward compat: se ainda for do formato antigo (campanha_anuncios), tenta mostrar
        const canaisCfg = abordagem && Object.keys(abordagem).length > 0 ? abordagem : null;
        if (!canaisCfg) return null;
        return (
          <CollapsBlock title={useLocal ? `Abordagem por Canal · ${activeMarket}` : 'Abordagem por Canal'} count={Object.keys(canaisCfg).length + ' canais'} defaultOpen={true} accentColor="var(--ai-500, #3859D0)">
            <div style={{ padding: '0 18px 16px', display: 'flex', flexDirection: 'column', gap: 18 }}>
              {Object.entries(canaisCfg).map(([ch, cfg]) => {
                const col = CANAL_COLORS[ch] || 'var(--ai-500, #3859D0)';
                const label = CANAL_LABELS[ch] || CANAL_LABEL[ch] || ch;
                const isEmail = ch === 'email';
                const isWebsite = ch === 'website';
                const isWA = ch === 'whatsapp';
                return (
                  <div key={ch} style={{ paddingTop: 14, borderTop: '1px solid var(--border-light, #f0f2f7)' }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
                      <div style={{ width: 10, height: 10, borderRadius: '50%', background: col, flexShrink: 0 }} />
                      <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--text, #1d2e38)', fontFamily: 'var(--font-display, Montserrat, sans-serif)' }}>{label}</span>
                      {cfg.objectivo && (
                        <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 99, background: col + '14', color: col, fontFamily: 'var(--font-mono)', fontWeight: 600 }}>
                          {cfg.objectivo}
                        </span>
                      )}
                      {cfg.n_ads_previstos && (
                        <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 99, background: 'var(--bg-sunken)', color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>
                          {cfg.n_ads_previstos} anúncios
                        </span>
                      )}
                      {isWebsite && cfg.n_artigos && (
                        <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 99, background: 'var(--bg-sunken)', color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>
                          {cfg.n_artigos} artigos
                        </span>
                      )}
                    </div>

                    {/* Email — sequência */}
                    {isEmail && Array.isArray(cfg.sequencia) && cfg.sequencia.length > 0 && (
                      <div style={{ marginLeft: 18, marginBottom: 10 }}>
                        {cfg.sequencia.map((eml, i) => (
                          <div key={i} style={{ display: 'flex', gap: 10, alignItems: 'flex-start', padding: '8px 12px', marginBottom: 5, background: 'var(--bg-sunken)', borderRadius: 6, borderLeft: `3px solid ${col}` }}>
                            <span style={{ fontSize: 10, fontWeight: 700, padding: '2px 7px', borderRadius: 4, background: col + '18', color: col, fontFamily: 'var(--font-mono)', textTransform: 'uppercase', flexShrink: 0 }}>
                              {(eml.tipo || '').replace('_',' ')}{eml.n && eml.n > 1 ? ` ×${eml.n}` : ''}
                            </span>
                            <span style={{ fontSize: 12, color: 'var(--text)', lineHeight: 1.5 }}>{eml.objectivo}</span>
                          </div>
                        ))}
                      </div>
                    )}

                    {/* Website — página produto + tipo */}
                    {isWebsite && (
                      <div style={{ marginLeft: 18, marginBottom: 10, padding: '10px 14px', background: 'var(--bg-sunken)', borderRadius: 8, borderLeft: `3px solid ${col}` }}>
                        {cfg.tipo && <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--text)', marginBottom: 4 }}>Tipo: {cfg.tipo}</div>}
                        {cfg.produto_pagina_ref && (
                          <div style={{ fontSize: 11, color: 'var(--text-muted)' }}>Página produto: <a href={cfg.produto_pagina_ref} target="_blank" rel="noreferrer" style={{ color: col }}>{cfg.produto_pagina_ref}</a></div>
                        )}
                      </div>
                    )}

                    {/* WhatsApp — tipo */}
                    {isWA && cfg.tipo && (
                      <div style={{ marginLeft: 18, marginBottom: 8, fontSize: 11, color: 'var(--text-muted)' }}>
                        Tipo: <strong style={{ color: 'var(--text)' }}>{cfg.tipo}</strong>
                      </div>
                    )}

                    {/* Formatos (canais Ads) */}
                    {Array.isArray(cfg.formatos) && cfg.formatos.length > 0 && (
                      <div style={{ marginLeft: 18, marginBottom: 8, display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                        {cfg.formatos.map((f, i) => (
                          <span key={i} style={{ fontSize: 10, fontWeight: 600, padding: '2px 8px', borderRadius: 99, background: col + '10', color: col, fontFamily: 'var(--font-mono)' }}>{f}</span>
                        ))}
                      </div>
                    )}

                    {/* Abordagem criativa */}
                    {cfg.abordagem_criativa && (
                      <div style={{ marginLeft: 18, marginBottom: 6, fontSize: 12, color: 'var(--text)', lineHeight: 1.55 }}>
                        {cfg.abordagem_criativa}
                      </div>
                    )}

                    {Array.isArray(cfg.pecas) && cfg.pecas.length > 0 && (
                      <div style={{ marginLeft: 18, display: 'flex', flexDirection: 'column', gap: 4 }}>
                        {cfg.pecas.map((peca, i) => (
                          <div key={i} style={{ fontSize: 12, color: 'var(--text, #1d2e38)', lineHeight: 1.45 }}>
                            <span style={{ fontSize: 10, fontWeight: 700, color: col, fontFamily: 'var(--font-mono, monospace)', marginRight: 6 }}>{peca.titulo}</span>
                            {peca.angulo || ''}
                          </div>
                        ))}
                      </div>
                    )}

                    {/* Notas */}
                    {cfg.notas && (
                      <div style={{ marginLeft: 18, fontSize: 11, color: 'var(--text-dim, #94a3b8)', fontStyle: 'italic', paddingLeft: 10, borderLeft: `2px solid ${col}30`, lineHeight: 1.5 }}>
                        {cfg.notas}
                      </div>
                    )}
                  </div>
                );
              })}
            </div>
          </CollapsBlock>
        );
      })()}

      {/* ── 5. Hooks por Canal ── */}
      {hooks && Object.keys(hooks).length > 0 && (
        <CollapsBlock title={'Hooks por Canal'} count={Object.keys(hooks).length + (Object.keys(hooks).length === 1 ? ' canal' : ' canais')} accentColor="#f59e0b">
          <div style={{ padding: '0 18px 16px', display: 'flex', flexDirection: 'column', gap: 16 }}>
            {Object.entries(hooks).map(([canal, hookList]) => {
              const vf = visualFormats && visualFormats[canal];
              return (
                <div key={canal} style={{ paddingTop: 14 }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 }}>
                    <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--ai-500, #3859D0)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{CANAL_LABEL[canal] || canal}</div>
                    {vf && vf.length > 0 && (
                      <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
                        {vf.map((fmt, fi) => <span key={fi} style={{ fontSize: 9, padding: '1px 6px', borderRadius: 99, background: 'rgba(16,185,129,.1)', color: '#059669', fontWeight: 600, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{fmt}</span>)}
                      </div>
                    )}
                  </div>
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
                    {(hookList || []).map((h, i) => {
                      const txt   = typeof h === 'object' ? h.texto : h;
                      const tipo  = typeof h === 'object' ? h.tipo  : null;
                      const stage = typeof h === 'object' ? (toAwarenessNum(h.target_stage) || h.target_stage) : null;
                      const stgColor = AWARENESS_COLOR[stage] || '#94a3b8';
                      return (
                        <div key={i} style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}>
                          <span style={{ fontSize: 9, fontWeight: 700, color: 'var(--text-dim, #94a3b8)', minWidth: 18, paddingTop: 2, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{i+1}.</span>
                          <div style={{ flex: 1 }}>
                            <div style={{ display: 'flex', gap: 6, marginBottom: 3, flexWrap: 'wrap' }}>
                              {tipo  && <span style={{ fontSize: 9, fontWeight: 700, color: 'var(--text-dim, #94a3b8)', textTransform: 'uppercase', letterSpacing: '0.06em', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{tipo}</span>}
                              {stage && <span style={{ fontSize: 9, fontWeight: 700, padding: '1px 6px', borderRadius: 99, background: stgColor + '18', color: stgColor, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>S{stage}</span>}
                            </div>
                            <span style={{ fontSize: 12, color: 'var(--text-muted, #64748b)', lineHeight: 1.55, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{txt}</span>
                          </div>
                        </div>
                      );
                    })}
                  </div>
                </div>
              );
            })}
          </div>
        </CollapsBlock>
      )}


      {/* ── 8. Narrativa Estratégica ── */}
      {narrativaProp && (
        <CollapsBlock title={useLocal ? `Narrativa Estratégica · ${activeMarket}` : 'Narrativa Estratégica'} subtitle="Para revisão RL / FC" accentColor="var(--ai-500, #3859D0)">
          <div style={{ padding: '18px 20px' }}>
            <div style={{ fontSize: 13, color: 'var(--text, #1d2e38)', lineHeight: 1.85, whiteSpace: 'pre-wrap', fontFamily: 'var(--font-body, Inter, sans-serif)', borderLeft: '3px solid var(--ai-500, #3859D0)', paddingLeft: 16 }}>{narrativaProp}</div>
          </div>
        </CollapsBlock>
      )}


    </div>
  );
};

const PedirAlteracoesAprovacaoModal = ({ onClose, onConfirm }) => {
  const [notes,    setNotes]    = React.useState('');
  const [revertTo, setRevertTo] = React.useState('conceito');
  // Apenas fases de Campanhas Marketing (Copy/Prompts/Idiomas ficam em Produção de Conteúdos)
  const FASES = [
    { value: 'briefing',     label: 'Briefing' },
    { value: 'estrategia',   label: 'Estratégia' },
    { value: 'conceito',     label: 'Conceito Criativo' },
    { value: 'orcamento',    label: 'Orçamento' },
    { value: 'segmentacao',  label: 'Target' },
    { value: 'planeamento',  label: 'Planeamento' },
    { value: 'funil',        label: 'Funil Multicanal' },
  ];
  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.45)', zIndex: 9000, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24 }}>
      <div style={{ background: '#fff', borderRadius: 14, maxWidth: 520, width: '100%', padding: '28px', boxShadow: '0 20px 60px rgba(0,0,0,.2)' }}>
        <div style={{ fontSize: 16, fontWeight: 700, color: '#d97706', fontFamily: 'var(--font-display,Montserrat,sans-serif)', marginBottom: 16 }}>Pedir Alterações</div>
        <div style={{ marginBottom: 14 }}>
          <label style={{ fontSize: 11, fontWeight: 600, color: '#64748b', fontFamily: 'monospace', textTransform: 'uppercase', display: 'block', marginBottom: 6 }}>Voltar à fase</label>
          <select value={revertTo} onChange={e => setRevertTo(e.target.value)} style={{ width: '100%', padding: '8px 12px', border: '1px solid var(--border,#e2e8f0)', borderRadius: 8, fontSize: 13, outline: 'none', background: '#fff' }}>
            {FASES.map(f => <option key={f.value} value={f.value}>{f.label}</option>)}
          </select>
        </div>
        <div style={{ marginBottom: 20 }}>
          <label style={{ fontSize: 11, fontWeight: 600, color: '#64748b', fontFamily: 'monospace', textTransform: 'uppercase', display: 'block', marginBottom: 6 }}>O que precisa de ser corrigido (obrigatório)</label>
          <textarea value={notes} onChange={e => setNotes(e.target.value)} rows={4} placeholder="Descreve o que precisa de ser corrigido antes de aprovares…" style={{ width: '100%', padding: '8px 12px', border: `1px solid ${notes.length < 10 && notes.length > 0 ? '#dc2626' : 'var(--border,#e2e8f0)'}`, borderRadius: 8, fontSize: 13, outline: 'none', resize: 'vertical', boxSizing: 'border-box', fontFamily: 'var(--font-body,Inter,sans-serif)' }} />
        </div>
        <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end' }}>
          <button className="btn" onClick={onClose}>Cancelar</button>
          <button className="btn" onClick={() => onConfirm(notes, revertTo)} disabled={notes.trim().length < 10} style={{ color: '#d97706', borderColor: '#d97706', opacity: notes.trim().length < 10 ? 0.5 : 1 }}>Confirmar Pedido</button>
        </div>
      </div>
    </div>
  );
};

const RejeitarCampanhaModal = ({ onClose, onConfirm }) => {
  const [notes, setNotes] = React.useState('');
  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.45)', zIndex: 9000, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24 }}>
      <div style={{ background: '#fff', borderRadius: 14, maxWidth: 480, width: '100%', padding: '28px', boxShadow: '0 20px 60px rgba(0,0,0,.2)' }}>
        <div style={{ fontSize: 16, fontWeight: 700, color: '#dc2626', fontFamily: 'var(--font-display,Montserrat,sans-serif)', marginBottom: 8 }}>Rejeitar Campanha</div>
        <div style={{ fontSize: 13, color: '#64748b', marginBottom: 16, lineHeight: 1.55 }}>A campanha voltará ao início (Conceito) para ser refeita. Esta acção não é reversível.</div>
        <div style={{ marginBottom: 20 }}>
          <label style={{ fontSize: 11, fontWeight: 600, color: '#64748b', fontFamily: 'monospace', textTransform: 'uppercase', display: 'block', marginBottom: 6 }}>Motivo (obrigatório)</label>
          <textarea value={notes} onChange={e => setNotes(e.target.value)} rows={4} placeholder="Descreve o motivo da rejeição…" style={{ width: '100%', padding: '8px 12px', border: '1px solid rgba(220,38,38,.3)', borderRadius: 8, fontSize: 13, outline: 'none', resize: 'vertical', boxSizing: 'border-box', fontFamily: 'var(--font-body,Inter,sans-serif)' }} />
        </div>
        <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end' }}>
          <button className="btn" onClick={onClose}>Cancelar</button>
          <button className="btn" onClick={() => onConfirm(notes)} disabled={notes.trim().length < 10} style={{ color: '#dc2626', borderColor: '#dc2626', opacity: notes.trim().length < 10 ? 0.5 : 1 }}>Confirmar Rejeição</button>
        </div>
      </div>
    </div>
  );
};

const TabAprovacao = ({ campanha, onAction, userEmail }) => {
  const isApprover   = canActOnCampaign(userEmail);
  const emailSessao  = String(userEmail || window.currentUser?.email || window.currentUser?.email_profissional || '').toLowerCase();
  const qAprov       = new URLSearchParams(window.location.search);
  const podeSubmeter = isApprover || emailSessao === 'carlos.alves@decal.pt' || qAprov.get('dev') === 'carlos' || qAprov.get('mkt_role') === 'strategist';
  const status       = campanha?.status || '';
  const isPending    = ['pending_executive','em_aprovacao'].includes(status);
  const isInProducao = ['em_producao','publicado'].includes(status);
  const bCol         = _brandColor(campanha?.brand_slug);
  const _brandSlug   = (campanha?.brand_slug || campanha?.briefing?.brand_slug || '').toLowerCase();
  const isMateriais  = new Set(['decal','biond','sensek','alldecor']).has(_brandSlug);

  const [showPedirAlt, setShowPedirAlt] = React.useState(false);
  const [showRejeitar, setShowRejeitar] = React.useState(false);
  const [submitting,   setSubmitting]   = React.useState(false);
  const [sending,      setSending]      = React.useState(false);
  const [boardData,    setBoardData]    = React.useState(null);
  const [boardLoading, setBoardLoading] = React.useState(false);
  const [boardError,   setBoardError]   = React.useState('');
  const [icApi,        setIcApi]        = React.useState(null);
  const [showObjEdit,  setShowObjEdit]  = React.useState(false);
  const [objDraft,     setObjDraft]     = React.useState(null);
  const [objSaving,    setObjSaving]    = React.useState(false);

  // Derivar statuses directamente das colunas de timestamp — sem API calls extra
  const orcStatus  = campanha?.orcamento_approved_at  ? 'approved' : campanha?.orcamento_generated_at  ? 'done' : 'pending';
  const segStatus  = campanha?.segmentacao_approved_at ? 'approved' : campanha?.segmentacao_generated_at ? 'done' : 'pending';
  const planStatus = campanha?.planeamento_approved_at ? 'approved' : campanha?.planeamento_generated_at ? 'done' : 'pending';

  // Fetch dados para o Investment Case (orcamento + segmentacao)
  React.useEffect(() => {
    if (!campanha?.id) return;
    setBoardLoading(true);
    setBoardError('');
    let orcErr = null; let segErr = null;
    Promise.all([
      campApiCall(`/api/marketing/campanhas/${campanha.id}/orcamento`).catch(e => { orcErr = e; return { rows: [] }; }),
      campApiCall(`/api/marketing/campanhas/${campanha.id}/segmentacao`).catch(e => { segErr = e; return { rows: [] }; }),
    ]).then(([orc, seg]) => {
      const totalBudget = (orc.rows || []).reduce((s, r) => s + (parseFloat(r.valor_eur) || 0), 0);
      const segRows = seg.rows || [];
      const emailContacts = segRows.filter(r => r.canal === 'email')
        .reduce((s, r) => s + ((typeof r.audiencia_json === 'object' ? r.audiencia_json?.n_contactos : null) || 0), 0);
      const waContacts = segRows.filter(r => r.canal === 'whatsapp')
        .reduce((s, r) => s + ((typeof r.audiencia_json === 'object' ? r.audiencia_json?.n_contactos : null) || 0), 0);
      const hasMetaAds = segRows.some(r => r.canal === 'meta_ads');
      const metaCountries = [...new Set(segRows.filter(r => r.canal === 'meta_ads').map(r => r.country))].length;
      // Reach real — audience_size string da Meta API (ex: "1400K–1700K")
      const metaAudienceStr = segRows.filter(r => r.canal === 'meta_ads')
        .map(r => r.audiencia_json?.audience_size || r.audiencia_json?.audience_estimate || '').filter(Boolean).join(' + ') || '';
      // Converter "1400K–1700K" em min/max numérico
      const parseKM = s => { const n = parseFloat(s); return s.toUpperCase().includes('M') ? n * 1e6 : s.toUpperCase().includes('K') ? n * 1000 : n; };
      const metaParts = metaAudienceStr.match(/[\d.]+[KMkm]*/g) || [];
      const metaReachMin = metaParts.length > 0 ? parseKM(metaParts[0]) : 0;
      const metaReachMax = metaParts.length > 1 ? parseKM(metaParts[metaParts.length - 1]) : metaReachMin;
      setBoardData({ totalBudget, emailContacts, waContacts, hasMetaAds, metaCountries, metaReachMin, metaReachMax, metaAudienceStr });
      if (orcErr && segErr) setBoardError('Não foi possível carregar orçamento nem targets — Investment Case incompleto');
      else if (orcErr) setBoardError('Não foi possível carregar orçamento — Investment Case parcial');
      else if (segErr) setBoardError('Não foi possível carregar targets — audiências não incluídas');
    }).finally(() => setBoardLoading(false));
  }, [campanha?.id]);

  React.useEffect(() => {
    if (!campanha?.id) return;
    campApiCall(`/api/marketing/campanhas/${campanha.id}/investment-case`)
      .then(setIcApi)
      .catch(() => setIcApi(null));
  }, [campanha?.id]);

  const approvals = campanha?.camp_approvals || [];
  const brief     = campanha?.briefing || {};
  const markets   = campanha?.estrategia_json?.markets || [];
  const commPlan  = campanha?.proposta_json?.comm_plan || [];
  const allChs    = [...new Set(markets.flatMap(m => (m.channel_fit || []).map(c => c.canal)))];
  const chLabels  = { meta_ads:'Meta Ads', email:'Email', linkedin_ads:'LinkedIn Ads', whatsapp:'WhatsApp', website:'Blog/Website', muppi_led:'LED/Muppi', google_ads_search:'Google Search' };
  const usps      = Array.isArray(brief.usps) ? brief.usps : [];
  const pains     = Array.isArray(brief.pain_points) ? brief.pain_points : [];

  const CAMP_PHASES = [
    { id:'briefing',    label:'Briefing',          tab:'briefing',
      status: campanha?.briefing ? 'approved' : 'pending',
      detail: brief.commercial_name || campanha?.titulo || '—' },
    { id:'estrategia',  label:'Estratégia',         tab:'estrategia',
      status: campanha?.estrategia_approved_at ? 'approved' : campanha?.estrategia_json?.markets?.length ? 'done' : 'pending',
      detail: markets.length ? `${markets.map(m=>m.country).join(' + ')} · ${markets.length} mercado(s)` : 'Não gerada' },
    { id:'conceito',    label:'Conceito Criativo',  tab:'conceito',
      status: campanha?.conceito_approved_at ? 'approved' : campanha?.big_idea ? 'done' : 'pending',
      detail: campanha?.big_idea ? campanha.big_idea.slice(0,70)+'…' : 'Não gerado' },
    { id:'orcamento',   label:'Orçamento',           tab:'orcamento',
      status: orcStatus || 'pending',
      detail: orcStatus === 'approved' ? 'Aprovado' : orcStatus === 'done' ? 'Gerado — aguarda aprovação' : 'Não gerado' },
    { id:'segmentacao', label:'Target',              tab:'segmentacao',
      status: segStatus || 'pending',
      detail: segStatus === 'approved' ? 'Confirmado' : segStatus === 'done' ? 'Gerado — aguarda confirmação' : 'Não gerado' },
    { id:'planeamento', label:'Planeamento',         tab:'planeamento',
      status: planStatus || 'pending',
      detail: planStatus === 'approved' ? 'Aprovado' : planStatus === 'partial' ? 'Parcialmente aprovado' : planStatus === 'done' ? 'Gerado — aguarda aprovação' : 'Não gerado' },
    { id:'funil',       label:'Funil Multicanal',    tab:'funil',
      status: markets.length ? (campanha?.planeamento_approved_at ? 'approved' : 'done') : 'pending',
      detail: markets.length ? `${allChs.map(c=>chLabels[c]||c).join(' · ')} · 5 layers` : 'Depende da Estratégia' },
    { id:'sdr_ai',      label:'SDR AI · Ficha',      tab:'sdr',
      status: campanha?.sdr_ai_activated_at ? 'approved' : campanha?.sdr_ficha_md ? 'done' : 'pending',
      detail: campanha?.sdr_ai_activated_at
        ? `Ficha activa v${campanha?.sdr_ficha_version || '?'}${campanha?.sdr_ficha_manual ? ' · manual' : ''}`
        : campanha?.sdr_ficha_md
          ? `Ficha gerada v${campanha?.sdr_ficha_version || '?'} · aguarda activação`
          : 'Ficha não gerada' },
  ];
  // ── Investment Case — dados do simulador + orcamento + segmentacao ─────────
  const PHT50_PRICE = 75; // €/pack — Tabela Mimaki Julho 2026 (PVP — custo real Digidelta será inferior)

  // EQUIP_DB: família de equipamentos com preços e cenários
  // family: modelos da mesma série ordenados do mais barato para o mais caro
  const EQUIP_DB = {
    'TxF150-75':   { pvp: 12800, total: 19103, promo: 8800,  promoPacks: 24, digirent: 395.11, family: ['TxF150-75','TxF300-75','TxF300-1600'] },
    'TxF300-75':   { pvp: 17300, total: null,  promo: 13400, promoPacks: 75, digirent: 316.45, family: ['TxF150-75','TxF300-75','TxF300-1600'] },
    'TxF300-1600': { pvp: 19900, total: null,  promo: null,  promoPacks:  0, digirent: 411.60, family: ['TxF150-75','TxF300-75','TxF300-1600'] },
    'TS200-1600':  { pvp: 32000, total: null,  promo: 25000, promoPacks:  0, digirent: null,   family: ['TS200-1600','TS500-1800'] },
    'TS500-1800':  { pvp: 49000, total: null,  promo: null,  promoPacks:  0, digirent: null,   family: ['TS200-1600','TS500-1800'] },
    'UCJV330-160': { pvp: 23800, total: null,  promo: 19800, promoPacks:  0, digirent: 492.26, family: ['UCJV300-75','UCJV330-160'] },
  };
  const SCENARIO_LABELS = ['Conservador', 'Base', 'Optimista'];

  const equip       = EQUIP_DB[brief.product_name] || null;
  const offerRaw2   = brief.commercial_offer;
  const offer2      = offerRaw2 ? (typeof offerRaw2 === 'string' ? (() => { try { return JSON.parse(offerRaw2); } catch { return { descricao: offerRaw2 }; } })() : offerRaw2) : {};
  const isPromo     = !!(offer2.condicao && /promo|summer|special/i.test(JSON.stringify(offer2)));

  // Cenários: família de modelos → conservador/base/optimista
  // Filtra modelos sem pvp definido (evita crash em .toLocaleString)
  const familyModels  = equip?.family?.map(k => ({ key: k, ...EQUIP_DB[k] })).filter(m => m && (m.pvp || m.total || m.promo)) || (equip ? [{ key: brief.product_name, ...equip }] : []);
  const scenarioCount = familyModels.length;
  // Preço âncora = modelo do briefing (promo se existir, senão pvp)
  const pvp           = equip ? (isPromo && equip.promo ? equip.promo : (equip.total || equip.pvp)) : null;
  const pvpLabel      = equip ? (isPromo && equip.promo ? `€${equip.promo.toLocaleString('pt-PT')} promo` : equip.total ? `€${equip.total.toLocaleString('pt-PT')} configurado` : `€${equip.pvp?.toLocaleString('pt-PT')} base`) : null;

  const totalBudget   = boardData?.totalBudget   || 0;
  const emailContacts = boardData?.emailContacts || 0;
  const waContacts    = boardData?.waContacts    || 0;

  // Reach real dos targets + funil estimado
  const metaReachMin  = boardData?.metaReachMin || 0;
  const metaReachMax  = boardData?.metaReachMax || 0;
  const metaReach     = metaReachMin > 0 ? metaReachMin : (boardData?.metaCountries || 1) * 100000;
  const totalMin      = metaReachMin + emailContacts + waContacts;
  const totalMax      = metaReachMax + emailContacts + waContacts;
  const fmtReach = n => n >= 1e6 ? `${(n/1e6).toFixed(1).replace('.0','')}M` : n >= 1e3 ? `${Math.round(n/1000)}K` : String(n);
  const reachLabel    = totalMin > 0
    ? (totalMax > totalMin ? `${fmtReach(totalMin)}–${fmtReach(totalMax)}` : `${fmtReach(totalMin)}+`)
    : `${fmtReach((boardData?.metaCountries || 1) * 100000)}+`;
  // KPIs da estratégia — 1º KPIs estruturados, 2º parse do objectivo_concreto
  const leadKpis = (campanha?.estrategia_json?.markets || []).flatMap(m => (m.kpis || []).filter(k => /lead|mql|qualificad/i.test(k.metrica || '')));
  const comCanal = leadKpis.filter(k => k.canal);
  const baseKpis = comCanal.length ? comCanal : leadKpis;
  const fromKpi = baseKpis.reduce((s, k) => s + (parseInt(String(k.target).replace(/\D/g, ''), 10) || 0), 0);
  const objConcreto  = campanha?.estrategia_json?.sintese_campanha?.objectivo_concreto || '';
  const leadsFromObj = (() => {
    const s = Array.isArray(objConcreto) ? objConcreto.join('; ') : String(objConcreto);
    const m = s.match(/gerar\s+(\d+)\s+(lead|mql|qualificad)/i) || s.match(/(\d+)\s+(lead|mql|qualificad)/i);
    return m ? parseInt(m[1]) : 0;
  })();
  const leadsTarget  = fromKpi || leadsFromObj || 0;
  const metaLeads   = Math.round(metaReach * 0.015 * 0.04);
  const emailLeads  = Math.round(emailContacts * 0.28 * 0.03 * 0.20);
  const waLeads     = Math.round(waContacts * 0.40 * 0.15);
  const totalLeads  = leadsTarget > 0 ? leadsTarget : (metaLeads + emailLeads + waLeads);
  const hotLeads    = Math.round(totalLeads * 0.30);
  const demos       = Math.round(hotLeads * (isMateriais ? 0.80 : 0.65));
  const sales       = Math.max(1, Math.round(demos * (isMateriais ? 0.50 : 0.20)));

  // Custo da oferta a PVP (nota: custo real Digidelta será inferior)
  const inkPacksPromo   = (isPromo && equip?.promoPacks) ? equip.promoPacks : 0;
  const offerCostUnit   = inkPacksPromo * PHT50_PRICE;   // PVP — conservador
  const totalOfferCost  = offerCostUnit * sales;
  const totalInvestment = totalBudget + totalOfferCost;

  // ROI por cenário (família de modelos)
  const scenarioROI = familyModels.map((m, i) => {
    const mPvp   = isPromo && m.promo ? m.promo : (m.total || m.pvp);
    const mOffer = (isPromo && m.promoPacks) ? m.promoPacks * PHT50_PRICE : 0;
    const mInvest = totalBudget + mOffer * sales;
    const mRev   = sales * mPvp;
    const mRoi   = mInvest > 0 ? Math.round((mRev - mInvest) / mInvest) : null;
    const mBE    = mInvest > 0 ? mInvest / mPvp : null;
    const label  = scenarioCount === 1 ? 'Único' : (SCENARIO_LABELS[i] || SCENARIO_LABELS[SCENARIO_LABELS.length-1]);
    return { key: m.key, label, pvp: mPvp, offer: mOffer, revenue: mRev, invest: mInvest, roi: mRoi, be: mBE };
  });

  // ROI âncora (modelo do briefing)
  const anchorIdx = familyModels.findIndex(m => m.key === brief.product_name);
  const anchorROI = scenarioROI[anchorIdx >= 0 ? anchorIdx : 0];
  // Timeline
  // Usar datas reais do comm_plan (Planeamento) — fallback para timeline do briefing
  const _commDates = (campanha?.proposta_json?.comm_plan || []).map(i => i.planned_date).filter(Boolean).sort();
  const tlStart = _commDates.length > 0 ? new Date(_commDates[0]) : (brief.timeline_start ? new Date(brief.timeline_start) : null);
  const tlEnd   = _commDates.length > 0 ? new Date(_commDates[_commDates.length - 1]) : (brief.timeline_end ? new Date(brief.timeline_end) : null);
  const tlDays  = tlStart && tlEnd ? Math.round((tlEnd - tlStart) / 864e5) : null;
  const fmtShort = d => d ? d.toLocaleDateString('pt-PT', { day:'numeric', month:'short' }) : '—';

  const icRow = icApi?.congelado || icApi?.calculado || null;
  const fSector = icRow?.sector || (isMateriais ? 'materiais' : 'equipamento');
  const objIc = icRow?.objectivos || {};
  const fLeads = objIc.leads ?? totalLeads;
  const fQual = objIc.qualificados ?? hotLeads;
  const fMid = fSector === 'materiais' ? (objIc.testing_rolls ?? demos) : fSector === 'equipamento' ? (objIc.demos ?? demos) : (objIc.oportunidades ?? demos);
  const fEnd = fSector === 'materiais' ? (objIc.primeira_encomenda ?? sales) : fSector === 'equipamento' ? (objIc.fechos ?? sales) : (objIc.wins ?? sales);
  const fMidLabel = fSector === 'materiais' ? 'Testing Rolls' : fSector === 'equipamento' ? 'Demos' : 'Oportunidades';
  const fEndLabel = fSector === 'materiais' ? '1ª Encomenda' : fSector === 'equipamento' ? 'Fechos est.' : 'Wins';
  const fMidSub = fSector === 'materiais' ? 'Conversão 80%' : fSector === 'equipamento' ? 'Conversão 65%' : 'Conversão 65%';
  const fEndSub = fSector === 'materiais' ? 'Conversão 50%' : fSector === 'equipamento' ? 'Close rate 20%' : 'Win rate 20%';
  const fReach = icRow
    ? ((icRow.reach_max > icRow.reach_min) ? `${fmtReach(icRow.reach_min)}–${fmtReach(icRow.reach_max)}` : `${fmtReach(icRow.reach_min || objIc.reach || 0)}+`)
    : reachLabel;
  const fRodape = icRow?.rodape || (leadsTarget > 0 ? `Leads: objectivo definido na estratégia (${leadsTarget}). ` : '') + `Qualificados: taxa de conversão 30% via SDR Digi AI. ${fSector === 'materiais' ? 'Testing Roll 80%. 1ª Encomenda 50%.' : fSector === 'comum' ? 'Oportunidades 65%. Wins 20%.' : 'Demos 65%. Fechos 20%.'} Valores reais dependem da qualidade das audiências e execução.`;
  const congelado = icApi?.congelado || null;

  const guardarObjectivos = () => {
    if (!objDraft || !campanha?.id) return;
    setObjSaving(true);
    campApiCall(`/api/marketing/campanhas/${campanha.id}/objectivos`, { method: 'PUT', body: JSON.stringify(objDraft) })
      .then(() => campApiCall(`/api/marketing/campanhas/${campanha.id}/investment-case`))
      .then(d => { setIcApi(d); setShowObjEdit(false); })
      .catch(e => setBoardError(e.message))
      .finally(() => setObjSaving(false));
  };

  const InvestmentCase = () => (
    <div style={{ display:'flex', flexDirection:'column', gap:12 }}>
      <div style={{ fontSize:10, fontWeight:700, fontFamily:'var(--font-mono)', letterSpacing:'.08em', textTransform:'uppercase', color:'var(--text-dim)', marginBottom:2 }}>
        Investment Case · visão executiva
      </div>

      {/* Loading spinner enquanto boardData carrega */}
      {boardLoading && !boardData && (
        <div style={{ background: 'var(--bg-surface,#fff)', border: '1px solid var(--border)', borderRadius: 8, padding: '14px 18px', display: 'flex', alignItems: 'center', gap: 10, color: 'var(--text-muted,#64748b)', fontSize: 12 }}>
          <div style={{ width: 12, height: 12, border: `2px solid ${bCol}`, borderTopColor: 'transparent', borderRadius: '50%', animation: 'spin 0.8s linear infinite' }} />
          <span>A carregar orçamento e targets para o Investment Case…</span>
          <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
        </div>
      )}

      {/* Aviso de erro parcial nos fetches */}
      {boardError && (
        <div style={{ background: 'rgba(217,119,6,.06)', border: '1px solid rgba(217,119,6,.25)', borderRadius: 8, padding: '10px 14px', fontSize: 12, color: '#92400E' }}>
          {boardError}
        </div>
      )}

      {/* Row 1: Investimento + Equipamento + Timeline */}
      <div style={{ display:'grid', gridTemplateColumns:'repeat(3,1fr)', gap:10 }}>
        {[
          { lbl:'Investimento media',   val: totalBudget > 0 ? `€${Math.round(totalBudget).toLocaleString('pt-PT')}` : '—', sub: tlDays ? `${tlDays} dias de campanha` : null, col:'#3859D0' },
          isMateriais
            ? { lbl:'Oferta activa', val: offer2.descricao ? offer2.descricao.slice(0,30)+'…' : (offer2.primary_cta || 'Testing Roll'), sub: offer2.details ? offer2.details.slice(0,40) : 'Testing Roll + Garantia de Satisfação', col:'#0F4C75' }
            : { lbl:'Equipamento · ' + (isPromo ? 'Promo' : 'Total config.'), val: pvpLabel || '—', sub: brief.product_name || null, col:'#0F4C75' },
          { lbl:'Janela temporal',      val: tlStart ? `${fmtShort(tlStart)} → ${fmtShort(tlEnd)}` : '—', sub: tlDays ? `${tlDays} dias` : null, col:'#065F46' },
        ].map((c,i) => (
          <div key={i} style={{ background:'var(--bg-card,#fff)', border:'1px solid var(--border)', borderTop:`3px solid ${c.col}`, borderRadius:8, padding:'12px 14px' }}>
            <div style={{ fontSize:9, fontWeight:700, color:c.col, fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.06em', marginBottom:4 }}>{c.lbl}</div>
            <div style={{ fontSize:18, fontWeight:800, color:'var(--navy,#112954)', fontFamily:'var(--font-display)', lineHeight:1 }}>{c.val}</div>
            {c.sub && <div style={{ fontSize:10, color:'var(--text-muted)', marginTop:4 }}>{c.sub}</div>}
          </div>
        ))}
      </div>

      {/* Row 2: Funil estimado */}
      {fLeads > 0 && (
        <div style={{ background:'var(--bg-card,#fff)', border:'1px solid var(--border)', borderRadius:8, padding:'14px 16px' }}>
          <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:10, gap:8 }}>
            <div style={{ fontSize:9, fontWeight:700, color:'var(--text-dim)', fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.06em' }}>Funil estimado · benchmarks B2B sector {fSector}</div>
            {congelado && isApprover && (
              <button type="button" className="btn btn-sm" onClick={() => { setObjDraft({ objectivos: { ...(congelado.objectivos || {}) }, objectivos_canal: { ...(congelado.objectivos_canal || {}) }, janela_inicio: congelado.janela_inicio ? String(congelado.janela_inicio).slice(0,10) : '', janela_fim: congelado.janela_fim ? String(congelado.janela_fim).slice(0,10) : '' }); setShowObjEdit(true); }}>Editar objectivos</button>
            )}
          </div>
          {congelado && (
            <div style={{ fontSize:11, color:'var(--text-muted)', marginBottom:8 }}>
              Objectivos congelados {congelado.congelado_em ? new Date(congelado.congelado_em).toLocaleDateString('pt-PT') : ''}{congelado.congelado_por ? ` por ${congelado.congelado_por}` : ''} · {congelado.origem === 'manual' ? 'editados à mão' : congelado.origem === 'backfill' ? 'backfill' : 'aprovação'}
            </div>
          )}
          <div style={{ display:'flex', alignItems:'center', gap:0 }}>
            {[
              { lbl:'Reach', val: fReach, sub: 'Meta Ads · Email · WhatsApp', col:'#0F4C75' },
              { lbl:'Leads', val: fLeads, sub: (icRow?.leads_fonte === 'estrategia' || leadsTarget > 0) ? 'Objectivo da estratégia' : `Meta ${metaLeads} · Email ${emailLeads} · WA ${waLeads}`, col:'#3859D0' },
              { lbl:'Qualificados', val: fQual, sub:'SDR Digi AI · 30%', col:'#065F46' },
              { lbl: fMidLabel, val: fMid, sub: fMidSub, col:'#92400E' },
              { lbl: fEndLabel, val: fEnd, sub: fEndSub, col:'#15803d' },
            ].map((s, i, arr) => (
              <React.Fragment key={i}>
                <div style={{ flex:1, textAlign:'center', padding:'6px 8px', background: i === arr.length-1 ? 'rgba(22,163,74,.07)' : 'transparent', borderRadius: i === arr.length-1 ? 6 : 0 }}>
                  <div style={{ fontSize:9, fontWeight:700, color:s.col, fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.05em', marginBottom:3 }}>{s.lbl}</div>
                  <div style={{ fontSize:20, fontWeight:800, color:'var(--navy,#112954)', fontFamily:'var(--font-display)', lineHeight:1 }}>{s.val}</div>
                  <div style={{ fontSize:9, color:'var(--text-muted)', marginTop:3, lineHeight:1.3 }}>{s.sub}</div>
                </div>
                {i < arr.length-1 && (
                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#cbd5e1" strokeWidth="2" strokeLinecap="round"><polyline points="9 18 15 12 9 6"/></svg>
                )}
              </React.Fragment>
            ))}
          </div>
        </div>
      )}

      {/* Row 3: Investimento + Cenários ROI */}
      {scenarioROI.length > 0 && totalBudget > 0 && (
        <div style={{ display:'flex', flexDirection:'column', gap:10 }}>

          {/* Desdobramento do investimento (âncora) */}
          <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap:8, padding:'10px 14px', background:'rgba(56,89,208,.04)', border:'1px solid rgba(56,89,208,.15)', borderRadius:8 }}>
            {[
              { lbl:'Investimento media', val:`€${Math.round(totalBudget).toLocaleString('pt-PT')}`, sub:'Ads + Email + WA' },
              { lbl:`Custo oferta* (${sales} vendas)`, val: offerCostUnit > 0 ? `€${Math.round(totalOfferCost).toLocaleString('pt-PT')}` : '—', sub: offerCostUnit > 0 ? `${sales}×${inkPacksPromo} packs×€${PHT50_PRICE} PVP` : 'sem oferta' },
              { lbl:'Investimento total', val:`€${Math.round(totalInvestment).toLocaleString('pt-PT')}`, sub:'media + custo oferta' },
            ].map((c,i) => (
              <div key={i}>
                <div style={{ fontSize:9, fontWeight:700, color:'#3859D0', fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.06em', marginBottom:3 }}>{c.lbl}</div>
                <div style={{ fontSize:15, fontWeight:800, color:'var(--navy,#112954)', fontFamily:'var(--font-display)' }}>{c.val}</div>
                <div style={{ fontSize:9, color:'var(--text-muted)', marginTop:2 }}>{c.sub}</div>
              </div>
            ))}
          </div>

          {/* Cenários ROI por modelo da família */}
          <div style={{ display:'grid', gridTemplateColumns:`repeat(${scenarioROI.length},1fr)`, gap:10 }}>
            {scenarioROI.map((sc, i) => {
              const isAnchor = sc.key === brief.product_name;
              const col = i === 0 ? '#64748b' : i === scenarioROI.length-1 ? '#15803d' : '#3859D0';
              const bg  = i === 0 ? 'rgba(100,116,139,.06)' : i === scenarioROI.length-1 ? 'rgba(22,163,74,.07)' : 'rgba(56,89,208,.06)';
              return (
                <div key={sc.key} style={{ background:bg, border:`1px solid var(--border)`, borderTop:`3px solid ${col}`, borderRadius:8, padding:'12px 14px', position:'relative' }}>
                  {isAnchor && <div style={{ position:'absolute', top:6, right:8, fontSize:8, fontWeight:700, color:col, fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.05em', background:`${col}18`, padding:'1px 5px', borderRadius:3 }}>briefing</div>}
                  <div style={{ fontSize:9, fontWeight:700, color:col, fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.06em', marginBottom:6 }}>{sc.label} · {sc.key}</div>
                  <div style={{ display:'flex', flexDirection:'column', gap:4 }}>
                    <div>
                      <div style={{ fontSize:8, color:'var(--text-dim)', textTransform:'uppercase', letterSpacing:'.04em', fontFamily:'var(--font-mono)' }}>Preço equip.</div>
                      <div style={{ fontSize:14, fontWeight:800, color:'var(--navy,#112954)', fontFamily:'var(--font-display)' }}>€{(sc.pvp || 0).toLocaleString('pt-PT')}</div>
                    </div>
                    <div>
                      <div style={{ fontSize:8, color:'var(--text-dim)', textTransform:'uppercase', letterSpacing:'.04em', fontFamily:'var(--font-mono)' }}>Receita ({sales} vendas)</div>
                      <div style={{ fontSize:14, fontWeight:800, color:'var(--navy,#112954)', fontFamily:'var(--font-display)' }}>€{(sc.revenue || 0).toLocaleString('pt-PT')}</div>
                    </div>
                    <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:6, marginTop:4, paddingTop:6, borderTop:'1px solid var(--border-light,#f1f5f9)' }}>
                      <div>
                        <div style={{ fontSize:8, color:'var(--text-dim)', textTransform:'uppercase', letterSpacing:'.04em', fontFamily:'var(--font-mono)' }}>ROI</div>
                        <div style={{ fontSize:16, fontWeight:800, color: col, fontFamily:'var(--font-display)' }}>{sc.roi !== null ? `${sc.roi}×` : '—'}</div>
                      </div>
                      <div>
                        <div style={{ fontSize:8, color:'var(--text-dim)', textTransform:'uppercase', letterSpacing:'.04em', fontFamily:'var(--font-mono)' }}>Break-even</div>
                        <div style={{ fontSize:11, fontWeight:700, color:'var(--navy,#112954)', fontFamily:'var(--font-display)', lineHeight:1.3 }}>
                          {sc.be !== null ? (sc.be < 1 ? `${(sc.be*100).toFixed(0)}% de 1 venda` : `${sc.be.toFixed(1)} vendas`) : '—'}
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              );
            })}
          </div>

          {offerCostUnit > 0 && (
            <div style={{ fontSize:9, color:'var(--text-dim)', fontStyle:'italic', lineHeight:1.5 }}>
              * Custo da oferta calculado a PVP (€{PHT50_PRICE}/pack) — custo real Digidelta será inferior. ROI subestimado.
            </div>
          )}
        </div>
      )}

      {/* Oferta comercial */}
      {(offer2.oferta || offer2.descricao) && (
        <div style={{ padding:'10px 14px', background:'rgba(146,64,14,.05)', border:'1px solid rgba(146,64,14,.2)', borderRadius:8, display:'flex', gap:10, alignItems:'flex-start' }}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#92400E" strokeWidth="2" style={{ flexShrink:0, marginTop:1 }}><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>
          <div>
            <div style={{ fontSize:10, fontWeight:700, color:'#92400E', fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.05em', marginBottom:2 }}>Oferta comercial · campanha</div>
            <div style={{ fontSize:12, color:'var(--text)', lineHeight:1.5 }}>{offer2.oferta || offer2.descricao}{offer2.condicao ? ` — ${offer2.condicao}` : ''}{offer2.validade ? ` · Válida ${offer2.validade}` : ''}</div>
          </div>
        </div>
      )}

      <div style={{ fontSize:9, color:'var(--text-dim)', fontStyle:'italic', lineHeight:1.5, paddingTop:4 }}>
        * {fRodape}
      </div>
      {showObjEdit && objDraft && (
        <div style={{ position:'fixed', inset:0, background:'rgba(15,23,42,.45)', zIndex:80, display:'flex', alignItems:'center', justifyContent:'center', padding:24 }} onClick={() => setShowObjEdit(false)}>
          <div style={{ background:'var(--bg-elev,#fff)', borderRadius:12, padding:20, width:480, maxWidth:'100%', maxHeight:'80vh', overflow:'auto' }} onClick={e => e.stopPropagation()}>
            <div style={{ fontWeight:700, marginBottom:12 }}>Editar objectivos</div>
            <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:8 }}>
              {Object.keys(objDraft.objectivos || {}).map(k => (
                <label key={k} style={{ fontSize:11, color:'var(--text-muted)' }}>{k}
                  <input type="number" min="0" value={objDraft.objectivos[k] ?? ''} onChange={e => setObjDraft(d => ({ ...d, objectivos: { ...d.objectivos, [k]: Number(e.target.value) } }))} style={{ display:'block', width:'100%', marginTop:4, padding:'6px 8px', borderRadius:6, border:'1px solid var(--border)' }} />
                </label>
              ))}
            </div>
            <div style={{ display:'flex', gap:8, marginTop:14, justifyContent:'flex-end' }}>
              <button type="button" className="btn btn-sm" onClick={() => setShowObjEdit(false)}>Cancelar</button>
              <button type="button" className="btn btn-sm btn-primary" disabled={objSaving} onClick={guardarObjectivos}>{objSaving ? 'A gravar…' : 'Gravar'}</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );

  // Ordem: Briefing → Estratégia → Conceito → Orçamento → Target → Planeamento → Funil → SDR AI · Ficha

  const SM = {
    approved: { label:'Aprovado', color:'#15803d', bg:'rgba(22,163,74,.1)',   dot:'#22c55e' },
    partial:  { label:'Parcial',  color:'#a16207', bg:'rgba(234,179,8,.1)',   dot:'#eab308' },
    done:     { label:'Pronto',   color:'#3859D0', bg:'rgba(56,89,208,.08)', dot:'#3859D0' },
    pending:  { label:'Pendente', color:'#94a3b8', bg:'var(--bg-sunken)',    dot:'#cbd5e1' },
  };

  const phasesDone     = CAMP_PHASES.filter(p => ['approved','done'].includes(p.status)).length;
  const phasesApproved = CAMP_PHASES.filter(p => p.status === 'approved').length;
  const pct            = Math.round((phasesDone / CAMP_PHASES.length) * 100);
  const canSubmit      = phasesDone >= 6; // briefing + estrategia + conceito + planeamento + funil + sdr_ai minimamente

  const timeAgo = iso => { if (!iso) return null; const h = Math.floor((Date.now() - new Date(iso).getTime()) / 3600000); const d = Math.floor(h / 24); return h < 1 ? 'há menos de 1h' : h < 24 ? `há ${h}h` : `há ${d}d`; };
  const fmtDate = iso => iso ? new Date(iso).toLocaleDateString('pt-PT', { day:'numeric', month:'long', year:'numeric', hour:'2-digit', minute:'2-digit' }) : null;

  const approveRecord = approvals.find(a => a.phase === 'producao' && a.action === 'approved');
  const submitRecord  = approvals.find(a => a.action === 'approved' && a.phase !== 'producao') || approvals.find(a => a.phase === 'funil');

  // ─── STATE 3: Em Produção / Publicado ───────────────────────────────────────
  if (isInProducao) {
    const commPlanItems = campanha?.proposta_json?.comm_plan || [];
    const emailCount  = commPlanItems.filter(r => r.canal === 'email').length;
    const socialCount = commPlanItems.filter(r => ['instagram','facebook','linkedin','meta_ads'].includes(r.canal) || ['post','carrossel','social_post'].includes(r.content_type)).length;
    const adsCount    = commPlanItems.filter(r => ['meta_ads','linkedin_ads','google_ads_search'].includes(r.canal)).length;
    const approvedBy  = campanha?.funil_approved_by || approveRecord?.approver_name || approveRecord?.approver_email || null;
    const approvedAt  = campanha?.funil_approved_at || approveRecord?.created_at || null;
    return (
      <div style={{ display:'flex', flexDirection:'column', gap:20 }}>
        {/* Hero aprovada */}
        <div style={{ background:'rgba(22,163,74,.07)', border:'1px solid rgba(22,163,74,.25)', borderRadius:12, padding:'20px 24px' }}>
          <div style={{ display:'flex', alignItems:'center', gap:10, marginBottom:4 }}>
            <div style={{ width:10, height:10, borderRadius:'50%', background:'#22c55e' }} />
            <div style={{ fontSize:15, fontWeight:700, color:'#15803d', fontFamily:'var(--font-display)' }}>
              {status === 'publicado' ? 'Campanha Publicada' : 'Aprovada — em Produção de Conteúdos'}
            </div>
          </div>
          {approvedBy && (
            <div style={{ fontSize:12, color:'#64748b', marginBottom:16 }}>
              Aprovado por <strong>{approvedBy}</strong>{approvedAt ? ` · ${fmtDate(approvedAt)}` : ''}
            </div>
          )}
          <div style={{ display:'grid', gridTemplateColumns:'repeat(4,1fr)', gap:10, marginBottom:16 }}>
            {[
              { lbl:'Emails planeados', val: emailCount  > 0 ? String(emailCount)  : '—' },
              { lbl:'Posts sociais',    val: socialCount > 0 ? String(socialCount) : '—' },
              { lbl:'Anúncios Ads',     val: adsCount    > 0 ? String(adsCount)    : '—' },
              { lbl:'Mercados',         val: markets.map(m=>m.country).join(' + ') || '—' },
            ].map((r,i) => (
              <div key={i} style={{ background:'rgba(22,163,74,.05)', borderRadius:8, padding:'10px 12px' }}>
                <div style={{ fontSize:9, fontWeight:700, color:'#94a3b8', fontFamily:'var(--font-mono)', textTransform:'uppercase', marginBottom:3 }}>{r.lbl}</div>
                <div style={{ fontSize:18, fontWeight:800, color:'#112954', fontFamily:'var(--font-display)' }}>{r.val}</div>
              </div>
            ))}
          </div>
          <button
            onClick={() => {
              const cid = campanha?.id;
              if (window.mktNavToSub) window.mktNavToSub('conteudos');
              // Espera o módulo montar antes de abrir a campanha específica
              setTimeout(() => {
                if (cid && window.mktProducaoOpenCampanha) window.mktProducaoOpenCampanha(cid);
              }, 200);
            }}
            className="btn btn-ai" style={{ fontSize:13, display:'inline-flex', alignItems:'center', gap:6 }}
          >
            <window.Icon name="arrowRight" size={13}/>
            Abrir esta campanha em Produção de Conteúdos
          </button>
        </div>
        <InvestmentCase />
        {/* Histórico */}
        <AprovacaoHistory approvals={approvals} timeAgo={timeAgo} campanha={campanha} />
      </div>
    );
  }

  // ─── STATE 2: Aguarda Aprovação Executiva ────────────────────────────────────
  if (isPending) {
    const sentBy = approvals.find(a => a.phase === 'funil' || !a.phase)?.approver_name || 'equipa marketing';
    return (
      <div style={{ display:'flex', flexDirection:'column', gap:20 }}>
        {/* Banner aguarda */}
        <div style={{ background:'#FEF3C7', border:'1px solid rgba(217,119,6,.3)', borderRadius:12, padding:'16px 20px', display:'flex', alignItems:'center', justifyContent:'space-between', gap:16 }}>
          <div>
            <div style={{ display:'flex', alignItems:'center', gap:8, marginBottom:4 }}>
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#92400E" strokeWidth="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
              <div style={{ fontSize:14, fontWeight:700, color:'#92400E', fontFamily:'var(--font-display)' }}>Aguarda Aprovação Executiva</div>
            </div>
            <div style={{ fontSize:12, color:'#92400E', opacity:.8 }}>Enviado por {sentBy} · a notificação foi para fabio.costa@digidelta.pt</div>
          </div>
          {isApprover && (
            <div style={{ display:'flex', gap:8, flexShrink:0 }}>
              <button className="btn" style={{ color:'#d97706', borderColor:'rgba(217,119,6,.4)', fontSize:12 }}
                onClick={() => setShowPedirAlt(true)}>Pedir Alterações</button>
              <button className="btn" style={{ color:'#dc2626', borderColor:'rgba(220,38,38,.3)', fontSize:12 }}
                onClick={() => setShowRejeitar(true)}>Rejeitar</button>
              <button className="btn btn-ai" disabled={submitting} style={{ fontSize:12 }}
                onClick={async () => { setSubmitting(true); await onAction('enviarProducao').catch(()=>{}); setSubmitting(false); }}>
                {submitting ? 'A enviar...' : '→ Enviar para Produção de Conteúdos'}
              </button>
            </div>
          )}
        </div>

        {/* 1º · Fases (validação CEO) */}
        <div>
          <div style={{ fontSize:10, fontWeight:700, fontFamily:'var(--font-mono)', letterSpacing:'.08em', textTransform:'uppercase', color:'var(--text-dim)', marginBottom:10 }}>1 · Fases de Campanha Marketing — {phasesApproved}/{CAMP_PHASES.length} aprovadas</div>
          <div style={{ display:'grid', gridTemplateColumns:`repeat(${CAMP_PHASES.length},1fr)`, gap:8 }}>
            {CAMP_PHASES.map(p => {
              const sm = SM[p.status] || SM.pending;
              return (
                <div key={p.id} style={{ background:'var(--bg-card,#fff)', border:`1px solid var(--border)`, borderTop:`3px solid ${sm.dot}`, borderRadius:8, padding:'10px 10px 8px' }}>
                  <div style={{ fontSize:9, fontWeight:700, color:sm.color, fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.04em', marginBottom:4 }}>{sm.label}</div>
                  <div style={{ fontSize:11, fontWeight:700, color:'var(--navy,#112954)', fontFamily:'var(--font-display)', lineHeight:1.2 }}>{p.label}</div>
                  {p.detail && <div style={{ fontSize:9, color:'var(--text-muted)', marginTop:3, lineHeight:1.3 }}>{p.detail}</div>}
                </div>
              );
            })}
          </div>
        </div>

        {/* 2º · Investment Case */}
        <InvestmentCase />

        {/* 3º · Contexto Estratégico (Resumo + USPs+Dores) */}
        <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:16 }}>
          {/* Info campanha */}
          <div style={{ background:'var(--bg-card,#fff)', border:'1px solid var(--border)', borderRadius:10, padding:'16px 18px' }}>
            <div style={{ fontSize:10, fontWeight:700, fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.08em', color:'var(--text-dim)', marginBottom:12 }}>3 · Contexto Estratégico · Resumo</div>
            {[
              { lbl:'Marca',         val: campanha?.brand_name || '—' },
              { lbl:'Produto',       val: brief.commercial_name || campanha?.titulo || '—' },
              { lbl:'Mercados',      val: markets.map(m=>m.country).join(' + ') || '—' },
              { lbl:'Canais',        val: allChs.map(c=>chLabels[c]||c).join(' · ') || '—' },
              { lbl:'Objectivo',     val: brief.objective || '—' },
              { lbl:'Decisor',       val: brief.decision_maker || '—' },
            ].map((r,i) => (
              <div key={i} style={{ display:'flex', gap:8, padding:'6px 0', borderBottom:'1px solid var(--border-light,#f1f5f9)' }}>
                <div style={{ fontSize:10, fontWeight:700, color:'var(--text-dim)', fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.04em', minWidth:90, flexShrink:0 }}>{r.lbl}</div>
                <div style={{ fontSize:12, color:'var(--text)', lineHeight:1.4 }}>{r.val}</div>
              </div>
            ))}
            {brief.key_message && (
              <div style={{ marginTop:10, padding:'8px 10px', background:'rgba(56,89,208,.05)', borderRadius:6, borderLeft:'3px solid #3859D0' }}>
                <div style={{ fontSize:9, fontWeight:700, color:'#3859D0', fontFamily:'var(--font-mono)', textTransform:'uppercase', marginBottom:3 }}>Mensagem-chave</div>
                <div style={{ fontSize:12, color:'var(--text)', fontStyle:'italic', lineHeight:1.5 }}>"{brief.key_message}"</div>
              </div>
            )}
          </div>

          {/* USPs + Dores */}
          <div style={{ background:'var(--bg-card,#fff)', border:'1px solid var(--border)', borderRadius:10, padding:'16px 18px' }}>
            <div style={{ fontSize:10, fontWeight:700, fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.08em', color:'var(--text-dim)', marginBottom:12 }}>Argumentos · Dores</div>
            {usps.length > 0 && (
              <div style={{ marginBottom:12 }}>
                <div style={{ fontSize:10, fontWeight:700, color:'#3859D0', fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.05em', marginBottom:6 }}>Top USPs</div>
                {usps.slice(0,4).map((u,i) => (
                  <div key={i} style={{ display:'flex', gap:8, marginBottom:6 }}>
                    <div style={{ fontSize:12, fontWeight:800, color:'#3859D0', flexShrink:0, lineHeight:1.2 }}>{i+1}</div>
                    <div style={{ fontSize:11, color:'var(--text)', lineHeight:1.4 }}>{typeof u==='string'?u:u.usp||u.descricao||''}</div>
                  </div>
                ))}
              </div>
            )}
            {pains.length > 0 && (
              <div>
                <div style={{ fontSize:10, fontWeight:700, color:'#065F46', fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.05em', marginBottom:6 }}>Dores dominantes</div>
                {pains.slice(0,3).map((p,i) => (
                  <div key={i} style={{ display:'flex', gap:8, marginBottom:5 }}>
                    <div style={{ width:5, height:5, borderRadius:'50%', background:'#065F46', flexShrink:0, marginTop:5 }} />
                    <div style={{ fontSize:11, color:'var(--text)', lineHeight:1.4 }}>{typeof p==='string'?p:p.descricao||''}</div>
                  </div>
                ))}
              </div>
            )}
          </div>
        </div>

        {/* 4º · Histórico */}
        <AprovacaoHistory approvals={approvals} timeAgo={timeAgo} campanha={campanha} sectionLabel="4 · Histórico de Aprovações"/>

        {showPedirAlt && <PedirAlteracoesAprovacaoModal onClose={() => setShowPedirAlt(false)} onConfirm={(notes, phase) => { onAction('pedirAlteracoes', [{ notes, phase }]); setShowPedirAlt(false); }} />}
        {showRejeitar && <RejeitarCampanhaModal onClose={() => setShowRejeitar(false)} onConfirm={(notes) => { onAction('rejeitarAprovacao', [notes]); setShowRejeitar(false); }} />}
      </div>
    );
  }

  // ─── STATE 1: Checklist — não submetido ─────────────────────────────────────
  return (
    <div style={{ display:'flex', flexDirection:'column', gap:20 }}>

      {/* Hero progresso */}
      <div style={{ background:'var(--bg-card,#fff)', border:'1px solid var(--border)', borderRadius:12, padding:'20px 24px' }}>
        <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', gap:16, marginBottom:12 }}>
          <div>
            <div style={{ fontSize:15, fontWeight:700, color:'var(--navy,#112954)', fontFamily:'var(--font-display)', marginBottom:3 }}>
              {phasesDone}/{CAMP_PHASES.length} fases concluídas
            </div>
            <div style={{ fontSize:12, color:'var(--text-muted)' }}>
              {canSubmit ? 'Pronto para submeter à aprovação executiva.' : `Completa as fases em falta antes de submeter.`}
            </div>
          </div>
          <div style={{ display:'flex', gap:8, flexShrink:0 }}>
            {podeSubmeter && (<>
              <button className="btn btn-ai" disabled={submitting || !canSubmit} style={{ opacity: canSubmit ? 1 : .5, fontSize: 12, height: 32, padding: '0 14px' }}
                onClick={async () => { setSubmitting(true); await onAction('enviarAprovacaoExecutiva').catch(()=>{}); setSubmitting(false); }}
                title="Envia o resumo da campanha por email só para fabio.costa@digidelta.pt">
                {submitting ? 'A enviar...' : 'Enviar para aprovação'}
              </button>
            </>)}
          </div>
        </div>
        {/* Barra de progresso */}
        <div style={{ height:6, borderRadius:99, background:'var(--bg-sunken,#f1f5f9)', overflow:'hidden' }}>
          <div style={{ height:'100%', borderRadius:99, background: pct >= 80 ? '#22c55e' : pct >= 50 ? '#3859D0' : '#f59e0b', width:`${pct}%`, transition:'width .4s' }} />
        </div>
      </div>

      {/* 1 · Fases checklist */}
      <div>
        <div style={{ fontSize:10, fontWeight:700, fontFamily:'var(--font-mono)', letterSpacing:'.08em', textTransform:'uppercase', color:'var(--text-dim)', marginBottom:10 }}>1 · Fases de Campanha Marketing</div>
        <div style={{ display:'flex', flexDirection:'column', gap:6 }}>
          {CAMP_PHASES.map(phase => {
            const sm = SM[phase.status] || SM.pending;
            return (
              <div key={phase.id} style={{ display:'flex', alignItems:'center', gap:12, padding:'12px 16px', background:'var(--bg-card,#fff)', border:'1px solid var(--border)', borderLeft:`3px solid ${sm.dot}`, borderRadius:8 }}>
                <div style={{ width:18, height:18, borderRadius:'50%', background:sm.bg, border:`1.5px solid ${sm.dot}`, display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0 }}>
                  {phase.status === 'approved' && <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke={sm.color} strokeWidth="3"><polyline points="20 6 9 17 4 12"/></svg>}
                  {phase.status === 'done'     && <div style={{ width:6, height:6, borderRadius:'50%', background:sm.dot }} />}
                </div>
                <div style={{ flex:1, minWidth:0 }}>
                  <div style={{ fontSize:13, fontWeight:700, color:'var(--navy,#112954)', fontFamily:'var(--font-display)' }}>{phase.label}</div>
                  <div style={{ fontSize:11, color:'var(--text-muted)', marginTop:1 }}>{phase.detail}</div>
                </div>
                <span style={{ fontSize:10, fontWeight:700, padding:'2px 8px', borderRadius:20, background:sm.bg, color:sm.color, flexShrink:0 }}>{sm.label}</span>
              </div>
            );
          })}
        </div>
      </div>

      {!canSubmit && (
        <div style={{ background:'rgba(245,158,11,.06)', border:'1px solid rgba(245,158,11,.25)', borderRadius:8, padding:'12px 16px', fontSize:12, color:'#92400E', lineHeight:1.6 }}>
          Para submeter à aprovação executiva é necessário ter pelo menos Briefing, Estratégia, Conceito, Planeamento, Funil Multicanal e ficha SDR AI gerada.
        </div>
      )}

      {/* 2 · Investment Case */}
      <InvestmentCase />
      {/* 3 · Histórico */}
      <AprovacaoHistory approvals={approvals} timeAgo={timeAgo} campanha={campanha} sectionLabel="3 · Histórico de Aprovações"/>
    </div>
  );
};

const AprovacaoHistory = ({ approvals, timeAgo, campanha, sectionLabel }) => {
  const fmtExact = iso => iso ? new Date(iso).toLocaleString('pt-PT', { day:'2-digit', month:'short', year:'numeric', hour:'2-digit', minute:'2-digit' }) : '—';
  const [filter, setFilter] = React.useState('all');

  const ACTION_META = {
    generated:              { label:'Gerado',                        col:'#3859D0', bg:'rgba(56,89,208,.06)',   icon:'refresh' },
    approved:               { label:'Aprovado',                      col:'#15803d', bg:'rgba(22,163,74,.08)',   icon:'check'   },
    activated:              { label:'Ficha SDR AI activada',         col:'#15803d', bg:'rgba(22,163,74,.08)',   icon:'check'   },
    deactivated:            { label:'Ficha SDR AI desactivada',      col:'#dc2626', bg:'rgba(220,38,38,.07)',   icon:'close'   },
    edited:                 { label:'Ficha SDR AI editada',          col:'#b45309', bg:'rgba(217,119,6,.07)',   icon:'doc'     },
    rejected:               { label:'Rejeitado',                     col:'#dc2626', bg:'rgba(220,38,38,.07)',   icon:'close'   },
    changes_requested:      { label:'Alterações pedidas',            col:'#d97706', bg:'rgba(217,119,6,.07)',   icon:'warning' },
    request_changes:        { label:'Alterações pedidas',            col:'#d97706', bg:'rgba(217,119,6,.07)',   icon:'warning' },
    submitted_for_approval: { label:'Enviado para aprovação exec.',  col:'#ea580c', bg:'rgba(234,88,12,.07)',   icon:'arrowRight' },
    reverted:               { label:'Revertido',                     col:'#64748b', bg:'rgba(100,116,139,.07)', icon:'refresh' },
  };

  const PHASE_LABEL = {
    estrategia:'Estratégia', conceito:'Conceito Criativo', orcamento:'Orçamento',
    segmentacao:'Target', planeamento:'Planeamento', funil:'Funil Multicanal',
    briefing:'Briefing', producao:'Produção', canais:'Canais', idiomas:'Idiomas',
    sdr_ai:'SDR AI · Ficha', sdr_ai_activate:'SDR AI · Ficha',
  };

  // Construir eventos a partir das colunas de timestamp do campanha
  const tsEvents = [];
  const c = campanha || {};
  [
    ['estrategia',   c.estrategia_generated_at, null,                      c.estrategia_approved_at,   c.estrategia_approved_by  ],
    ['conceito',     c.conceito_generated_at,   null,                      c.conceito_approved_at,     c.conceito_approved_by    ],
    ['orcamento',    c.orcamento_generated_at,  null,                      c.orcamento_approved_at,    c.orcamento_approved_by   ],
    ['segmentacao',  c.segmentacao_generated_at,null,                      c.segmentacao_approved_at,  c.segmentacao_approved_by ],
    ['planeamento',  c.planeamento_generated_at,null,                      c.planeamento_approved_at,  c.planeamento_approved_by ],
  ].forEach(([phase, genAt, , appAt, appBy]) => {
    if (genAt) tsEvents.push({ action:'generated', phase, created_at: genAt, approver_name: null });
    if (appAt) tsEvents.push({ action:'approved',  phase, created_at: appAt, approver_name: appBy });
  });
  // SDR AI · Ficha — timeline: geração (auto), edição manual, activação
  if (c.sdr_ficha_generated_at) tsEvents.push({ action:'generated', phase:'sdr_ai', created_at: c.sdr_ficha_generated_at, approver_name: c.sdr_ficha_generated_by || null });
  if (c.sdr_ficha_edited_at)    tsEvents.push({ action:'edited',    phase:'sdr_ai', created_at: c.sdr_ficha_edited_at,    approver_name: c.sdr_ficha_edited_by || null });
  if (c.sdr_ai_activated_at)    tsEvents.push({ action:'activated', phase:'sdr_ai', created_at: c.sdr_ai_activated_at,    approver_name: c.sdr_ai_activated_by || null });

  // Merge com camp_approvals — evitar duplicar aprovações já cobertas pelas colunas de timestamp
  const coveredPhases = new Set(tsEvents.filter(e => e.action === 'approved').map(e => e.phase));
  const sdrAiCovered = tsEvents.some(e => e.phase === 'sdr_ai');
  const extraApprovals = (approvals || []).filter(a => {
    // Skip sdr_ai_activate log lines quando já temos o evento derivado da coluna timestamp
    if (a.phase === 'sdr_ai_activate' && sdrAiCovered && ['activated','deactivated'].includes(a.action)) return false;
    return a.action !== 'approved' || !coveredPhases.has(a.phase);
  });

  const allEvents = [...tsEvents, ...extraApprovals]
    .filter(e => e.created_at)
    .sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); // Mais recente primeiro

  if (!allEvents.length) return null;

  // Filtro
  const FILTERS = [
    { key:'all',       label:'Todos',            match: () => true },
    { key:'approvals', label:'Aprovações',       match: e => e.action === 'approved' || e.action === 'activated' },
    { key:'sdr',       label:'SDR AI · Ficha',   match: e => e.phase === 'sdr_ai' || e.phase === 'sdr_ai_activate' },
    { key:'issues',    label:'Rejeições / Alt.', match: e => ['rejected','changes_requested','request_changes','deactivated','reverted'].includes(e.action) },
  ];
  const activeFilter = FILTERS.find(f => f.key === filter) || FILTERS[0];
  const filteredEvents = allEvents.filter(activeFilter.match);

  return (
    <div>
      <div style={{ display:'flex', alignItems:'center', gap:12, marginBottom:12, flexWrap:'wrap' }}>
        <div style={{ fontSize:10, fontWeight:700, fontFamily:'var(--font-mono)', letterSpacing:'.08em', textTransform:'uppercase', color:'var(--text-dim)' }}>
          {sectionLabel || 'Histórico de Aprovações'} · {filteredEvents.length}{filteredEvents.length !== allEvents.length ? ` de ${allEvents.length}` : ''} eventos
        </div>
        <div style={{ display:'flex', gap:0, marginLeft:'auto', background:'var(--bg-sunken,#f1f5f9)', borderRadius:6, padding:2 }}>
          {FILTERS.map(f => {
            const active = filter === f.key;
            const count = allEvents.filter(f.match).length;
            return (
              <button
                key={f.key}
                onClick={() => setFilter(f.key)}
                disabled={count === 0 && f.key !== 'all'}
                style={{
                  padding:'4px 10px', fontSize:10, fontFamily:'var(--font-mono)', letterSpacing:'.02em',
                  fontWeight: active ? 700 : 500,
                  color: active ? '#3859D0' : count === 0 ? 'var(--text-dim)' : 'var(--text-muted)',
                  background: active ? 'var(--bg-surface,#fff)' : 'transparent',
                  border:'none', borderRadius:4,
                  cursor: count === 0 && f.key !== 'all' ? 'not-allowed' : 'pointer',
                  opacity: count === 0 && f.key !== 'all' ? .4 : 1,
                }}
              >
                {f.label} <span style={{ opacity:.6, marginLeft:2 }}>({count})</span>
              </button>
            );
          })}
        </div>
      </div>
      {filteredEvents.length === 0 && (
        <div style={{ padding:'20px 16px', textAlign:'center', fontSize:12, color:'var(--text-dim)', background:'var(--bg-sunken)', borderRadius:8, fontStyle:'italic' }}>
          Sem eventos para este filtro.
        </div>
      )}
      {filteredEvents.length > 0 && (
        <div style={{ position:'relative', paddingLeft:8 }}>
          {/* Linha vertical timeline */}
          <div style={{ position:'absolute', left:15, top:14, bottom:14, width:1, background:'var(--border)' }} />
          <div style={{ display:'flex', flexDirection:'column', gap:8 }}>
            {filteredEvents.map((a, i) => {
              const meta  = ACTION_META[a.action] || { label: a.action, col:'#64748b', bg:'var(--bg-sunken)', icon:'circle' };
              const name  = a.approver_name || a.approver_email?.split('@')[0] || null;
              const email = a.approver_email || null;
              const notes = a.notes || a.reject_notes || null;
              const phaseLabel = a.phase ? (PHASE_LABEL[a.phase] || a.phase) : null;
              return (
                <div key={i} style={{ display:'grid', gridTemplateColumns:'auto 1fr auto', gap:12, alignItems:'flex-start', padding:'10px 14px 10px 40px', background:meta.bg, border:'1px solid var(--border)', borderRadius:8, position:'relative' }}>
                  {/* Dot + icon na linha */}
                  <div style={{ position:'absolute', left:2, top:'50%', transform:'translateY(-50%)', width:26, height:26, borderRadius:'50%', background:'var(--bg-surface,#fff)', border:`2px solid ${meta.col}`, display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0, color:meta.col }}>
                    <window.Icon name={meta.icon} size={12}/>
                  </div>
                  <div /> {/* spacer for grid */}
                  <div>
                    <div style={{ display:'flex', alignItems:'center', gap:6, flexWrap:'wrap', marginBottom: (name || email) ? 4 : 0 }}>
                      <span style={{ fontSize:12, fontWeight:700, color:meta.col }}>{meta.label}</span>
                      {phaseLabel && <span style={{ fontSize:10, fontFamily:'var(--font-mono)', color:'var(--text-dim)', background:'rgba(0,0,0,.06)', padding:'1px 7px', borderRadius:4 }}>{phaseLabel}</span>}
                    </div>
                    {(name || email) && (
                      <div style={{ display:'flex', alignItems:'center', gap:6 }}>
                        <div style={{ width:16, height:16, borderRadius:'50%', background:'var(--bg-sunken)', border:'1px solid var(--border)', display:'flex', alignItems:'center', justifyContent:'center', fontSize:8, fontWeight:700, color:'var(--text-dim)', fontFamily:'var(--font-mono)', flexShrink:0 }}>
                          {(name || email || '?')[0].toUpperCase()}
                        </div>
                        {name && <span style={{ fontSize:11, fontWeight:600, color:'var(--text)' }}>{name}</span>}
                        {email && <span style={{ fontSize:10, color:'var(--text-muted)', marginLeft: name ? 4 : 0 }}>{email}</span>}
                      </div>
                    )}
                    {notes && (
                      <div style={{ fontSize:11, color:'#92400E', marginTop:5, lineHeight:1.5, fontStyle:'italic', background:'rgba(146,64,14,.06)', padding:'5px 8px', borderRadius:5 }}>"{notes}"</div>
                    )}
                  </div>
                  <div style={{ textAlign:'right', flexShrink:0 }}>
                    <div style={{ fontSize:10, color:'var(--text-muted)', fontWeight:600 }}>{timeAgo(a.created_at)}</div>
                    <div style={{ fontSize:9, color:'var(--text-dim)', marginTop:2, fontFamily:'var(--font-mono)' }}>{fmtExact(a.created_at)}</div>
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      )}
    </div>
  );
};



// ── EditConceitoModal — helpers ───────────────────────────────────────────────
const _EcmSection = ({ title }) => (
  <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: '#3859D0', fontFamily: 'monospace', padding: '10px 0 6px', borderBottom: '1px solid #e2e8f0', marginTop: 4 }}>{title}</div>
);
const _EcmLabel = ({ label, hint }) => (
  <div style={{ marginBottom: hint ? 2 : 5 }}>
    <div style={{ fontSize: 11, fontWeight: 600, color: '#6b7fa3', fontFamily: 'monospace', letterSpacing: '0.05em', textTransform: 'uppercase' }}>{label}</div>
    {hint && <div style={{ fontSize: 10, color: '#94a3b8', marginTop: 1 }}>{hint}</div>}
  </div>
);
const _EcmTa = ({ value, onChange, rows = 2, placeholder = '' }) => (
  <textarea value={value || ''} onChange={e => onChange(e.target.value)} rows={rows} placeholder={placeholder}
    style={{ width: '100%', padding: '8px 10px', boxSizing: 'border-box', background: '#f8fafc', border: '1px solid #e2e8f0', borderRadius: 6, fontSize: 12.5, color: '#1d2e38', fontFamily: 'inherit', outline: 'none', resize: 'vertical', lineHeight: 1.55 }} />
);
const _EcmIn = ({ value, onChange, placeholder = '' }) => (
  <input value={value || ''} onChange={e => onChange(e.target.value)} placeholder={placeholder}
    style={{ width: '100%', padding: '7px 10px', boxSizing: 'border-box', background: '#f8fafc', border: '1px solid #e2e8f0', borderRadius: 6, fontSize: 12.5, color: '#1d2e38', fontFamily: 'inherit', outline: 'none' }} />
);
const _EcmAddBtn = ({ label, onClick }) => (
  <button onClick={onClick} style={{ fontSize: 11, padding: '4px 10px', borderRadius: 5, background: 'none', border: '1px solid #dde3ef', cursor: 'pointer', color: '#3859D0', fontFamily: 'inherit', marginTop: 6 }}>+ {label}</button>
);
const _EcmRemBtn = ({ onClick }) => (
  <button onClick={onClick} style={{ fontSize: 11, padding: '2px 8px', borderRadius: 4, background: 'none', border: '1px solid #fecaca', cursor: 'pointer', color: '#dc2626', fontFamily: 'inherit', flexShrink: 0 }}>✕</button>
);
const _EcmCard = ({ children, extra }) => (
  <div style={{ border: '1px solid #e2e8f0', borderRadius: 8, padding: '12px 14px', background: '#fafbfc', display: 'flex', flexDirection: 'column', gap: 10, position: 'relative' }}>
    {extra && <div style={{ position: 'absolute', top: 8, right: 8 }}>{extra}</div>}
    {children}
  </div>
);

// ── EditConceitoModal ──────────────────────────────────────────────────────────
const EditConceitoModal = ({ campanha, onSave, onClose }) => {
  const isFinal      = campanha?.status === 'publicado';
  const isProduction = campanha?.status === 'em_producao';

  React.useEffect(() => {
    if (isFinal) { alert('Não é possível editar o conceito de campanhas publicadas.'); onClose(); }
  }, [isFinal]);
  if (isFinal) return null;

  const pj = campanha.proposta_json || {};
  const [saving, setSaving] = React.useState(false);

  // ── Estratégia Central (colunas + campos simples) ──
  const [bigIdea,     setBigIdea]     = React.useState(campanha.big_idea       || '');
  const [posic,       setPosic]       = React.useState(campanha.posicionamento || '');
  const [narrativa,   setNarrativa]   = React.useState(campanha.narrativa      || '');
  const [tom,         setTom]         = React.useState(campanha.tom_campanha   || '');
  const [keyMsg,      setKeyMsg]      = React.useState(pj.key_message          || '');
  const [anchor,      setAnchor]      = React.useState(pj.anchor_type          || '');

  // ── Core Promise + Comparison Baseline (novos campos — âncora universal dos copies) ──
  const [corePromise, setCorePromise] = React.useState(pj.core_promise || '');
  const _baseline = pj.comparison_baseline || {};
  const [basePrimary, setBasePrimary] = React.useState(_baseline.primary || '');
  const [baseAlt, setBaseAlt] = React.useState(Array.isArray(_baseline.alt_terms) ? _baseline.alt_terms.join(', ') : '');
  const [baseForb, setBaseForb] = React.useState(Array.isArray(_baseline.forbidden_confusions) ? _baseline.forbidden_confusions.join(', ') : '');

  // ── Personas ──
  const [personas, setPersonas] = React.useState(
    (pj.personas || []).map(p => ({ nome: p.nome||'', perfil: p.perfil||'', como_vive_a_dor: p.como_vive_a_dor||'', deepest_desire: p.deepest_desire||'' }))
  );
  const updPersona = (i, k, v) => setPersonas(ps => ps.map((p, j) => j === i ? { ...p, [k]: v } : p));

  // ── Messaging Angles ──
  const [angles, setAngles] = React.useState(
    (pj.messaging_angles || []).map(a => ({ persona: a.persona||'', dor_principal: a.dor_principal||'', usp_principal: a.usp_principal||'', angulo: a.angulo||'', rationale: a.rationale||'', awareness_stage: a.awareness_stage||'', awareness_nome: a.awareness_nome||'' }))
  );
  const updAngle = (i, k, v) => setAngles(as => as.map((a, j) => j === i ? { ...a, [k]: v } : a));

  // ── Hooks por Canal ──
  const [hooks, setHooks] = React.useState(() => {
    const h = pj.hooks || {};
    return Object.fromEntries(Object.entries(h).map(([ch, items]) => [ch, (items||[]).map(it => ({ texto: it.texto||'', tipo: it.tipo||'', target_stage: String(it.target_stage||'') }))]));
  });
  const updHook = (ch, i, k, v) => setHooks(h => ({ ...h, [ch]: h[ch].map((it, j) => j === i ? { ...it, [k]: v } : it) }));
  const addHook = (ch) => setHooks(h => ({ ...h, [ch]: [...(h[ch]||[]), { texto: '', tipo: '', target_stage: '' }] }));
  const remHook = (ch, i) => setHooks(h => ({ ...h, [ch]: h[ch].filter((_, j) => j !== i) }));

  const handleSave = async () => {
    setSaving(true);
    try {
      // rebuild hooks (preserve original extra fields)
      const hooksOut = {};
      Object.entries(hooks).forEach(([ch, items]) => {
        hooksOut[ch] = items.map((it, i) => {
          const orig = (pj.hooks?.[ch]?.[i]) || {};
          return { ...orig, texto: it.texto, tipo: it.tipo, target_stage: Number(it.target_stage)||orig.target_stage||2 };
        });
      });

      // Build comparison_baseline objecto — só envia se algum sub-campo tem valor
      const _altList = baseAlt.split(',').map(s => s.trim()).filter(Boolean);
      const _forbList = baseForb.split(',').map(s => s.trim()).filter(Boolean);
      const _baselineOut = (basePrimary.trim() || _altList.length || _forbList.length)
        ? { primary: basePrimary.trim() || null, alt_terms: _altList, forbidden_confusions: _forbList }
        : null;

      const payload = {
        action: 'editar',
        big_idea: bigIdea, posicionamento: posic, narrativa, tom_campanha: tom,
        key_message: keyMsg, anchor_type: anchor,
        core_promise: corePromise.trim() || null,
        comparison_baseline: _baselineOut,
        personas: personas.map((p, i) => ({ ...(pj.personas?.[i]||{}), ...p })),
        messaging_angles: angles.map((a, i) => ({ ...(pj.messaging_angles?.[i]||{}), ...a, awareness_stage: Number(a.awareness_stage)||a.awareness_stage })),
        hooks: hooksOut,
      };
      await CampAPI.patchConceito(campanha.id, payload);
      onSave(); onClose();
    } catch { setSaving(false); }
  };

  const hookChannels = Object.keys(hooks);

  return (
    <div style={{ position: 'fixed', inset: 0, zIndex: 9999, background: 'rgba(0,0,0,.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20 }}
      onClick={e => { if (e.target === e.currentTarget) onClose(); }}>
      <div style={{ background: '#fff', borderRadius: 12, width: '100%', maxWidth: 860, maxHeight: '92vh', display: 'flex', flexDirection: 'column', boxShadow: '0 24px 64px rgba(0,0,0,.25)' }}>

        {/* Header */}
        <div style={{ padding: '18px 24px 14px', borderBottom: '1px solid #e2e8f0', display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexShrink: 0 }}>
          <div>
            <div style={{ fontSize: 10, color: '#94a3b8', fontFamily: 'monospace', letterSpacing: '0.08em', marginBottom: 3 }}>EDITAR CONCEITO</div>
            <div style={{ fontSize: 15, fontWeight: 700, color: '#1d2e38', fontFamily: 'var(--font-display, Montserrat, sans-serif)' }}>{campanha.titulo}</div>
          </div>
          <button onClick={onClose} style={{ background: 'none', border: 'none', fontSize: 20, cursor: 'pointer', color: '#64748b', lineHeight: 1, padding: 4 }}>✕</button>
        </div>

        {/* Body */}
        <div className="scrollbar" style={{ flex: 1, overflowY: 'auto', padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 12 }}>
          {isProduction && (
            <div style={{ background: 'rgba(220,38,38,0.08)', border: '1px solid rgba(220,38,38,0.3)', borderRadius: 8, padding: '10px 14px', fontSize: 12, color: '#dc2626', lineHeight: 1.5 }}>
              <strong>Atenção:</strong> Campanha em produção. Editar pode dessincronizar copy e prompts já aprovados.
            </div>
          )}

          {/* ── Estratégia Central ── */}
          <_EcmSection title="Estratégia Central" />
          <_EcmLabel label="Big Idea" hint="Metáfora criativa central da campanha" />
          <_EcmTa value={bigIdea} onChange={setBigIdea} rows={3} />
          <_EcmLabel label="Posicionamento" hint="Diferenciação + awareness stage" />
          <_EcmTa value={posic} onChange={setPosic} rows={3} />
          <_EcmLabel label="Narrativa" hint="Arco Antes → Durante → Depois" />
          <_EcmTa value={narrativa} onChange={setNarrativa} rows={4} />
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
            <div><_EcmLabel label="Tom da Campanha" /><_EcmTa value={tom} onChange={setTom} rows={2} /></div>
            <div><_EcmLabel label="Anchor Type" hint="pain · desire · contrarian" /><_EcmIn value={anchor} onChange={setAnchor} /></div>
          </div>
          <_EcmLabel label="Mensagem-Chave" hint="Mensagem transversal da campanha" />
          <_EcmTa value={keyMsg} onChange={setKeyMsg} rows={2} />

          {/* ── Core Promise + Comparison Baseline (âncora universal dos copies) ── */}
          <_EcmLabel label="Core Promise" hint="Tese central destilada (8-14 palavras) — reforçada por todos os copies com ângulos diferentes" />
          <_EcmTa value={corePromise} onChange={setCorePromise} rows={2} placeholder="Ex: Sustentável = mais rentável, mais fácil, mais competitivo vs vinil polimérico" />
          <_EcmLabel label="Comparison Baseline · Primary" hint="Produto/método contra o qual todos os claims relativos comparam (vazio = claims absolutos)" />
          <_EcmIn value={basePrimary} onChange={setBasePrimary} placeholder="Ex: vinil polimérico convencional" />
          <_EcmLabel label="Alt Terms (CSV)" hint="Sinónimos aceitáveis do baseline, separados por vírgula" />
          <_EcmIn value={baseAlt} onChange={setBaseAlt} placeholder="vinil polimérico, vinil PVC monomérico" />
          <_EcmLabel label="Forbidden Confusions (CSV)" hint="Termos proibidos por criarem confusão semântica" />
          <_EcmIn value={baseForb} onChange={setBaseForb} placeholder="vinil monomérico, PVC-free, plástico genérico" />

          {/* ── Personas ── */}
          <_EcmSection title="Personas" />
          {personas.map((p, i) => (
            <_EcmCard key={i} extra={<_EcmRemBtn onClick={() => setPersonas(ps => ps.filter((_, j) => j !== i))} />}>
              <div style={{ fontSize: 11, fontWeight: 700, color: '#3859D0', fontFamily: 'monospace' }}>Persona {i + 1}</div>
              <_EcmLabel label="Nome" /><_EcmIn value={p.nome} onChange={v => updPersona(i, 'nome', v)} />
              <_EcmLabel label="Perfil" /><_EcmTa value={p.perfil} onChange={v => updPersona(i, 'perfil', v)} rows={2} />
              <_EcmLabel label="Como vive a dor" /><_EcmTa value={p.como_vive_a_dor} onChange={v => updPersona(i, 'como_vive_a_dor', v)} rows={2} />
              <_EcmLabel label="Deepest Desire" /><_EcmTa value={p.deepest_desire} onChange={v => updPersona(i, 'deepest_desire', v)} rows={2} />
            </_EcmCard>
          ))}
          <_EcmAddBtn label="Adicionar Persona" onClick={() => setPersonas(ps => [...ps, { nome:'', perfil:'', como_vive_a_dor:'', deepest_desire:'' }])} />

          {/* ── Ângulos de Mensagem ── */}
          <_EcmSection title="Ângulos de Mensagem" />
          {angles.map((a, i) => (
            <_EcmCard key={i} extra={<_EcmRemBtn onClick={() => setAngles(as => as.filter((_, j) => j !== i))} />}>
              <div style={{ fontSize: 11, fontWeight: 700, color: '#3859D0', fontFamily: 'monospace' }}>Ângulo {i + 1}</div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
                <div><_EcmLabel label="Persona" /><_EcmIn value={a.persona} onChange={v => updAngle(i,'persona',v)} /></div>
                <div><_EcmLabel label="Awareness Stage" hint="1-5" /><_EcmIn value={a.awareness_stage} onChange={v => updAngle(i,'awareness_stage',v)} placeholder="2" /></div>
              </div>
              <_EcmLabel label="Dor Principal" /><_EcmTa value={a.dor_principal} onChange={v => updAngle(i,'dor_principal',v)} rows={2} />
              <_EcmLabel label="USP Principal" /><_EcmTa value={a.usp_principal} onChange={v => updAngle(i,'usp_principal',v)} rows={2} />
              <_EcmLabel label="Ângulo" /><_EcmTa value={a.angulo} onChange={v => updAngle(i,'angulo',v)} rows={2} />
              <_EcmLabel label="Rationale" /><_EcmTa value={a.rationale} onChange={v => updAngle(i,'rationale',v)} rows={2} />
            </_EcmCard>
          ))}
          <_EcmAddBtn label="Adicionar Ângulo" onClick={() => setAngles(as => [...as, { persona:'', dor_principal:'', usp_principal:'', angulo:'', rationale:'', awareness_stage:'', awareness_nome:'' }])} />

          {/* ── Hooks por Canal ── */}
          <_EcmSection title="Hooks por Canal" />
          {hookChannels.map(ch => (
            <div key={ch}>
              <div style={{ fontSize: 11, fontWeight: 700, color: '#475569', fontFamily: 'monospace', marginBottom: 6, textTransform: 'uppercase' }}>{ch}</div>
              {(hooks[ch]||[]).map((h, i) => (
                <_EcmCard key={i} extra={<_EcmRemBtn onClick={() => remHook(ch, i)} />}>
                  <_EcmLabel label="Texto do Hook" />
                  <_EcmTa value={h.texto} onChange={v => updHook(ch, i, 'texto', v)} rows={2} />
                  <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
                    <div><_EcmLabel label="Tipo" hint="Pain Agitation, Curiosity Gap…" /><_EcmIn value={h.tipo} onChange={v => updHook(ch, i, 'tipo', v)} /></div>
                    <div><_EcmLabel label="Target Stage" hint="1-5" /><_EcmIn value={h.target_stage} onChange={v => updHook(ch, i, 'target_stage', v)} placeholder="2" /></div>
                  </div>
                </_EcmCard>
              ))}
              <_EcmAddBtn label={`Adicionar hook ${ch}`} onClick={() => addHook(ch)} />
            </div>
          ))}

        </div>

        {/* Footer */}
        <div style={{ padding: '14px 24px', borderTop: '1px solid #e2e8f0', display: 'flex', gap: 8, justifyContent: 'flex-end', flexShrink: 0 }}>
          <button onClick={onClose} className="btn" style={{ height: 32, padding: '0 16px', fontSize: 12 }}>Cancelar</button>
          <button onClick={handleSave} disabled={saving} className="btn btn-ai" style={{ height: 32, padding: '0 18px', fontSize: 12 }}>
            {saving ? 'A guardar…' : '✓ Guardar alterações'}
          </button>
        </div>
      </div>
    </div>
  );
};

// ── EditTituloModal ────────────────────────────────────────────────────────────
const EditTituloModal = ({ campanha, onSave, onClose }) => {
  const [titulo, setTitulo] = React.useState(campanha.titulo);
  const [saving, setSaving] = React.useState(false);
  const inputRef = React.useRef(null);
  React.useEffect(() => { inputRef.current?.focus(); inputRef.current?.select(); }, []);
  const save = async () => {
    if (!titulo.trim() || titulo.trim() === campanha.titulo) { onClose(); return; }
    setSaving(true);
    await CampAPI.update(campanha.id, { titulo: titulo.trim() });
    onSave();
    onClose();
  };
  return (
    <>
      <div onClick={onClose} style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.5)', zIndex: 300 }} />
      <div style={{
        position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%,-50%)', zIndex: 301,
        background: '#ffffff', border: '1px solid var(--border, #e2e8f0)',
        borderRadius: 10, padding: '22px 24px', width: 400,
        boxShadow: '0 16px 48px rgba(0,0,0,.5)',
      }}>
        <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--text, #1e293b)', marginBottom: 14, fontFamily: 'var(--font-display, Montserrat, sans-serif)' }}>
          Editar título
        </div>
        <input ref={inputRef} value={titulo} onChange={e => setTitulo(e.target.value)}
          onKeyDown={e => { if (e.key === 'Enter') save(); if (e.key === 'Escape') onClose(); }}
          style={{
            width: '100%', boxSizing: 'border-box', padding: '9px 12px', borderRadius: 6, fontSize: 13,
            background: 'var(--bg-sunken, #f1f5f9)', border: '1px solid var(--border, #e2e8f0)',
            color: 'var(--text, #1e293b)', outline: 'none', fontFamily: 'var(--font-body, Inter, sans-serif)',
            marginBottom: 14,
          }}
        />
        <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
          <button onClick={onClose} className="btn" style={{ fontSize: 12 }}>Cancelar</button>
          <button onClick={save} disabled={saving || !titulo.trim()} className="btn btn-ai" style={{ fontSize: 12 }}>
            {saving ? 'A guardar…' : 'Guardar'}
          </button>
        </div>
      </div>
    </>
  );
};

// ── EmailBriefingModal ─────────────────────────────────────────────────────────
const EmailBriefingModal = ({ campanha, briefing, onClose }) => {
  const [search,        setSearch]        = React.useState('');
  const [selected,      setSelected]      = React.useState([]);
  const [externalInput, setExternalInput] = React.useState('');
  const [externalOpen,  setExternalOpen]  = React.useState(false);
  const [externalError, setExternalError] = React.useState('');
  const [showAll,       setShowAll]       = React.useState(false);
  const [allUsers,      setAllUsers]      = React.useState([]);
  const searchRef = React.useRef(null);

  // Carrega utilizadores do sistema (AdminUsersData pode estar pronto ou chegar via evento)
  React.useEffect(() => {
    const load = () => {
      const D = window.AdminUsersData;
      if (D && D.ALL_USERS && D.ALL_USERS.length) {
        setAllUsers(D.ALL_USERS.slice());
      }
    };
    load();
    window.addEventListener('digi-users-loaded', load);
    return () => window.removeEventListener('digi-users-loaded', load);
  }, []);

  React.useEffect(() => {
    setTimeout(() => searchRef.current && searchRef.current.focus(), 80);
  }, []);

  // Fechar com Escape
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [onClose]);

  const baseUsers = showAll ? allUsers : allUsers.filter(u => u.portal_activo);

  const filteredUsers = baseUsers.filter(u => {
    if (!search.trim()) return true;
    const q = search.toLowerCase();
    return (u.nome_apresentar || '').toLowerCase().includes(q)
        || (u.email_profissional || '').toLowerCase().includes(q)
        || (u.perfil_cargo || '').toLowerCase().includes(q);
  });

  const toggle = (u) => {
    setSelected(prev =>
      prev.find(s => s.id === u.id)
        ? prev.filter(s => s.id !== u.id)
        : [...prev, { id: u.id, nome: u.nome_apresentar, email: u.email_profissional }]
    );
  };

  const removeSelected = (id) => setSelected(prev => prev.filter(s => s.id !== id));

  const addExternal = () => {
    const v = externalInput.trim();
    if (!v) return;
    const emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRe.test(v)) { setExternalError('Email inválido.'); return; }
    if (selected.find(s => s.email === v)) { setExternalError('Já adicionado.'); return; }
    setSelected(prev => [...prev, { id: `ext_${v}`, nome: v, email: v, external: true }]);
    setExternalInput('');
    setExternalError('');
    setExternalOpen(false);
  };

  const buildEmailBody = () => {
    if (!briefing) return '';
    const b1 = briefing.block1 || {}, b2 = briefing.block2 || {},
          b3 = briefing.block3 || {}, b4 = briefing.block4 || {}, b5 = briefing.block5 || {};
    const arr = (v) => Array.isArray(v) && v.length ? v.join(', ') : '—';
    return [
      `BRIEFING — ${campanha.titulo}`,
      `Marca: ${campanha.brand_name || '—'}`,
      '',
      '── PRODUTO ──',
      `Nome comercial: ${b1.commercial_name || '—'}`,
      `Elevator pitch: ${b1.elevator_pitch || '—'}`,
      `USPs: ${arr(b1.usps)}`,
      `Aplicações: ${arr(b1.applications)}`,
      `Mercados: ${arr(b1.geo_markets)}`,
      '',
      '── CLIENTE-ALVO ──',
      `Decisor: ${b2.decision_maker || '—'}`,
      `Utilizador final: ${b2.end_user || '—'}`,
      `Dores: ${arr(b2.pain_points)}`,
      `Motivadores: ${arr(b2.motivators)}`,
      `Objecções: ${arr(b2.objections)}`,
      `Trigger de compra: ${b2.purchase_trigger || '—'}`,
      '',
      '── MERCADO ──',
      `Concorrentes: ${arr(b3.competitors)}`,
      `Diferenciação: ${arr(b3.differentiation_args)}`,
      `Tendências: ${arr(b3.market_trends)}`,
      `Posicionamento: ${b3.positioning_narrative || '—'}`,
      '',
      '── CAMPANHA ──',
      `Objectivo: ${b4.objective || '—'}`,
      `KPIs: ${formatKpis(b4.kpis)}`,
      `Canais: ${arr(b4.channels)}`,
      `Tom: ${b4.tone || '—'}`,
      `Mensagem-chave: ${b4.key_message || '—'}`,
      `Período: ${b4.timeline_start || '—'} → ${b4.timeline_end || '—'}`,
      '',
      '── RESTRIÇÕES ──',
      `Claims proibidos: ${arr(b5.prohibited_claims)}`,
      `Mensagens fora de posicionamento: ${arr(b5.off_brand_messages)}`,
      `Concorrentes a não nomear: ${arr(b5.competitors_not_name)}`,
    ].join('\n');
  };

  // TODO (Paulino): substituir esta função por POST /api/marketing/briefings/:id/send-email
  // com body { recipients: selected.map(s => s.email), subject, body }
  // e remover o window.open abaixo quando o SMTP estiver configurado.
  const handleSend = () => {
    if (!selected.length) return;
    const to = selected.map(s => s.email).join(',');
    const subject = encodeURIComponent(`Briefing — ${campanha.titulo}`);
    const body    = encodeURIComponent(buildEmailBody());
    window.open(`mailto:${to}?subject=${subject}&body=${body}`);
    onClose();
  };

  const TIPO_LABEL = {
    admin: 'Admin', c_suite: 'C-Suite', director: 'Director', manager: 'Manager',
    comercial: 'Comercial', tecnico: 'Técnico', marketing: 'Marketing',
    operacional: 'Operacional', administrativo: 'Administrativo',
  };

  const initials = (nome) => {
    const parts = (nome || '').split(' ').filter(Boolean);
    if (parts.length >= 2) return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
    return (nome || '?')[0].toUpperCase();
  };

  return ReactDOM.createPortal(
    <div style={{
      position: 'fixed', inset: 0, zIndex: 10000,
      background: 'rgba(17,41,84,0.45)', backdropFilter: 'blur(2px)',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      padding: 24,
    }} onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>

      <div style={{
        width: '100%', maxWidth: 520,
        background: '#ffffff', borderRadius: 14,
        boxShadow: '0 24px 64px rgba(17,41,84,0.18)',
        display: 'flex', flexDirection: 'column',
        maxHeight: 'calc(100vh - 48px)', overflow: 'hidden',
      }}>

        {/* Header */}
        <div style={{ padding: '20px 24px 16px', borderBottom: '1px solid var(--border, #ECEFF5)', flexShrink: 0 }}>
          <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 12 }}>
            <div>
              <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--text, #283252)', fontFamily: 'var(--font-display, Montserrat, sans-serif)', lineHeight: 1.3 }}>
                Enviar Briefing por Email
              </div>
              <div style={{ fontSize: 11.5, color: 'var(--text-muted, #94A4C4)', marginTop: 3, fontFamily: 'var(--font-body, Inter, sans-serif)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: 380 }}>
                {campanha.titulo}
              </div>
            </div>
            <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-muted, #94A4C4)', padding: '2px 4px', fontSize: 18, lineHeight: 1, marginTop: -2, flexShrink: 0 }}>✕</button>
          </div>

          {/* Search */}
          <div style={{ marginTop: 14, position: 'relative' }}>
            <svg style={{ position: 'absolute', left: 10, top: '50%', transform: 'translateY(-50%)', color: 'var(--text-muted, #94A4C4)', pointerEvents: 'none' }} width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
            <input
              ref={searchRef}
              value={search}
              onChange={e => setSearch(e.target.value)}
              placeholder="Pesquisar por nome ou email…"
              style={{
                width: '100%', boxSizing: 'border-box',
                padding: '8px 12px 8px 32px',
                border: '1px solid var(--border, #ECEFF5)', borderRadius: 8,
                fontSize: 13, fontFamily: 'var(--font-body, Inter, sans-serif)',
                color: 'var(--text, #283252)', background: 'var(--bg-app, #F5F6F8)',
                outline: 'none',
              }}
            />
          </div>
        </div>

        {/* Selected chips */}
        {selected.length > 0 && (
          <div style={{ padding: '10px 24px', borderBottom: '1px solid var(--border, #ECEFF5)', display: 'flex', flexWrap: 'wrap', gap: 6, flexShrink: 0 }}>
            {selected.map(s => (
              <div key={s.id} style={{
                display: 'inline-flex', alignItems: 'center', gap: 5,
                background: 'rgba(56,89,208,0.08)', border: '1px solid rgba(56,89,208,0.18)',
                borderRadius: 99, padding: '3px 8px 3px 10px',
                fontSize: 12, color: 'var(--ai-500, #3859D0)', fontFamily: 'var(--font-body, Inter, sans-serif)', fontWeight: 500,
              }}>
                {s.nome}
                <button onClick={() => removeSelected(s.id)} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--ai-500, #3859D0)', padding: 0, fontSize: 13, lineHeight: 1, opacity: 0.7, display: 'flex', alignItems: 'center' }}>✕</button>
              </div>
            ))}
          </div>
        )}

        {/* User list */}
        <div className="scrollbar" style={{ flex: 1, overflowY: 'auto', minHeight: 0 }}>
          {allUsers.length === 0 ? (
            <div style={{ padding: '32px 24px', textAlign: 'center', fontSize: 13, color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>
              A carregar utilizadores…
            </div>
          ) : filteredUsers.length === 0 ? (
            <div style={{ padding: '32px 24px', textAlign: 'center', fontSize: 13, color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>
              Sem resultados para "{search}".
            </div>
          ) : (
            <div style={{ padding: '6px 0' }}>
              {filteredUsers.map(u => {
                const isSelected = !!selected.find(s => s.id === u.id);
                return (
                  <button key={u.id} onClick={() => toggle(u)} style={{
                    width: '100%', background: isSelected ? 'rgba(56,89,208,0.05)' : 'none',
                    border: 'none', padding: '9px 24px', cursor: 'pointer',
                    display: 'flex', alignItems: 'center', gap: 12, textAlign: 'left',
                    transition: 'background .1s',
                  }}
                  onMouseEnter={e => { if (!isSelected) e.currentTarget.style.background = 'var(--bg-app, #F5F6F8)'; }}
                  onMouseLeave={e => { e.currentTarget.style.background = isSelected ? 'rgba(56,89,208,0.05)' : 'none'; }}
                  >
                    {/* Avatar */}
                    <div style={{ width: 34, height: 34, borderRadius: '50%', flexShrink: 0, overflow: 'hidden', background: 'var(--border, #ECEFF5)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                      {u.foto_url
                        ? <img src={u.foto_url} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
                        : <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{initials(u.nome_apresentar)}</span>
                      }
                    </div>
                    {/* Info */}
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--text, #283252)', fontFamily: 'var(--font-body, Inter, sans-serif)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{u.nome_apresentar}</div>
                      <div style={{ fontSize: 11, color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                        {u.perfil_cargo || TIPO_LABEL[u.tipo] || ''}
                        {u.email_profissional && <span style={{ marginLeft: 6, opacity: 0.75 }}>· {u.email_profissional}</span>}
                      </div>
                    </div>
                    {/* Checkbox */}
                    <div style={{
                      width: 18, height: 18, borderRadius: 5, flexShrink: 0,
                      border: `2px solid ${isSelected ? 'var(--ai-500, #3859D0)' : 'var(--border, #ECEFF5)'}`,
                      background: isSelected ? 'var(--ai-500, #3859D0)' : '#ffffff',
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                      transition: 'all .1s',
                    }}>
                      {isSelected && <svg width="10" height="10" viewBox="0 0 12 12" fill="none"><polyline points="2,6 5,9 10,3" stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>}
                    </div>
                  </button>
                );
              })}
            </div>
          )}
        </div>

        {/* Footer */}
        <div style={{ borderTop: '1px solid var(--border, #ECEFF5)', padding: '12px 24px', flexShrink: 0, display: 'flex', flexDirection: 'column', gap: 10 }}>

          {/* Email externo */}
          {externalOpen ? (
            <div style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
              <div style={{ flex: 1 }}>
                <input
                  value={externalInput}
                  onChange={e => { setExternalInput(e.target.value); setExternalError(''); }}
                  onKeyDown={e => e.key === 'Enter' && addExternal()}
                  placeholder="email@empresa.pt"
                  style={{
                    width: '100%', boxSizing: 'border-box',
                    padding: '7px 10px', border: `1px solid ${externalError ? '#CF2E2E' : 'var(--border, #ECEFF5)'}`,
                    borderRadius: 7, fontSize: 12.5, fontFamily: 'var(--font-body, Inter, sans-serif)',
                    color: 'var(--text, #283252)', background: 'var(--bg-app, #F5F6F8)', outline: 'none',
                  }}
                  autoFocus
                />
                {externalError && <div style={{ fontSize: 11, color: '#CF2E2E', marginTop: 3, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{externalError}</div>}
              </div>
              <button onClick={addExternal} className="btn btn-xs btn-ai">Adicionar</button>
              <button onClick={() => { setExternalOpen(false); setExternalError(''); setExternalInput(''); }} className="btn btn-xs">Cancelar</button>
            </div>
          ) : (
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
              <button
                onClick={() => setExternalOpen(true)}
                style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 12, color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)', padding: 0, display: 'flex', alignItems: 'center', gap: 4 }}
              >
                <span style={{ fontSize: 14, lineHeight: 1 }}>+</span> Adicionar email externo
              </button>
              {!showAll && allUsers.some(u => !u.portal_activo) && (
                <button
                  onClick={() => setShowAll(true)}
                  style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 11.5, color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)', padding: 0 }}
                >
                  Mostrar todos ({allUsers.length})
                </button>
              )}
            </div>
          )}

          {/* Actions */}
          <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
            <button onClick={onClose} className="btn" style={{ height: 34, padding: '0 16px', fontSize: 13 }}>Cancelar</button>
            <button
              onClick={handleSend}
              disabled={!selected.length}
              className="btn btn-ai"
              style={{ height: 34, padding: '0 18px', fontSize: 13, opacity: selected.length ? 1 : 0.45 }}
            >
              Abrir no Email →
            </button>
          </div>
        </div>

      </div>
    </div>,
    document.body
  );
};

// ── BriefingResumo ─────────────────────────────────────────────────────────────
const BriefingResumo = ({ campanha, onBack, onRefresh }) => {
  const [briefing,    setBriefing]    = React.useState(null);
  const [loading,     setLoading]     = React.useState(true);
  const [generating,  setGenerating]  = React.useState(false);
  const [emailModal,  setEmailModal]  = React.useState(false);
  const bCol = _brandColor(campanha.brand_slug);

  React.useEffect(() => {
    if (!campanha.briefing_id) { setLoading(false); return; }
    CampAPI.getBriefing(campanha.briefing_id)
      .then(d => setBriefing(d))
      .catch(() => {})
      .finally(() => setLoading(false));
  }, [campanha.briefing_id]);

  const handleGerar = async () => {
    setGenerating(true);
    try { await CampAPI.generateConceito(campanha.id); } catch (e) {}
    setGenerating(false);
    onRefresh();
    onBack();
  };

  // Helpers para texto narrativo
  const str = (v) => v || null;
  const list = (v) => Array.isArray(v) && v.length ? v : null;
  const joinNarr = (items, prefix) => {
    if (!items || !items.length) return null;
    const clean = items.map(i => typeof i === 'object' ? (i.name || i.label || '') : i).filter(Boolean);
    if (!clean.length) return null;
    if (clean.length === 1) return `${prefix} ${clean[0]}.`;
    return `${prefix} ${clean.slice(0, -1).join(', ')} e ${clean[clean.length - 1]}.`;
  };

  const Section = ({ title, num, children }) => (
    <div style={{ background: '#ffffff', border: '1px solid var(--border, #ECEFF5)', borderRadius: 10, padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 16 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
        <div style={{ width: 22, height: 22, borderRadius: '50%', background: bCol, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
          <span style={{ fontSize: 10, fontWeight: 700, color: '#fff', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{num}</span>
        </div>
        <div style={{ fontSize: 12, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: bCol, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{title}</div>
      </div>
      {children}
    </div>
  );

  const Row = ({ label, value }) => value ? (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
      <div style={{ fontSize: 10, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{label}</div>
      <div style={{ fontSize: 13.5, color: 'var(--text, #283252)', lineHeight: 1.65, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{value}</div>
    </div>
  ) : null;

  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: 'var(--bg-app, #F5F6F8)' }}>

      {/* ── Page Header ── */}
      <div style={{ background: '#ffffff', borderBottom: '1px solid var(--border, #ECEFF5)', padding: '20px 40px 20px', flexShrink: 0 }}>

        <div style={{ fontSize: 11, fontWeight: 600, letterSpacing: '0.07em', color: 'var(--text-muted, #94A4C4)', textTransform: 'uppercase', marginBottom: 6, fontFamily: 'var(--font-mono, monospace)' }}>
          MARKETING · CAMPANHAS
        </div>

        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16 }}>
          <div>
            <h1 style={{ margin: 0, fontSize: 21, fontWeight: 700, color: 'var(--text, #283252)', fontFamily: 'var(--font-display, Montserrat, sans-serif)', letterSpacing: '-0.01em', lineHeight: 1.2 }}>
              {campanha.titulo}
            </h1>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6, flexWrap: 'wrap' }}>
              <CampStatusBadge status={campanha.status || 'conceito_pendente'} />
              {campanha.brand_name && (
                <span style={{ fontSize: 11, color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>
                  {campanha.brand_name}
                </span>
              )}
            </div>
          </div>
          <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexShrink: 0, marginTop: 2 }}>
            <button onClick={onBack} className="btn" style={{ height: 30, padding: '0 12px', fontSize: 12 }}>← Voltar</button>
            <button onClick={() => setEmailModal(true)} disabled={!briefing} className="btn" style={{ height: 30, padding: '0 12px', fontSize: 12 }}>Enviar por Email</button>
            <button onClick={handleGerar} disabled={generating || !briefing} className="btn btn-ai" style={{ height: 30, padding: '0 14px', fontSize: 12 }}>
              {generating ? 'A gerar…' : 'Gerar Conceito'}
            </button>
          </div>
        </div>
      </div>

      {/* Conteúdo scrollável */}
      <div className="scrollbar" style={{ flex: 1, overflowY: 'auto' }}>
        <div style={{ display: 'flex', gap: 20, padding: '24px 40px 40px', alignItems: 'flex-start' }}>

          {loading ? (
            <div style={{ flex: 1, padding: 32, fontSize: 13, color: 'var(--text-muted, #94A4C4)' }}>A carregar briefing…</div>
          ) : !briefing ? (
            <div style={{ flex: 1, padding: 32, fontSize: 13, color: 'var(--text-muted, #94A4C4)' }}>Briefing não encontrado.</div>
          ) : (
            <>
              {/* Coluna principal */}
              <div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 12 }}>

                {/* B1 — Produto */}
                {briefing.block1 && (
                  <Section title="Produto" num="1">
                    {str(briefing.block1.elevator_pitch) && (
                      <Row label="Em resumo" value={briefing.block1.elevator_pitch} />
                    )}
                    {list(briefing.block1.usps) && (
                      <Row label="Vantagens competitivas" value={joinNarr(briefing.block1.usps, 'O produto destaca-se por')} />
                    )}
                    {list(briefing.block1.applications) && (
                      <Row label="Aplicações" value={joinNarr(briefing.block1.applications, 'Indicado para')} />
                    )}
                    {list(briefing.block1.geo_markets) && (
                      <Row label="Mercados" value={joinNarr(briefing.block1.geo_markets, 'Actua em')} />
                    )}
                  </Section>
                )}

                {/* B2 — Cliente-alvo */}
                {briefing.block2 && (
                  <Section title="Cliente-alvo" num="2">
                    {(str(briefing.block2.decision_maker) || str(briefing.block2.end_user)) && (
                      <Row label="Quem decide e quem usa"
                        value={[briefing.block2.decision_maker && `O decisor de compra é o ${briefing.block2.decision_maker}`, briefing.block2.end_user && `quem utiliza o produto no dia-a-dia é o ${briefing.block2.end_user}`].filter(Boolean).join('. ') + '.'}
                      />
                    )}
                    {list(briefing.block2.pain_points) && (
                      <Row label="Principais desafios" value={joinNarr(briefing.block2.pain_points, 'Os principais problemas que enfrentam são')} />
                    )}
                    {list(briefing.block2.motivators) && (
                      <Row label="O que os motiva a comprar" value={joinNarr(briefing.block2.motivators, 'Os motivadores de decisão são')} />
                    )}
                    {list(briefing.block2.objections) && (
                      <Row label="Objecções a antecipar" value={joinNarr(briefing.block2.objections, 'As objecções mais comuns são')} />
                    )}
                    {str(briefing.block2.purchase_trigger) && (
                      <Row label="O que precipita a decisão" value={briefing.block2.purchase_trigger} />
                    )}
                  </Section>
                )}

                {/* B3 — Mercado */}
                {briefing.block3 && (
                  <Section title="Mercado" num="3">
                    {list(briefing.block3.competitors) && (
                      <Row label="Concorrentes directos" value={joinNarr(briefing.block3.competitors, 'Competimos directamente com')} />
                    )}
                    {list(briefing.block3.differentiation_args) && (
                      <Row label="Como nos diferenciamos" value={joinNarr(briefing.block3.differentiation_args, 'Os nossos argumentos de diferenciação são')} />
                    )}
                    {list(briefing.block3.market_trends) && (
                      <Row label="Tendências relevantes" value={joinNarr(briefing.block3.market_trends, 'O mercado está a ser influenciado por')} />
                    )}
                    {str(briefing.block3.positioning_narrative) && (
                      <Row label="Posicionamento desejado" value={briefing.block3.positioning_narrative} />
                    )}
                  </Section>
                )}

                {/* B4 — Campanha */}
                {briefing.block4 && (
                  <Section title="Campanha" num="4">
                    {str(briefing.block4.key_message) && (
                      <Row label="Mensagem central" value={briefing.block4.key_message} />
                    )}
                    {str(briefing.block4.objective) && (
                      <Row label="Objectivo" value={(() => { const m = { awareness: 'Awareness — aumentar reconhecimento de marca', lead_gen: 'Geração de leads qualificados', conversion: 'Conversão directa', retention: 'Retenção e upsell de clientes existentes' }; return m[briefing.block4.objective] || briefing.block4.objective; })()} />
                    )}
                    {list(briefing.block4.kpis) && (
                      <Row label="KPIs de sucesso" value={formatKpis(briefing.block4.kpis)} />
                    )}
                    {str(briefing.block4.tone) && (
                      <Row label="Tom e linguagem" value={briefing.block4.tone} />
                    )}
                    {list(briefing.block4.channels) && (
                      <Row label="Canais" value={joinNarr(briefing.block4.channels, 'A campanha vai estar presente em')} />
                    )}
                    {(str(briefing.block4.timeline_start) || str(briefing.block4.timeline_end)) && (
                      <Row label="Período" value={[briefing.block4.timeline_start, briefing.block4.timeline_end].filter(Boolean).join(' → ')} />
                    )}
                    {briefing.block4.commercial_offer && (() => {
                      const o = briefing.block4.commercial_offer;
                      const tl = { digirent:'Digirent', printplan:'PrintPlan', voucher:'Voucher on Demand', direct_discount:'Desconto Directo', trade_in:'Trade-In' };
                      const cl = { quote_request:'pedido de proposta', whatsapp:'WhatsApp', form:'formulário', call:'chamada directa', landing:'landing page' };
                      const tm = { standard:'Standard', premium:'Premium', tailor_made:'Tailor-Made', tailor_made_rappel:'Tailor-Made (entrada Rappel)', tailor_made_renda:'Tailor-Made (entrada + renda)' };
                      const parts = [`${tl[o.type] || o.type}${o.tier ? ' · ' + (tm[o.tier] || o.tier) : ''}`];
                      if (o.details) parts.push(o.details);
                      const ctaStr = o.primary_cta && o.primary_cta !== 'none' ? `CTA: ${cl[o.primary_cta] || o.primary_cta}` : null;
                      const val = [parts.join(' — '), ctaStr, o.negotiable ? 'Tier negociável.' : null].filter(Boolean).join('. ');
                      return <Row label="Oferta comercial" value={val} />;
                    })()}
                  </Section>
                )}

                {/* B5 — Restrições */}
                {briefing.block5 && (list(briefing.block5.prohibited_claims) || list(briefing.block5.off_brand_messages) || list(briefing.block5.competitors_not_name)) && (
                  <Section title="Restrições" num="5">
                    {list(briefing.block5.prohibited_claims) && (
                      <Row label="Claims que não podem ser usados" value={joinNarr(briefing.block5.prohibited_claims, 'É proibido afirmar')} />
                    )}
                    {list(briefing.block5.off_brand_messages) && (
                      <Row label="Mensagens fora de posicionamento" value={joinNarr(briefing.block5.off_brand_messages, 'Não deve transmitir')} />
                    )}
                    {list(briefing.block5.competitors_not_name) && (
                      <Row label="Concorrentes a não nomear" value={joinNarr(briefing.block5.competitors_not_name, 'Não devem ser referenciados')} />
                    )}
                  </Section>
                )}
              </div>

              {/* Painel direito — meta */}
              <div style={{ width: 260, flexShrink: 0, position: 'sticky', top: 20, display: 'flex', flexDirection: 'column', gap: 12 }}>
                <div style={{ background: '#ffffff', border: '1px solid var(--border, #ECEFF5)', borderRadius: 10, padding: '16px 18px', display: 'flex', flexDirection: 'column', gap: 12 }}>
                  <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>Resumo</div>
                  {campanha.brand_name && (
                    <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
                      <span style={{ fontSize: 10, color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>Marca</span>
                      <span style={{ fontSize: 13, fontWeight: 600, color: bCol, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{campanha.brand_name}</span>
                    </div>
                  )}
                  {(campanha.commercial_name || campanha.product_name) && (
                    <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
                      <span style={{ fontSize: 10, color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>Produto</span>
                      <span style={{ fontSize: 12, color: 'var(--text, #283252)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{campanha.commercial_name || campanha.product_name}</span>
                    </div>
                  )}
                  {list(briefing.block4?.channels) && (
                    <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                      <span style={{ fontSize: 10, color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>Canais</span>
                      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
                        {briefing.block4.channels.map((ch, i) => (
                          <span key={i} style={{ fontSize: 11, padding: '2px 8px', borderRadius: 99, background: 'var(--bg-app, #F5F6F8)', border: '1px solid var(--border, #ECEFF5)', color: 'var(--text, #283252)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>
                            {CANAL_LABEL[ch] || ch}
                          </span>
                        ))}
                      </div>
                    </div>
                  )}
                  {list(briefing.block1?.geo_markets) && (
                    <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                      <span style={{ fontSize: 10, color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>Mercados</span>
                      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
                        {briefing.block1.geo_markets.map((m, i) => (
                          <span key={i} style={{ fontSize: 11, padding: '2px 8px', borderRadius: 99, background: 'var(--bg-app, #F5F6F8)', border: '1px solid var(--border, #ECEFF5)', color: 'var(--text, #283252)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{m}</span>
                        ))}
                      </div>
                    </div>
                  )}
                  {(briefing.block4?.timeline_start || briefing.block4?.timeline_end) && (
                    <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
                      <span style={{ fontSize: 10, color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>Período</span>
                      <span style={{ fontSize: 12, color: 'var(--text, #283252)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{[briefing.block4.timeline_start, briefing.block4.timeline_end].filter(Boolean).join(' → ')}</span>
                    </div>
                  )}
                </div>
              </div>
            </>
          )}
        </div>
      </div>

      {emailModal && (
        <EmailBriefingModal
          campanha={campanha}
          briefing={briefing}
          onClose={() => setEmailModal(false)}
        />
      )}
    </div>
  );
};

// ── NovaCampanhaModal ──────────────────────────────────────────────────────────
const NovaCampanhaModal = ({ onClose, onCreated }) => {
  const [briefings,    setBriefings]    = React.useState(null);
  const [selected,     setSelected]     = React.useState(null);
  const [titulo,       setTitulo]       = React.useState('');
  const [saving,       setSaving]       = React.useState(false);
  const [error,        setError]        = React.useState('');
  const [brandFilter,  setBrandFilter]  = React.useState('all');
  const isSubmittingRef = React.useRef(false);

  React.useEffect(() => {
    CampAPI.availableForCampaign()
      .then(d => setBriefings(Array.isArray(d) ? d : []))
      .catch(() => setBriefings([]));
  }, []);

  const handleCreate = async () => {
    if (!selected || isSubmittingRef.current) return;
    isSubmittingRef.current = true;
    setSaving(true); setError('');
    try {
      const nome = (titulo || '').trim() || ((Number(selected.n_campanhas) > 0) ? '' : (selected.commercial_name || selected.product_name || `Campanha #${selected.id}`));
      if (!nome) throw new Error('Dá um nome a esta campanha.');
      const camp = await CampAPI.create({ briefing_id: selected.id, titulo: nome });
      if (camp?.error) throw new Error(camp.error);
      onCreated(camp);
    } catch (e) {
      setError(e.message || 'Erro ao criar campanha.');
    } finally {
      isSubmittingRef.current = false;
      setSaving(false);
    }
  };

  const goToBriefings = () => {
    onClose();
    if (window.mktNavToSub) window.mktNavToSub('briefings');
  };

  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 200,
      background: 'rgba(0,0,0,0.55)', display: 'flex', alignItems: 'center', justifyContent: 'center',
    }} onClick={e => { if (e.target === e.currentTarget) onClose(); }}>
      <div style={{
        background: '#ffffff', border: '1px solid var(--border, #e2e8f0)',
        borderRadius: 12, width: 520, height: 480, display: 'flex', flexDirection: 'column',
        boxShadow: '0 24px 64px rgba(0,0,0,0.4)',
      }}>
        {/* Header */}
        <div style={{ padding: '20px 24px 16px', borderBottom: '1px solid var(--border, #e2e8f0)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexShrink: 0 }}>
          <div>
            <div style={{ fontSize: 10, fontFamily: 'var(--font-mono, monospace)', color: 'var(--text-dim, #475569)', letterSpacing: '0.08em', marginBottom: 4 }}>NOVA CAMPANHA</div>
            <div style={{ fontSize: 15, fontWeight: 600, fontFamily: 'var(--font-display, Montserrat, sans-serif)', color: 'var(--text, #1e293b)' }}>Selecciona um briefing aprovado</div>
          </div>
          <button onClick={onClose} style={{ background: 'none', border: 'none', color: 'var(--text-muted, #64748b)', fontSize: 18, cursor: 'pointer', lineHeight: 1, padding: 4 }}>✕</button>
        </div>

        {/* Brand pills */}
        {briefings && briefings.length > 0 && (() => {
          const brands = [{ slug: 'all', name: 'Todas', color: '#3859D0' },
            ...Object.values(briefings.reduce((acc, b) => {
              if (b.brand_slug && !acc[b.brand_slug]) acc[b.brand_slug] = { slug: b.brand_slug, name: b.brand_name || b.brand_slug, color: b.brand_color || '#3859D0' };
              return acc;
            }, {}))
          ];
          if (brands.length <= 2) return null;
          return (
            <div style={{ display: 'flex', gap: 6, padding: '10px 24px', borderBottom: '1px solid var(--border, #ECEFF5)', flexWrap: 'wrap', flexShrink: 0 }}>
              {brands.map(b => {
                const active = b.slug === brandFilter;
                return (
                  <button key={b.slug} onClick={() => { setBrandFilter(b.slug); setSelected(null); }} style={{
                    padding: '4px 12px', borderRadius: 99, border: `1px solid ${active ? b.color : 'var(--border, #ECEFF5)'}`,
                    background: active ? b.color + '15' : 'transparent',
                    color: active ? b.color : 'var(--text-muted, #94A4C4)',
                    fontSize: 12, fontWeight: active ? 600 : 400,
                    fontFamily: 'var(--font-body, Inter, sans-serif)', cursor: 'pointer', transition: 'all .12s',
                  }}>
                    {b.name}
                  </button>
                );
              })}
            </div>
          );
        })()}

        {/* Body */}
        <div className="scrollbar" style={{ flex: 1, overflowY: 'auto', padding: '12px 24px' }}>
          {briefings === null ? (
            <div style={{ padding: '32px 0', textAlign: 'center', color: 'var(--text-muted, #64748b)', fontSize: 13 }}>A carregar…</div>
          ) : briefings.length === 0 ? (
            <div style={{ padding: '32px 0', textAlign: 'center', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 14 }}>
              <div style={{ fontSize: 13, color: 'var(--text-muted, #64748b)' }}>Não há briefings aprovados disponíveis.</div>
              <button onClick={goToBriefings} className="btn btn-ai">
                Ir para Briefings
              </button>
            </div>
          ) : (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              {briefings.filter(b => brandFilter === 'all' || b.brand_slug === brandFilter).map(b => {
                const isActive = selected?.id === b.id;
                const bColor = b.brand_color || '#3859D0';
                return (
                  <div key={b.id} onClick={() => setSelected(b)} style={{
                    padding: '12px 14px', borderRadius: 8, cursor: 'pointer',
                    border: `1px solid ${isActive ? 'var(--ai-500, #3859D0)' : 'var(--border, #e2e8f0)'}`,
                    background: isActive ? 'color-mix(in oklch, var(--ai-500, #3859D0) 8%, transparent)' : 'var(--bg, #f8fafc)',
                    transition: 'border-color .15s, background .15s',
                    display: 'flex', alignItems: 'center', gap: 12,
                  }}>
                    <div style={{ width: 4, borderRadius: 2, alignSelf: 'stretch', background: bColor, flexShrink: 0 }} />
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 2 }}>
                        <span style={{ fontSize: 11, fontWeight: 700, color: bColor, fontFamily: 'var(--font-display, Montserrat, sans-serif)', letterSpacing: '0.04em' }}>{b.brand_name}</span>
                        {(b.commercial_name || b.product_name) && (
                          <span style={{ fontSize: 12, color: 'var(--text, #1e293b)', fontWeight: 500 }}>{b.commercial_name || b.product_name}</span>
                        )}
                      </div>
                    </div>
                    {isActive && <div style={{ color: 'var(--ai-500, #3859D0)', fontSize: 16, flexShrink: 0 }}>✓</div>}
                  </div>
                );
              })}
            </div>
          )}
          {selected && (
            <label style={{ display: 'block', marginTop: 12, fontSize: 12, color: '#4a5e7a' }}>
              Nome da campanha
              <input value={titulo} onChange={e => setTitulo(e.target.value)} placeholder={Number(selected.n_campanhas) > 0 ? 'Obrigatório nesta activação' : (selected.commercial_name || selected.product_name || '')} style={{ display: 'block', width: '100%', marginTop: 4, boxSizing: 'border-box', border: '1px solid #dde3ef', borderRadius: 8, padding: '8px 10px', font: 'inherit', fontSize: 13 }} />
            </label>
          )}
          {error && <div style={{ marginTop: 8, fontSize: 12, color: 'var(--danger, #ef4444)' }}>{error}</div>}
        </div>

        {/* Footer */}
        {briefings && briefings.length > 0 && (
          <div style={{ padding: '14px 24px', borderTop: '1px solid var(--border, #e2e8f0)', display: 'flex', justifyContent: 'flex-end', gap: 8, flexShrink: 0 }}>
            <button onClick={onClose} className="btn">Cancelar</button>
            <button onClick={handleCreate} disabled={!selected || saving} className="btn btn-ai"
              style={{ opacity: (!selected || saving) ? 0.5 : 1 }}>
              {saving ? 'A criar…' : 'Criar Campanha'}
            </button>
          </div>
        )}
      </div>
    </div>
  );
};

// ── MktCampanhasScreen (main) ──────────────────────────────────────────────────

const SDR_FICHA_STEPS = [
  { label: 'Mensagem da campanha', detail: 'Mensagem-chave, USPs e oferta real', duration: 2000 },
  { label: 'Banco BANT por mercado', detail: 'Duas perguntas de recurso por letra, a partir do briefing', duration: 18000 },
  { label: 'Contexto dos canais', detail: 'O que a lead pode ter recebido. A entrevista é a chamada.', duration: 3000 },
  { label: 'Guardar ficha', detail: 'O markdown que a Digi recebe na chamada', duration: 2000 },
];

const SDR_BANT_LETRAS = [
  { id: 'need', label: 'Need', nota: 'Dor e volume' },
  { id: 'timeline', label: 'Timeline', nota: 'Prazo' },
  { id: 'budget', label: 'Budget', nota: 'Grandeza, sem pedir o orçamento' },
  { id: 'authority', label: 'Authority', nota: 'Quem decide' },
];

function sdrCriterioMarca(slug, nome) {
  const s = `${slug || ''} ${nome || ''}`.toLowerCase();
  if (s.includes('biond') || s.includes('decal')) return 'As quatro letras são as mesmas. O fecho desta marca, amostras ou testing roll, fica para a parte seguinte. Esta ficha não propõe demo de showroom nem marca hora.';
  return 'As quatro letras e o prazo até 12 meses, ou um pedido de demo ou visita, autorizam a proposta. A oportunidade nasce quando a pessoa aceita. As demos são às segundas-feiras, nos horários reais. Se a empresa já está no CRM, confirma-se o contribuinte. Se não está, pedem-se NIF, nome e morada. A Carina abre a oportunidade.';
}

// ── TabSDRAI — Ficha SDR por campanha · o que vai ser injectado no SP_CLIENTES ─
const TabSDRAI = ({ campanha, userEmail, onAction }) => {
  const brief    = campanha?.briefing || {};
  const proposta = campanha?.proposta_json || {};
  const estrat   = campanha?.estrategia_json || {};
  const commPlan = (proposta.comm_plan || []);

  // FASE A: fetch funil-context (Sonnet AI generated por mercado)
  const [funilCtx, setFunilCtx] = React.useState(null);
  const [ctxLoading, setCtxLoading] = React.useState(false);
  const [activating, setActivating] = React.useState(false);
  React.useEffect(() => {
    if (!campanha?.id) return;
    setCtxLoading(true);
    campApiCall('/api/marketing/campanhas/' + campanha.id + '/funil-context')
      .then(d => setFunilCtx(d))
      .catch(() => {})
      .finally(() => setCtxLoading(false));
  }, [campanha?.id]);
  const sdrByMarket = funilCtx?.sdr_by_market || {};
  const sdrCross    = funilCtx?.sdr_ai || null;
  const nurtByMarket= funilCtx?.nurturing_by_market || {};
  const nurtCross   = funilCtx?.nurturing_stages || [];

  // Ficha SDR AI — markdown compilado que o heydigi injecta em SP_CLIENTES {campanhas}
  const [fichaMd, setFichaMd]         = React.useState(null);
  const [fichaMeta, setFichaMeta]     = React.useState(null);
  const [fichaLoading, setFichaLoading] = React.useState(false);
  const [fichaError, setFichaError]   = React.useState(null);
  const [regenerating, setRegenerating] = React.useState(false);
  const [genStep, setGenStep] = React.useState(0);
  const [genElapsed, setGenElapsed] = React.useState(0);
  const [stepPct, setStepPct] = React.useState(0);
  React.useEffect(() => {
    if (!regenerating) { setGenStep(0); setGenElapsed(0); setStepPct(0); return; }
    const started = Date.now();
    setGenStep(1);
    const tick = setInterval(() => setGenElapsed(Math.floor((Date.now() - started) / 1000)), 1000);
    let cum = 0;
    const timers = SDR_FICHA_STEPS.map((s, i) => {
      cum += s.duration;
      return setTimeout(() => setGenStep(i + 1 < SDR_FICHA_STEPS.length ? i + 2 : SDR_FICHA_STEPS.length), cum);
    });
    return () => { clearInterval(tick); timers.forEach(clearTimeout); };
  }, [regenerating]);
  React.useEffect(() => {
    if (!regenerating || genStep <= 0) return;
    const def = SDR_FICHA_STEPS[genStep - 1];
    const stepStart = Date.now();
    const iv = setInterval(() => {
      const sp = Math.min(((Date.now() - stepStart) / (def?.duration || 1)) * 90, 90);
      setStepPct(Math.round((((genStep - 1) + sp / 100) / SDR_FICHA_STEPS.length) * 100));
    }, 80);
    return () => clearInterval(iv);
  }, [regenerating, genStep]);
  const [editing, setEditing] = React.useState(false);
  const [editDraft, setEditDraft] = React.useState('');
  const [saving, setSaving] = React.useState(false);
  const [confirmDeact, setConfirmDeact] = React.useState(false);
  const [confirmRegen, setConfirmRegen] = React.useState(false);
  const loadFicha = React.useCallback(async () => {
    if (!campanha?.id) return;
    setFichaLoading(true);
    setFichaError(null);
    try {
      const d = await campApiCall('/api/marketing/campanhas/' + campanha.id + '/sdr-ficha?format=json');
      setFichaMd(d.sdr_ficha_md || null);
      setFichaMeta({
        version: d.sdr_ficha_version,
        generated_at: d.sdr_ficha_generated_at,
        generated_by: d.sdr_ficha_generated_by,
        manual: !!d.sdr_ficha_manual,
        edited_at: d.sdr_ficha_edited_at,
        edited_by: d.sdr_ficha_edited_by,
        activa_para_digi: !!d.activa_para_digi,
        motivo_inactiva: d.motivo_inactiva || null,
      });
    } catch (e) {
      // 404 → ainda não gerada, é OK
      if (String(e.message || '').includes('404') || String(e.message || '').toLowerCase().includes('não gerada')) {
        setFichaMd(null); setFichaMeta(null);
      } else {
        setFichaError(e.message);
      }
    }
    setFichaLoading(false);
  }, [campanha?.id]);
  React.useEffect(() => { loadFicha(); }, [loadFicha]);
  const regenerateFicha = async () => {
    if (regenerating || !campanha?.id || !podeGerarFicha) return;
    if ((fichaMeta?.manual || campanha?.sdr_ai_activated_at) && !confirmRegen) {
      setConfirmRegen(true);
      return;
    }
    setConfirmRegen(false);
    setRegenerating(true);
    setFichaError(null);
    try {
      await campApiCall('/api/marketing/campanhas/' + campanha.id + '/sdr-ficha/generate', {
        method: 'POST',
        body: JSON.stringify({ user_email: userEmail }),
      });
      await loadFicha();
      campApiCall('/api/marketing/campanhas/' + campanha.id + '/funil-context').then(d => setFunilCtx(d)).catch(() => {});
    } catch (e) { setFichaError(e.message); }
    setRegenerating(false);
  };

  const deactivateSdrAi = async () => {
    if (activating || !campanha?.id) return;
    setConfirmDeact(false);
    setActivating(true);
    try {
      await campApiCall('/api/marketing/campanhas/' + campanha.id + '/sdr-ai/activate', {
        method: 'DELETE',
        body: JSON.stringify({ user_email: userEmail }),
      });
      if (onAction) await onAction('refreshCampanha');
    } catch (e) { console.error('deactivate sdr-ai', e); }
    setActivating(false);
  };

  const startEdit = () => {
    setEditDraft(fichaMd || '');
    setEditing(true);
  };
  const cancelEdit = () => {
    setEditing(false);
    setEditDraft('');
  };
  const saveEdit = async () => {
    if (saving || !campanha?.id) return;
    if (!editDraft || editDraft.trim().length < 100) {
      alert('Ficha vazia ou demasiado curta (mínimo 100 caracteres).');
      return;
    }
    setSaving(true);
    setFichaError(null);
    try {
      await campApiCall('/api/marketing/campanhas/' + campanha.id + '/sdr-ficha', {
        method: 'PATCH',
        body: JSON.stringify({ sdr_ficha_md: editDraft, user_email: userEmail }),
      });
      setEditing(false);
      setEditDraft('');
      await loadFicha();
    } catch (e) { setFichaError(e.message); }
    setSaving(false);
  };

  // Tab filtro da ficha: completa | pt | es | regras
  const [fichaTab, setFichaTab] = React.useState('inbound');
  const fichaFiltered = React.useMemo(() => {
    if (!fichaMd) return '';
    if (fichaTab === 'inbound') {
      const parts = fichaMd.split(/\n#### OUTBOUND/);
      return (parts[0] || fichaMd).trim();
    }
    if (fichaTab === 'outbound') {
      const m = fichaMd.match(/#### OUTBOUND[\s\S]*?(?=\n#### REGRAS HARD|$)/);
      return m ? m[0].trim() : fichaMd;
    }
    if (fichaTab === 'regras') {
      const m = fichaMd.match(/#### REGRAS HARD[\s\S]*/);
      return m ? m[0].trim() : fichaMd;
    }
    return fichaMd;
  }, [fichaMd, fichaTab]);

  const podeGerarFicha = !!campanha?.planeamento_approved_at && !['arquivada','cancelada','concluida'].includes(campanha?.status);
  const bCol = _brandColor(campanha?.brand_slug);

  const _a = v => Array.isArray(v) ? v : (v ? [v] : []);
  const _str = v => typeof v === 'string' ? v : (v?.descricao || v?.usp || v?.angulo || JSON.stringify(v) || '');

  const usps        = _a(brief.usps).slice(0,5).map(_str);
  const pains       = _a(brief.pain_points).slice(0,5).map(_str);
  const angles      = _a(proposta.messaging_angles).slice(0,4);
  const competitors = _a(brief.competitors).slice(0,3).map(_str);
  const markets     = estrat.markets || [];
  const personas    = (Array.isArray(proposta.personas) && proposta.personas.length ? proposta.personas : markets.flatMap(m => _a(m.personas_priorizadas))).filter((p,i,a) => a.findIndex(x => (x.nome||x.perfil) === (p.nome||p.perfil)) === i).slice(0,4);
  const keyMessage  = campanha?.key_message || brief.key_message || proposta.key_message || '';

  const offerRaw  = campanha?.commercial_offer || brief.commercial_offer;
  const offer     = offerRaw ? (typeof offerRaw === 'string' ? (() => { try { return JSON.parse(offerRaw); } catch { return { descricao: offerRaw }; } })() : offerRaw) : {};
  const CTA_FUNIL = {
    quote_request: 'Pedido de proposta', demo_request: 'Pedido de demonstração',
    testing_roll: 'Pedir Testing Roll', sample_request: 'Pedir amostra',
    visit_showroom: 'Visita ao showroom', whatsapp: 'WhatsApp', form: 'Formulário',
    call: 'Chamada', landing: 'Página da campanha', none: 'Sem CTA específico',
  };
  const offerTipo = offer.type || offer.tipo || '';
  const ctaLabel = CTA_FUNIL[offer.primary_cta] || offer.primary_cta || 'CTA da campanha';
  const ofertaStr = [offer.headline, offer.details, offer.descricao, offer.oferta].filter(Boolean).join(' · ') || 'Oferta da campanha';
  const provaDeAmostra = offerTipo === 'testing_roll' || offerTipo === 'sample_request';

  const objections = _a(brief.objections).map(_str).filter(Boolean).slice(0, 4);
  const objResponses = objections.map((_, i) => angles[i]?.angulo || usps[i] || usps[0] || keyMessage || '');

  const linhasCanal = (m) => (Array.isArray(m?.channel_mix) && m.channel_mix.length ? m.channel_mix : (m?.channel_fit || []));
  const temWa = markets.some(m => linhasCanal(m).some(ch => ch?.canal === 'whatsapp')) || !!proposta.abordagem_por_canal?.whatsapp;
  const waPecas = _a(proposta.abordagem_por_canal?.whatsapp?.pecas);
  const waGrupos = [
    { papel: 'abertura', nome: 'Daily Prospect', nota: '4 aberturas' },
    { papel: 'usp', nome: 'SDR AI', nota: '1 mensagem por USP' },
    { papel: 'qualificacao', nome: 'Qualificação', nota: '4 follow-ups' },
  ].map(g => ({ ...g, itens: waPecas.filter(p => p?.papel === g.papel) }));
  const emailCliente = commPlan.filter(i => (i.canal === 'email' || i.content_type === 'email_html') && i.papel !== 'interno');
  const emailInterno = commPlan.find(i => i.canal === 'email_interno' || i.papel === 'interno' || i.content_type === 'email_interno');

  const CRM_FIELDS = provaDeAmostra ? [
    'Produto de interesse', 'Aplicação', 'Dor principal', 'USP que respondeu à dor',
    'Oferta da campanha', 'Amostra aceite', 'Data de envio', 'Próxima acção',
  ] : [
    'Produto de interesse', 'Decisor', 'Dor principal', 'USP que respondeu à dor',
    'Oferta da campanha', 'Objecção', 'Próximo passo', 'Data e hora',
  ];
  const SCORE = [
    { criterio: 'A dor corresponde a uma USP desta campanha', pts: 25 },
    { criterio: 'O decisor está na conversa', pts: 25 },
    { criterio: 'Aceitou o CTA da campanha', pts: 25 },
    { criterio: 'Há data e hora para o próximo passo', pts: 25 },
  ];


  // SVG inline helpers (Icon set não tem pen/disk)
  const PenIcon = ({ size = 12 }) => (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
      <path d="M12 20h9M16.5 3.5a2.121 2.121 0 013 3L7 19l-4 1 1-4L16.5 3.5z"/>
    </svg>
  );
  const DiskIcon = ({ size = 12 }) => (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
      <path d="M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z"/>
      <path d="M17 21v-8H7v8M7 3v5h8"/>
    </svg>
  );
  const langBadge = (kind) => {
    if (kind === 'ref-pt') return (
      <span
        title="Este bloco é derivado do briefing, que só existe em português. Serve de referência para a equipa marketing — não vai directamente para a Digi AI. As secções multi-mercado (Ficha, Processo SDR, Nurturing) são as que a Digi AI usa por idioma do lead."
        style={{ fontSize:9, fontWeight:700, padding:'2px 8px', borderRadius:99, background:'rgba(100,116,139,.12)', color:'#475569', fontFamily:'var(--font-mono)', letterSpacing:'.04em', marginLeft:8, textTransform:'none', cursor:'help', display:'inline-flex', alignItems:'center', gap:4 }}
      >
        <window.Icon name="doc" size={11}/>
        REFERÊNCIA · briefing PT
      </span>
    );
    if (kind === 'multi') return (
      <span
        title="Este bloco tem versão PT e ES. A Digi AI usa a secção correspondente ao idioma detectado do lead — nunca traduz nem mistura."
        style={{ fontSize:9, fontWeight:700, padding:'2px 8px', borderRadius:99, background:'rgba(56,89,208,.12)', color:'#3859D0', fontFamily:'var(--font-mono)', letterSpacing:'.04em', marginLeft:8, textTransform:'none', cursor:'help', display:'inline-flex', alignItems:'center', gap:4 }}
      >
        <window.Icon name="globe" size={11}/>
        MULTI-MERCADO · Digi AI por idioma
      </span>
    );
    return null;
  };
  const secTitle = (t, col, badgeKind) => (
    <div style={{ fontSize:10, fontWeight:700, fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.08em', color:col||bCol, marginBottom:10, paddingBottom:6, borderBottom:`1px solid ${(col||bCol)}30`, display:'flex', alignItems:'center', flexWrap:'wrap' }}>
      <span>{t}</span>
      {badgeKind && langBadge(badgeKind)}
    </div>
  );
  const pill = (t, col) => (
    <span style={{ fontSize:9, fontWeight:700, padding:'2px 8px', borderRadius:99, background:`${col}18`, color:col, fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.04em', display:'inline-block' }}>{t}</span>
  );
  const card = (children, extra={}) => (
    <div style={{ background:'var(--bg-card,#fff)', border:'1px solid var(--border)', borderRadius:8, padding:'14px 16px', ...extra }}>{children}</div>
  );

  return (
    <div style={{ display:'flex', flexDirection:'column', gap:20 }}>

      {(keyMessage || ofertaStr) && (
        <div style={{ background:'linear-gradient(135deg,rgba(56,89,208,.08),rgba(99,102,241,.05))', border:'1px solid rgba(56,89,208,.25)', borderRadius:10, padding:'14px 18px' }}>
          <div style={{ fontSize:10, fontWeight:700, fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.08em', color:'#3859D0', marginBottom:6 }}>Mensagem desta campanha</div>
          {keyMessage && <div style={{ fontSize:14, fontWeight:700, color:'var(--navy,#112954)', lineHeight:1.4, marginBottom:8 }}>{keyMessage}</div>}
          <div style={{ fontSize:12, color:'var(--text)' }}>{ofertaStr} · {ctaLabel}</div>
        </div>
      )}

      {/* Header */}
      <div style={{ display:'flex', alignItems:'center', gap:12, padding:'10px 14px', background:'var(--bg-surface,#fff)', borderRadius:8, boxShadow:'var(--shadow-card)' }}>
        {pill('SDR AI', bCol)}
        <span style={{ fontSize:11, color:'var(--fg-3,var(--text-muted))' }}>
          {campanha?.brand_name} · {brief.commercial_name || campanha?.titulo}
        </span>
        <span style={{ marginLeft:'auto', fontSize:10, fontWeight:700, padding:'2px 10px', borderRadius:99, background:'rgba(100,116,139,.08)', color:'var(--text-dim)', fontFamily:'var(--font-mono)' }}>
          CTA → {ctaLabel}
        </span>
        {campanha?.sdr_ai_activated_at ? (
          <div style={{ display:'flex', alignItems:'center', gap:6 }}>
            <span
              title="A Digi AI serve esta ficha aos leads que chegarem via CTWA/CRM. Desactivar faz o heydigi deixar de a servir."
              style={{ fontSize:10, fontWeight:700, padding:'4px 10px', borderRadius:99, background: fichaMeta?.activa_para_digi === false ? 'rgba(220,38,38,.08)' : 'var(--green-100,#E1F7E6)', color: fichaMeta?.activa_para_digi === false ? '#dc2626' : 'var(--green-700,#1F8A52)', fontFamily:'var(--font-mono)', display:'inline-flex', alignItems:'center', gap:5 }}
            >
              <window.Icon name="check" size={11}/>
              {fichaMeta?.activa_para_digi === false
                ? `Inactiva para a Digi${fichaMeta.motivo_inactiva === 'fora_vigencia' ? ' · fora de vigência' : fichaMeta.motivo_inactiva === 'cancelada' ? ' · cancelada' : ''}`
                : `Ficha activa · ${new Date(campanha.sdr_ai_activated_at).toLocaleDateString('pt-PT')}`}
            </span>
            <button
              className="btn"
              style={{ height:24, padding:'0 10px', fontSize:10, cursor: regenerating ? 'wait' : 'pointer', display:'inline-flex', alignItems:'center', gap:4 }}
              disabled={regenerating || !podeGerarFicha}
              title={!podeGerarFicha ? 'Aprova o planeamento antes de gerar a ficha.' : 'Regenerar a ficha com a mensagem e as peças desta campanha'}
              onClick={regenerateFicha}
            >
              <window.Icon name="refresh" size={11}/>
              {regenerating ? 'A regenerar…' : 'Regenerar ficha'}
            </button>
            {confirmDeact ? (
              <div style={{ display:'inline-flex', alignItems:'center', gap:6, padding:'2px 8px', background:'rgba(220,38,38,.08)', border:'1px solid rgba(220,38,38,.3)', borderRadius:6 }}>
                <span style={{ fontSize:10, color:'#dc2626', fontWeight:600 }}>Desactivar ficha?</span>
                <button
                  className="btn"
                  style={{ height:22, padding:'0 8px', fontSize:10, background:'#dc2626', color:'#fff', borderColor:'#dc2626', cursor: activating ? 'wait' : 'pointer' }}
                  disabled={activating}
                  onClick={deactivateSdrAi}
                >
                  {activating ? '…' : 'Sim'}
                </button>
                <button
                  className="btn"
                  style={{ height:22, padding:'0 8px', fontSize:10 }}
                  disabled={activating}
                  onClick={() => setConfirmDeact(false)}
                >
                  Não
                </button>
              </div>
            ) : (
              <button
                className="btn"
                style={{ height:24, padding:'0 10px', fontSize:10, cursor:'pointer', color:'#dc2626', borderColor:'rgba(220,38,38,.3)', display:'inline-flex', alignItems:'center', gap:4 }}
                title="Desactivar esta ficha — Digi AI deixa de a servir a leads (fallback: FRAME base + KB sem contexto de campanha)"
                onClick={() => setConfirmDeact(true)}
              >
                <window.Icon name="close" size={11}/>
                Desactivar ficha
              </button>
            )}
          </div>
        ) : (
          <button
            className="btn btn-ai"
            style={{ height:28, padding:'0 12px', fontSize:11, cursor: activating ? 'wait' : 'pointer' }}
            disabled={activating || !podeGerarFicha}
            title={!podeGerarFicha ? 'Aprova o planeamento antes de activar a ficha.' : 'A Digi passa a usar esta ficha'}
            onClick={async () => {
              if (activating || !podeGerarFicha) return;
              setActivating(true);
              try {
                await campApiCall('/api/marketing/campanhas/' + campanha.id + '/sdr-ai/activate', {
                  method: 'POST',
                  body: JSON.stringify({ user_email: userEmail })
                });
                if (onAction) await onAction('refreshCampanha');
              } catch (e) { console.error('activate sdr-ai', e); }
              setActivating(false);
            }}
          >
            {activating ? 'A activar…' : 'Activar ficha no Digi AI'}
          </button>
        )}
      </div>

      {/* ══ FICHA SDR AI — contexto que a Digi AI recebe (SP_CLIENTES {campanhas}) ══ */}
      <div style={{ background:'var(--bg-surface,#fff)', borderRadius:10, boxShadow:'var(--shadow-card)', overflow:'hidden', border:'1.5px solid rgba(56,89,208,.15)' }}>
        <div style={{ padding:'12px 16px', borderBottom:'1px solid var(--border,#e5e7eb)', display:'flex', alignItems:'center', gap:12, background:'linear-gradient(135deg,rgba(56,89,208,.06),rgba(56,89,208,.02))' }}>
          <span style={{ display:'inline-flex', color:'#3859D0' }}><window.Icon name="doc" size={20}/></span>
          <div style={{ flex:1 }}>
            <div style={{ fontSize:13, fontWeight:700, color:'var(--navy,#112954)', display:'flex', alignItems:'center', flexWrap:'wrap' }}>
              <span>Ficha SDR AI · o que a Digi AI vê</span>
              {(() => { const mkts = Array.isArray(brief.geo_markets) ? brief.geo_markets : []; return mkts.length > 1 ? langBadge('multi') : mkts.length === 1 ? <span style={{ fontSize:9, fontWeight:700, padding:'2px 8px', borderRadius:99, background:'rgba(56,89,208,.12)', color:'#3859D0', fontFamily:'var(--font-mono)', letterSpacing:'.04em', marginLeft:8 }}>{mkts[0]}</span> : null; })()}
            </div>
            <div style={{ fontSize:10, color:'var(--text-dim)', fontFamily:'var(--font-mono)', marginTop:2, display:'flex', alignItems:'center', gap:8, flexWrap:'wrap' }}>
              {fichaMeta?.version
                ? <>
                    <span>v{fichaMeta.version} · {(fichaMd?.length || 0).toLocaleString('pt-PT')} chars</span>
                    {fichaMeta.manual && fichaMeta.edited_at ? (
                      <span style={{ padding:'2px 7px', borderRadius:99, background:'rgba(217,119,6,.15)', color:'#b45309', fontWeight:700, display:'inline-flex', alignItems:'center', gap:4 }}>
                        <PenIcon size={10}/>
                        EDITADA {new Date(fichaMeta.edited_at).toLocaleString('pt-PT')} {fichaMeta.edited_by ? '· ' + fichaMeta.edited_by : ''}
                      </span>
                    ) : (
                      <span>gerada {fichaMeta.generated_at ? new Date(fichaMeta.generated_at).toLocaleString('pt-PT') : '?'} {fichaMeta.generated_by ? '· ' + fichaMeta.generated_by : ''}</span>
                    )}
                  </>
                : (fichaLoading ? 'A carregar...' : 'ainda não gerada')}
            </div>
          </div>
          {editing ? (
            <>
              <button
                className="btn"
                style={{ height:28, padding:'0 14px', fontSize:11, background:'#16a34a', color:'#fff', borderColor:'#16a34a', cursor: saving ? 'wait' : 'pointer', display:'inline-flex', alignItems:'center', gap:6 }}
                disabled={saving}
                onClick={saveEdit}
              >
                <DiskIcon size={12}/>
                {saving ? 'A guardar…' : 'Guardar'}
              </button>
              <button
                className="btn"
                style={{ height:28, padding:'0 12px', fontSize:11, display:'inline-flex', alignItems:'center', gap:5 }}
                disabled={saving}
                onClick={cancelEdit}
              >
                <window.Icon name="close" size={12}/>
                Cancelar
              </button>
            </>
          ) : (
            <>
              {fichaMd && (
                <button
                  className="btn"
                  style={{ height:28, padding:'0 12px', fontSize:11, display:'inline-flex', alignItems:'center', gap:5 }}
                  onClick={startEdit}
                  title="Editar directamente o markdown que a Digi AI vê"
                >
                  <PenIcon size={12}/>
                  Editar
                </button>
              )}
              {fichaMd && (
                <button
                  className="btn"
                  style={{ height:28, padding:'0 12px', fontSize:11, display:'inline-flex', alignItems:'center', gap:5 }}
                  onClick={() => { try { navigator.clipboard.writeText(fichaMd); } catch(_) {} }}
                  title="Copiar markdown"
                >
                  <window.Icon name="copy" size={12}/>
                  Copiar
                </button>
              )}
              <button
                className="btn"
                style={{ height:28, padding:'0 12px', fontSize:11, cursor: regenerating ? 'wait' : 'pointer', display:'inline-flex', alignItems:'center', gap:5 }}
                disabled={regenerating || !podeGerarFicha}
                onClick={regenerateFicha}
                title={!podeGerarFicha ? 'Aprova o planeamento antes de gerar a ficha.' : 'Gerar a ficha com a mensagem, as USPs e as peças desta campanha'}
              >
                <window.Icon name="refresh" size={12}/>
                {regenerating ? 'A regenerar…' : (fichaMd ? 'Regenerar' : 'Gerar ficha')}
              </button>
            </>
          )}
        </div>
        {fichaLoading && (
          <div style={{ padding:'20px 16px', fontSize:12, color:'var(--text-dim)', textAlign:'center' }}>A carregar ficha…</div>
        )}
        {fichaError && (
          <div style={{ padding:'12px 16px', fontSize:12, color:'#dc2626', background:'rgba(220,38,38,.05)' }}>
            Erro: {fichaError}
          </div>
        )}
        {regenerating && (() => {
          const passo = SDR_FICHA_STEPS[Math.max(0, genStep - 1)] || SDR_FICHA_STEPS[0];
          const mins = Math.floor(genElapsed / 60);
          const secs = String(genElapsed % 60).padStart(2, '0');
          return (
            <div style={{ margin:'16px', background:'linear-gradient(135deg, #eef2ff 0%, #f5f3ff 100%)', border:'1px solid #c7d2fe', borderRadius:14, padding:'18px 20px' }}>
              <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', gap:12, marginBottom:12 }}>
                <div style={{ display:'flex', alignItems:'center', gap:12, minWidth:0 }}>
                  <div style={{ display:'flex', alignItems:'flex-end', gap:3, height:18 }}>
                    {[0,1,2,3,4].map(i => (
                      <div key={i} style={{ width:3, height:16, background:'#6366f1', borderRadius:2, animation:'ctdWave 1s ease-in-out infinite', animationDelay:`${i * 0.12}s`, transformOrigin:'bottom' }} />
                    ))}
                  </div>
                  <div style={{ minWidth:0 }}>
                    <div style={{ fontSize:13, color:'#3730a3', fontWeight:600 }}>{passo.label}</div>
                    <div style={{ fontSize:11, color:'#6366f1', marginTop:2 }}>{passo.detail}</div>
                  </div>
                </div>
                <div style={{ fontSize:12, color:'#6366f1', fontFamily:'var(--font-mono, monospace)', fontWeight:600, flexShrink:0 }}>
                  {genStep > 0 ? `${genStep}/${SDR_FICHA_STEPS.length} · ` : ''}{stepPct}% · {mins}:{secs}
                </div>
              </div>
              <div style={{ height:6, background:'#e0e7ff', borderRadius:3, overflow:'hidden' }}>
                <div style={{ width:`${stepPct}%`, height:'100%', background:'linear-gradient(90deg, #6366f1, #8b5cf6)', transition:'width 0.3s ease' }} />
              </div>
              <style>{`@keyframes ctdWave { 0%,100%{transform:scaleY(0.3)} 50%{transform:scaleY(1)} }`}</style>
            </div>
          );
        })()}
        {!fichaLoading && !fichaMd && !fichaError && !regenerating && (
          <div style={{ padding:'20px 16px', fontSize:12, color:'var(--text-dim)', textAlign:'center', background:'rgba(56,89,208,.02)' }}>
            {podeGerarFicha
              ? 'Ainda não há ficha. Gera o markdown com a mensagem-chave, as USPs, a oferta e as peças de WhatsApp e email.'
              : 'Aprova o planeamento antes de gerar a ficha SDR.'}
          </div>
        )}
        {fichaMd && !editing && (
          <>
            <div style={{ display:'flex', gap:0, borderBottom:'1px solid var(--border,#e5e7eb)', background:'rgba(15,23,42,.02)' }}>
              {[
                { key:'inbound',  label:'Inbound',    iconEl:<window.Icon name="arrow-down" size={12}/>, hint:'Contexto para leads que contactam via anúncios, email ou outros canais' },
                { key:'outbound', label:'Outbound',   iconEl:<window.Icon name="arrow-up" size={12}/>, hint:'Contexto para contactos proactivos WA — leads dos segmentos Target' },
                { key:'regras',   label:'Regras HARD',iconEl:<window.Icon name="warning" size={12}/>, hint:'Regras transversais: idioma, preços, fecho, handoff' },
              ].map(t => {
                const active = fichaTab === t.key;
                return (
                  <button
                    key={t.key}
                    onClick={() => setFichaTab(t.key)}
                    title={t.hint}
                    style={{
                      flex:1,
                      padding:'10px 12px',
                      fontSize:11,
                      fontWeight: active ? 700 : 500,
                      color: active ? '#3859D0' : 'var(--text-dim)',
                      background: active ? 'var(--bg-surface,#fff)' : 'transparent',
                      border:'none',
                      borderBottom: active ? '2px solid #3859D0' : '2px solid transparent',
                      cursor:'pointer',
                      fontFamily:'var(--font-mono)',
                      letterSpacing:'.02em',
                      transition:'all 120ms ease',
                      display:'inline-flex',
                      alignItems:'center',
                      justifyContent:'center',
                      gap:6,
                    }}
                  >
                    {t.iconEl}
                    {t.label}
                  </button>
                );
              })}
            </div>
          <div style={{ position:'relative', maxHeight:520, overflowY:'auto', background:'rgba(15,23,42,0.02)' }}>
            <pre style={{
              margin:0,
              padding:'16px 20px',
              fontSize:11,
              lineHeight:1.7,
              fontFamily:'var(--font-mono)',
              color:'var(--text)',
              whiteSpace:'pre-wrap',
              wordBreak:'break-word',
              opacity: (regenerating || fichaLoading) ? 0.35 : 1,
              transition: 'opacity 180ms ease',
              pointerEvents: (regenerating || fichaLoading) ? 'none' : 'auto',
            }}>{fichaFiltered}</pre>
            {fichaLoading && !regenerating && (
              <div style={{
                position:'sticky',
                top:0,
                marginTop:-520,
                height:520,
                display:'flex',
                alignItems:'center',
                justifyContent:'center',
                pointerEvents:'none',
                zIndex:2,
              }}>
                <div style={{
                  background:'var(--bg-surface,#fff)',
                  border:'1px solid rgba(56,89,208,.25)',
                  borderRadius:8,
                  padding:'12px 20px',
                  fontSize:12,
                  fontWeight:600,
                  color:'#3859D0',
                  boxShadow:'0 4px 16px rgba(15,23,42,0.12)',
                  fontFamily:'var(--font-mono)',
                  display:'flex',
                  alignItems:'center',
                  gap:10,
                }}>
                  <span style={{
                    display:'inline-block',
                    width:14, height:14,
                    border:'2px solid rgba(56,89,208,.25)',
                    borderTopColor:'#3859D0',
                    borderRadius:'50%',
                    animation:'spin 800ms linear infinite',
                  }}/>
                  {regenerating ? 'A regenerar ficha…' : 'A carregar ficha…'}
                </div>
              </div>
            )}
          </div>
          </>
        )}
        {confirmRegen && (
          <div style={{ padding:'10px 14px', background:'rgba(220,38,38,.06)', borderBottom:'1px solid rgba(220,38,38,.25)', display:'flex', alignItems:'center', gap:10, flexWrap:'wrap' }}>
            <span style={{ display:'inline-flex', color:'#dc2626' }}><window.Icon name="warning" size={14}/></span>
            <span style={{ fontSize:11, color:'#dc2626', fontWeight:600 }}>
              {campanha?.sdr_ai_activated_at
                ? 'A ficha está activa na Digi. Regenerar substitui o contexto que ela usa nesta campanha.'
                : 'Esta ficha tem edições manuais. Regenerar substitui o texto pelo compilado da campanha.'}
            </span>
            <div style={{ marginLeft:'auto', display:'flex', gap:6 }}>
              <button
                className="btn"
                style={{ height:24, padding:'0 10px', fontSize:10, background:'#dc2626', color:'#fff', borderColor:'#dc2626', cursor: regenerating ? 'wait' : 'pointer' }}
                disabled={regenerating}
                onClick={regenerateFicha}
              >
                {regenerating ? 'A regenerar…' : 'Sim, sobrepor'}
              </button>
              <button
                className="btn"
                style={{ height:24, padding:'0 10px', fontSize:10 }}
                disabled={regenerating}
                onClick={() => setConfirmRegen(false)}
              >
                Não
              </button>
            </div>
          </div>
        )}
        {fichaMd && editing && (
          <div style={{ padding:'12px 14px', background:'rgba(217,119,6,.05)', borderBottom:'1px solid rgba(217,119,6,.2)' }}>
            <div style={{ fontSize:11, color:'#b45309', fontWeight:600, marginBottom:8, display:'flex', alignItems:'center', gap:6 }}>
              <PenIcon size={12}/>
              Modo edição · o conteúdo abaixo será guardado como override manual. Regenerar depois pede confirmação.
            </div>
            <textarea
              value={editDraft}
              onChange={(e) => setEditDraft(e.target.value)}
              disabled={saving}
              spellCheck={false}
              style={{
                width:'100%',
                minHeight:520,
                maxHeight:'70vh',
                padding:'14px 16px',
                fontSize:12,
                lineHeight:1.6,
                fontFamily:'var(--font-mono)',
                color:'var(--text)',
                background:'#fff',
                border:'1px solid rgba(217,119,6,.35)',
                borderRadius:6,
                resize:'vertical',
                boxSizing:'border-box',
                whiteSpace:'pre',
                overflowWrap:'normal',
                overflowX:'auto',
              }}
            />
            <div style={{ fontSize:10, color:'var(--text-dim)', marginTop:6, fontFamily:'var(--font-mono)' }}>
              {editDraft.length.toLocaleString('pt-PT')} chars {editDraft.length < 100 && ' · mínimo 100'}
            </div>
          </div>
        )}
        <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
        {fichaMd && (
          <div style={{ padding:'8px 16px', borderTop:'1px solid var(--border,#e5e7eb)', fontSize:10, color:'var(--text-dim)', background:'rgba(15,23,42,.02)', display:'flex', gap:12, flexWrap:'wrap', alignItems:'center' }}>
            <span style={{ display:'inline-flex', alignItems:'center', gap:5 }}>
              <window.Icon name="info" size={12}/>
              Este markdown substitui o placeholder <code style={{ background:'rgba(15,23,42,.08)', padding:'1px 5px', borderRadius:3 }}>{'{campanhas}'}</code> no SP_CLIENTES
            </span>
            <span style={{ marginLeft:'auto' }}>Regenera após editar briefing / conceito / estratégia / segmentação</span>
          </div>
        )}
      </div>

      {/* ══ PROCESSO SDR AI POR MERCADO (Sonnet 4.6 do funil-context) ══ */}
      <div style={{ background:'linear-gradient(135deg,rgba(17,41,84,.04),rgba(56,89,208,.06))', border:'2px solid rgba(56,89,208,.2)', borderRadius:12, padding:'18px 20px' }}>
        <div style={{ fontSize:11, fontWeight:700, fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.08em', color:'#3859D0', marginBottom:4, display:'flex', alignItems:'center', gap:8, flexWrap:'wrap' }}>
          <span>Processo SDR AI · gerado por Sonnet 4.6 do funil-context</span>
          {langBadge('multi')}
          {ctxLoading && <span style={{ fontSize:9, color:'#7c3aed' }}>a carregar...</span>}
          {funilCtx?._cached && <span style={{ fontSize:9, color:'#16a34a' }}>· em cache</span>}
        </div>
        <div style={{ fontSize:11, color:'var(--text-muted)', marginBottom:16 }}>
          Quatro letras BANT. Cada uma com duas perguntas de recurso desta campanha. A abertura de cada letra está na system prompt.
        </div>

        {/* Cards por mercado (PT + ES + ...) */}
        {Object.keys(sdrByMarket).length === 0 && !ctxLoading && (
          <div style={{ padding:14, background:'rgba(245,158,11,.08)', border:'1px dashed #f59e0b', borderRadius:8, fontSize:11, color:'#92400e' }}>
            Sem contexto por mercado. Gera a ficha: ela pede as duas perguntas de recurso de cada letra.
          </div>
        )}
        <div style={{ display:'flex', flexDirection:'column', gap:14 }}>
          {Object.entries(sdrByMarket).map(([country, sdrData]) => sdrData && (
            <div key={country} style={{ background:'var(--bg-card,#fff)', borderRadius:10, border:'1px solid var(--border)', overflow:'hidden' }}>
              <div style={{ padding:'10px 14px', background:'var(--bg-sunken)', borderBottom:'1px solid var(--border)', display:'flex', alignItems:'center', gap:10 }}>
                <div style={{ width:24, height:24, borderRadius:4, background:bCol, display:'flex', alignItems:'center', justifyContent:'center', fontSize:10, fontWeight:700, color:'#fff' }}>{country}</div>
                <div style={{ fontSize:12, fontWeight:700, color:'var(--navy,#112954)' }}>Mercado {country}</div>
              </div>
              <div style={{ padding:'14px 16px', display:'flex', flexDirection:'column', gap:12 }}>
                {sdrData.bant ? (
                  <>
                    {SDR_BANT_LETRAS.map(letra => (
                      <div key={letra.id}>
                        <div style={{ fontSize:9, fontWeight:700, color:bCol, fontFamily:'var(--font-mono)', textTransform:'uppercase', marginBottom:4 }}>{letra.label} · {letra.nota}</div>
                        {(Array.isArray(sdrData.bant[letra.id]) ? sdrData.bant[letra.id] : []).map((q, i) => (
                          <div key={i} style={{ display:'flex', gap:8, marginBottom:4 }}>
                            <div style={{ width:18, height:18, borderRadius:'50%', background:'#7c3aed15', color:'#7c3aed', display:'flex', alignItems:'center', justifyContent:'center', fontSize:9, fontWeight:700, flexShrink:0 }}>{i + 1}</div>
                            <div style={{ fontSize:11, color:'var(--text)', lineHeight:1.5 }}>{q}</div>
                          </div>
                        ))}
                        {!(sdrData.bant[letra.id] || []).length && <div style={{ fontSize:11, color:'var(--text-muted)' }}>Sem pergunta de recurso nesta letra.</div>}
                      </div>
                    ))}
                    {sdrData.objecao_principal && (
                      <div style={{ padding:'8px 10px', background:'rgba(146,64,14,.05)', borderRadius:6, borderLeft:'3px solid #92400E' }}>
                        <div style={{ fontSize:9, fontWeight:700, color:'#92400E', fontFamily:'var(--font-mono)', textTransform:'uppercase', marginBottom:3 }}>Objecção principal</div>
                        <div style={{ fontSize:11, color:'var(--text)', lineHeight:1.5 }}>{sdrData.objecao_principal}</div>
                      </div>
                    )}
                    <div style={{ padding:'8px 10px', background:'rgba(22,163,74,.05)', borderRadius:6, borderLeft:'3px solid #16a34a' }}>
                      <div style={{ fontSize:9, fontWeight:700, color:'#16a34a', fontFamily:'var(--font-mono)', textTransform:'uppercase', marginBottom:3 }}>Critério desta campanha</div>
                      <div style={{ fontSize:11, color:'var(--text)', lineHeight:1.5 }}>{sdrCriterioMarca(funilCtx?.campanha?.brand_slug, funilCtx?.campanha?.brand_name)}</div>
                    </div>
                  </>
                ) : (
                  <div style={{ padding:10, background:'rgba(245,158,11,.08)', border:'1px dashed #f59e0b', borderRadius:8, fontSize:11, color:'#92400e' }}>
                    Esta geração ainda é a anterior. Gera a ficha outra vez para sair o banco BANT.
                  </div>
                )}
              </div>
            </div>
          ))}
        </div>

        {/* Nurturing stages por mercado */}
        {Object.entries(nurtByMarket).some(([_, s]) => s?.length > 0) && (
          <div style={{ marginTop:16, padding:'12px 14px', background:'var(--bg-card,#fff)', borderRadius:8, border:'1px solid var(--border)' }}>
            <div style={{ fontSize:10, fontWeight:700, color:'#4C1D95', fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.06em', marginBottom:10, display:'flex', alignItems:'center', flexWrap:'wrap' }}>
              <span>Nurturing WA — stages adaptados por mercado</span>
              {langBadge('multi')}
            </div>
            <div style={{ display:'grid', gridTemplateColumns:`repeat(${Object.keys(nurtByMarket).length}, 1fr)`, gap:12 }}>
              {Object.entries(nurtByMarket).map(([country, stages]) => (
                <div key={country}>
                  <div style={{ fontSize:9, fontWeight:700, color:bCol, marginBottom:6 }}>{country}</div>
                  {(stages || []).slice(0,4).map((s,i) => (
                    <div key={i} style={{ padding:'6px 8px', background:'var(--bg-sunken)', borderRadius:4, marginBottom:4, fontSize:10 }}>
                      <div style={{ display:'flex', gap:6, marginBottom:2 }}>
                        <span style={{ fontWeight:700, color:'#4C1D95' }}>Stage {s.stage}</span>
                        <span style={{ color:'var(--text-muted)' }}>{s.nome}</span>
                      </div>
                      <div style={{ color:'var(--text-muted)', fontSize:9, marginBottom:2 }}>{s.trigger} · {s.accao}</div>
                      {s.mensagem_exemplo && <div style={{ fontStyle:'italic', color:'var(--text)', fontSize:10 }}>"{s.mensagem_exemplo}"</div>}
                    </div>
                  ))}
                </div>
              ))}
            </div>
          </div>
        )}
      </div>

      {/* Grid produto + personas */}
      <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:16 }}>
        {card(<>
          {secTitle('Produto & Oferta', '#0F4C75', 'ref-pt')}
          {[
            { l:'Produto',    v: brief.commercial_name || campanha?.titulo },
            { l:'Pitch',      v: brief.elevator_pitch?.slice(0,120) },
            { l:'Oferta',     v: ofertaStr || null },
            { l:'CTA',        v: ctaLabel },
            { l:'Não nomear', v: competitors.join(' · ') || null },
          ].filter(r => r.v).map((r,i) => (
            <div key={i} style={{ display:'flex', gap:8, paddingBottom:5, borderBottom:'1px solid var(--border-light,#f1f5f9)' }}>
              <div style={{ fontSize:9, fontWeight:700, color:'#0F4C75', fontFamily:'var(--font-mono)', textTransform:'uppercase', minWidth:90, flexShrink:0 }}>{r.l}</div>
              <div style={{ fontSize:11, color:'var(--text)', lineHeight:1.4 }}>{r.v}</div>
            </div>
          ))}
        </>)}
        {card(<>
          {secTitle('Personas & Mensagem', '#3859D0', 'ref-pt')}
          {personas.slice(0,2).map((p,i) => (
            <div key={i} style={{ fontSize:11, color:'var(--text)', marginBottom:6, paddingLeft:8, borderLeft:`2px solid ${bCol}40` }}>
              <div style={{ fontWeight:600 }}>{_str(p.nome || p.perfil).slice(0,70)}</div>
              {p.peso && <div style={{ fontSize:9, color:'var(--text-dim)' }}>{p.peso}% peso · {p.razao?.slice(0,60)}</div>}
            </div>
          ))}
          {keyMessage && (
            <div style={{ marginTop:10, padding:'8px 10px', background:'rgba(56,89,208,.05)', borderRadius:6, borderLeft:'3px solid #3859D0' }}>
              <div style={{ fontSize:9, fontWeight:700, color:'#3859D0', fontFamily:'var(--font-mono)', textTransform:'uppercase', marginBottom:3 }}>Mensagem-chave</div>
              <div style={{ fontSize:10, color:'var(--text)', lineHeight:1.4 }}>{keyMessage}</div>
            </div>
          )}
        </>)}
      </div>

      {/* USP Matrix */}
      {card(<>
        {secTitle('USP Matrix — Dor → USP → Benefício para o SDR', '#065F46', 'ref-pt')}
        <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap:6, marginBottom:8 }}>
          {['DOR DO CLIENTE','USP RELEVANTE','BENEFÍCIO CONCRETO (script)'].map((h,i) => (
            <div key={i} style={{ fontSize:9, fontWeight:700, color:'#065F46', fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.05em' }}>{h}</div>
          ))}
        </div>
        {pains.slice(0,4).map((pain,i) => {
          const usp     = angles[i]?.usp_principal || usps[i] || usps[0] || '—';
          const angle   = angles[i];
          const benefit = angle?.beneficio || angle?.angulo || (usp.split('—')[0]?.trim()) || usp;
          return (
            <div key={i} style={{ display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap:6, padding:'7px 0', borderTop:'1px solid var(--border-light,#f1f5f9)' }}>
              <div style={{ fontSize:10, color:'#dc2626', lineHeight:1.4 }}>{pain.slice(0,100)}{pain.length>100?'…':''}</div>
              <div style={{ fontSize:10, color:'var(--text)', fontWeight:500, lineHeight:1.4 }}>{usp.slice(0,100)}{usp.length>100?'…':''}</div>
              <div style={{ fontSize:10, color:'#065F46', lineHeight:1.4 }}>{_str(benefit).slice(0,100)}{_str(benefit).length>100?'…':''}</div>
            </div>
          );
        })}
      </>)}

      <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:16 }}>
        {/* Objecções */}
        {card(<>
          {secTitle('Objecções Esperadas & Respostas SDR', '#92400E', 'ref-pt')}
          {objections.map((obj,i) => (
            <div key={i} style={{ padding:'8px 0', borderBottom:'1px solid var(--border-light,#f1f5f9)' }}>
              <div style={{ fontSize:10, fontWeight:600, color:'#92400E', marginBottom:4, display:'flex', gap:6, alignItems:'flex-start' }}>
                <span style={{ display:'inline-flex', flexShrink:0, marginTop:1 }}><window.Icon name="close" size={11}/></span>
                <span>{obj.slice(0,100)}{obj.length>100?'…':''}</span>
              </div>
              <div style={{ fontSize:10, color:'#065F46', paddingLeft:18, display:'flex', gap:6, alignItems:'flex-start' }}>
                <span style={{ display:'inline-flex', flexShrink:0, marginTop:1 }}><window.Icon name="check" size={11}/></span>
                <span>{objResponses[i]?.slice(0,120)}{objResponses[i]?.length>120?'…':''}</span>
              </div>
            </div>
          ))}
          {objections.length === 0 && (
            <div style={{ fontSize:11, color:'var(--text-muted)' }}>O briefing não tem objecções. A Digi usa as USPs e a mensagem-chave.</div>
          )}
        </>)}

        {/* Planeamento contacto */}
        {card(<>
          {secTitle('Planeamento de Contacto', '#16a34a')}
          {temWa && (
            <div style={{ marginBottom:10 }}>
              <div style={{ fontSize:9, fontWeight:700, color:'#16a34a', fontFamily:'var(--font-mono)', textTransform:'uppercase', marginBottom:6 }}>WhatsApp · produção de conteúdos</div>
              {waGrupos.map(g => (
                <div key={g.papel} style={{ fontSize:10, color:'var(--text)', marginBottom:4 }}>
                  <span style={{ fontWeight:600 }}>{g.nome}</span>
                  <span style={{ color:'var(--text-dim)' }}> · {g.itens.length || g.nota}</span>
                </div>
              ))}
            </div>
          )}
          {emailInterno && (
            <div style={{ fontSize:10, color:'var(--text)', marginBottom:6 }}>Email interno à equipa · {emailInterno.planned_date || 'antes do arranque'}</div>
          )}
          {emailCliente.length > 0 && (
            <div>
              <div style={{ fontSize:9, fontWeight:700, color:'#7c3aed', fontFamily:'var(--font-mono)', textTransform:'uppercase', marginBottom:6 }}>Email · um por USP</div>
              {emailCliente.map((e,i) => (
                <div key={i} style={{ display:'flex', justifyContent:'space-between', fontSize:10, color:'var(--text)', marginBottom:3 }}>
                  <span>{(e.titulo || `USP ${e.usp_index || i + 1}`).slice(0, 42)}</span>
                  <span style={{ color:'var(--text-dim)', fontFamily:'var(--font-mono)', fontSize:9 }}>{e.planned_date || ''}</span>
                </div>
              ))}
            </div>
          )}
          {!temWa && emailCliente.length === 0 && (
            <div style={{ fontSize:11, color:'var(--text-muted)' }}>Gera o Conceito para ver as peças de contacto.</div>
          )}
        </>)}
      </div>

      <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:16 }}>
        {/* CRM Fields */}
        {card(<>
          {secTitle('CRM — Campos a Registar após Conversa', '#0891b2')}
          <div style={{ display:'flex', flexDirection:'column', gap:3 }}>
            {CRM_FIELDS.map((f,i) => (
              <div key={i} style={{ display:'flex', alignItems:'center', gap:8, padding:'3px 0', borderBottom:'1px solid var(--border-light,#f1f5f9)' }}>
                <div style={{ width:14, height:14, borderRadius:3, border:'1.5px solid #0891b230', background:'var(--bg-sunken)', flexShrink:0 }}/>
                <div style={{ fontSize:10, color:'var(--text)' }}>{f}</div>
              </div>
            ))}
          </div>
        </>)}

        {/* Score */}
        {card(<>
          {secTitle(`Score SDR · ${SCORE.reduce((s,i)=>s+i.pts,0)} pontos`, bCol)}
          <div style={{ display:'flex', flexDirection:'column', gap:5, marginBottom:12 }}>
            {SCORE.map((s,i) => (
              <div key={i} style={{ display:'flex', justifyContent:'space-between', alignItems:'center', padding:'5px 8px', background:'var(--bg-sunken)', borderRadius:5 }}>
                <div style={{ fontSize:10, color:'var(--text)' }}>{s.criterio}</div>
                <div style={{ fontSize:11, fontWeight:700, color:bCol, fontFamily:'var(--font-mono)', flexShrink:0, marginLeft:8 }}>{s.pts}</div>
              </div>
            ))}
          </div>
          <div style={{ display:'grid', gridTemplateColumns:'repeat(3,1fr)', gap:6 }}>
            {[
              { l:'Avança', r:'75–100', col:'#dc2626', bg:'#fef2f2' },
              { l:'Qualificado', r:'50–74', col:'#16a34a', bg:'#f0fdf4' },
              { l:'Fica em nurturing', r:'<50', col:'#d97706', bg:'#fffbeb' },
            ].map((t,i) => (
              <div key={i} style={{ background:t.bg, borderRadius:6, padding:'8px', textAlign:'center' }}>
                <div style={{ fontSize:9, fontWeight:700, color:t.col, fontFamily:'var(--font-mono)' }}>{t.r}</div>
                <div style={{ fontSize:9, color:t.col, marginTop:2 }}>{t.l}</div>
              </div>
            ))}
          </div>
        </>)}
      </div>

      {provaDeAmostra && (
        <div style={{ padding:'12px 16px', background:'rgba(22,163,74,.06)', border:'1px solid rgba(22,163,74,.25)', borderRadius:8, fontSize:12, color:'var(--text)' }}>
          A oferta desta campanha é uma amostra. O follow-up confirma se chegou, se foi testada e qual é o próximo passo.
        </div>
      )}

      {/* Aviso activação */}
      <div style={{ padding:'12px 16px', background:'rgba(56,89,208,.04)', border:'1px dashed rgba(56,89,208,.25)', borderRadius:8, fontSize:11, color:'var(--text-muted)', display:'flex', alignItems:'center', gap:10 }}>
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#3859D0" strokeWidth="2"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>
        <span>Esta ficha será injectada no <strong>SP_CLIENTES</strong> como contexto de campanha activa (PASSO 0, item 4). Quando activada, a Digi AI recebe produto, USPs, CTA, objecções e plano de contacto desta campanha.</span>
      </div>

      {/* Status strip + botão Aprovar SDR AI — só visível se status=sdr_ai_pendente */}
      {campanha?.status === 'sdr_ai_pendente' && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 16px', background: 'var(--bg-surface,#fff)', borderRadius: 8, boxShadow: 'var(--shadow-card)', marginTop: 12 }}>
          <span style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', padding: '3px 8px', borderRadius: 4, background: '#EDE9FE', color: '#7c3aed' }}>Aguarda validação SDR AI</span>
          <span style={{ fontSize: 11, color: 'var(--text-muted)' }}>Revê o processo de qualificação. Se estiver ok, aprova: o email de aprovação vai só para fabio.costa@digidelta.pt.</span>
          <div style={{ marginLeft: 'auto', display: 'flex', gap: 6 }}>
            <button className="btn btn-ai" onClick={() => onAction && onAction('aprovarSdrAi')} style={{ height: 32, padding: '0 16px', fontSize: 12 }}>
              Aprovar SDR AI →
            </button>
          </div>
        </div>
      )}
      {campanha?.status === 'sdr_ai_aprovado' && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 16px', background: 'var(--green-100,#E1F7E6)', borderRadius: 8, marginTop: 12 }}>
          <span style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--green-700,#1F8A52)' }}>SDR AI aprovado</span>
          <span style={{ fontSize: 11, color: 'var(--green-700,#1F8A52)' }}>Campanha em aprovação executiva.</span>
        </div>
      )}

    </div>
  );
};

// ── CampanhaDetail ─────────────────────────────────────────────────────────────
// Vista de detalhe: fetch da campanha completa + tabs (Briefing/Conceito/Copy/…)
