/* screen_marketing_campanhas.jsx
   Marketing · Campanhas — Geração de conceito, copy e prompts visuais a partir de briefings aprovados.
   Fluxo: conceito_pendente → conceito_gerado → copy_pendente → copy_gerado
          → prompts_pendente → prompts_gerado → em_producao
   Expõe window.MktCampanhasScreen.
*/

// ── Cores de marca ─────────────────────────────────────────────────────────────
const _BC = {
  mimaki: '#e60012', biond: '#00a86b', decal: '#f59e0b',
  alldecor: '#ec4899', sensek: '#8B2DE8', netscreen: '#F97316', digidelta: '#3859D0',
};
const _brandColor = (slug) => _BC[slug] || '#3859D0';

// Helper global: limpa travessões e en-dashes de todo o texto gerado por IA
const ct = (t) => !t ? t : String(t).replace(/\u2014|\u2013/g, ',').replace(/ ,/g, ',').replace(/,,+/g, ',').replace(/,\s*,/g, ',');

// Split texto em parágrafos numerados: tenta (1)(2)(3), fallback por ponto+vírgula, fallback por \n
const splitPoints = (raw) => {
  if (!raw) return [];
  const cleaned = ct(raw);
  // Tenta split por (1) (2) (3)...
  const parts = cleaned.split(/\s*\((\d+)\)\s*/).reduce((acc, seg, i, arr) => {
    if (/^\d+$/.test(seg)) return acc;
    const num = /^\d+$/.test(arr[i-1]) ? arr[i-1] : null;
    const txt = seg.trim();
    if (txt) acc.push({ num, txt });
    return acc;
  }, []);
  if (parts.length > 1) return parts;
  // Fallback 1: split por ponto e vírgula (;)
  const bySemi = cleaned.split(/;\s*/).map(t => t.trim()).filter(t => t.length > 20);
  if (bySemi.length > 1) return bySemi.map((txt, i) => ({ num: String(i + 1), txt: txt.replace(/\.$/, '') + '.' }));
  // Fallback 2: split por ". " seguido de nome de canal ou palavra maiúscula (para abordagem)
  const byPeriod = cleaned.split(/\.\s+(?=[A-ZÁÉÍÓÚÀÂÊÔÃÕÇ])/).map(t => t.trim()).filter(t => t.length > 30);
  if (byPeriod.length > 1) return byPeriod.map((txt, i) => ({ num: String(i + 1), txt: txt.replace(/\.$/, '') + '.' }));
  // Fallback: parágrafo único
  return [{ num: null, txt: cleaned }];
};

// ── Helpers ────────────────────────────────────────────────────────────────────
// KPIs podem vir como string[] (legado) ou {canal,kpi}[] (novo formato)
const formatKpis = (arr) =>
  (Array.isArray(arr) ? arr : [])
    .filter(Boolean)
    .map(k => typeof k === 'string' ? k : (k.canal ? `${k.canal}: ${k.kpi}` : k.kpi || ''))
    .filter(Boolean)
    .join(' · ') || '—';

// ── API ────────────────────────────────────────────────────────────────────────
async function campApiCall(url, options = {}) {
  let res;
  try {
    res = await fetch(url, { headers: { 'Content-Type': 'application/json' }, ...options });
  } catch (e) {
    throw new Error(`Erro de rede: ${e.message}`);
  }
  if (!res.ok) {
    let body = {};
    try { body = await res.json(); } catch {}
    throw new Error(body.error || `HTTP ${res.status}`);
  }
  return res.json();
}

const CampAPI = {
  list:             ()           => campApiCall('/api/marketing/campanhas'),
  get:              (id)         => campApiCall(`/api/marketing/campanhas/${id}`),
  update:           (id, body)   => campApiCall(`/api/marketing/campanhas/${id}`, { method: 'PUT',    body: JSON.stringify(body) }),
  remove:           (id)         => campApiCall(`/api/marketing/campanhas/${id}`, { method: 'DELETE' }),
  generateSchedule: (id, onStep) => {
    return new Promise(async (resolve, reject) => {
      try {
        const res = await fetch(`/api/marketing/campanhas/${id}/generate-schedule`, { method: 'POST', headers: { 'Content-Type': 'application/json' } });
        const reader = res.body.getReader(); const decoder = new TextDecoder(); let buf = '';
        while (true) {
          const { done, value } = await reader.read();
          if (value) {
            buf += decoder.decode(value, { stream: !done });
            const lines = buf.split('\n'); buf = done ? '' : (lines.pop() ?? '');
            for (const line of lines) {
              if (!line.startsWith('data: ')) continue;
              let evt; try { evt = JSON.parse(line.slice(6)); } catch { continue; }
              if (evt.type === 'status' && onStep) onStep(evt.step, evt.message);
              else if (evt.type === 'done') resolve(evt);
              else if (evt.type === 'error') reject(new Error(evt.message));
            }
          }
          if (done) break;
        }
      } catch (e) { reject(e); }
    });
  },
  generateIdiomas: (id, extraLinguas, onStep) => {
    return new Promise(async (resolve, reject) => {
      try {
        const res = await fetch(`/api/marketing/campanhas/${id}/generate-idiomas`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ extra_linguas: extraLinguas || [] }) });
        const reader = res.body.getReader(); const decoder = new TextDecoder(); let buf = '';
        while (true) {
          const { done, value } = await reader.read();
          if (value) {
            buf += decoder.decode(value, { stream: !done });
            const lines = buf.split('\n'); buf = done ? '' : (lines.pop() ?? '');
            for (const line of lines) {
              if (!line.startsWith('data: ')) continue;
              let evt; try { evt = JSON.parse(line.slice(6)); } catch { continue; }
              if (evt.type === 'status' && onStep) onStep(evt.step, evt.message, evt.total, evt.langs);
              else if (evt.type === 'done') resolve(evt.data);
              else if (evt.type === 'error') reject(new Error(evt.message));
            }
          }
          if (done) { if (buf.startsWith('data: ')) { let e; try { e=JSON.parse(buf.slice(6)); if(e.type==='done') resolve(e.data); } catch {} } break; }
        }
      } catch (e) { reject(e); }
    });
  },
  patchIdiomas: (id, data) => fetch(`/api/marketing/campanhas/${id}/idiomas`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }).then(r => r.json()),
  generateConceito: (id, onStep) => {
    return new Promise(async (resolve, reject) => {
      try {
        const res = await fetch(`/api/marketing/campanhas/${id}/generate-conceito`, { method: 'POST', headers: { 'Content-Type': 'application/json' } });
        // Verificar erro HTTP antes de ler como SSE
        if (!res.ok) {
          let msg = `HTTP ${res.status}`;
          try { const j = await res.json(); msg = j.error || msg; } catch {}
          return reject(new Error(msg));
        }
        const reader = res.body.getReader();
        const decoder = new TextDecoder();
        let buf = '';
        while (true) {
          const { done, value } = await reader.read();
          if (value) {
            buf += decoder.decode(value, { stream: !done });
            const lines = buf.split('\n');
            buf = done ? '' : (lines.pop() ?? '');
            for (const line of lines) {
              if (!line.startsWith('data: ')) continue;
              let evt; try { evt = JSON.parse(line.slice(6)); } catch { continue; }
              if (evt.type === 'status' && onStep) onStep(evt.step, evt.message);
              else if (evt.type === 'done') resolve(evt.data);
              else if (evt.type === 'error') reject(new Error(evt.message));
            }
          }
          if (done) { if (buf.startsWith('data: ')) { let e; try { e=JSON.parse(buf.slice(6)); if(e.type==='done') resolve(e.data); } catch {} } break; }
        }
      } catch (e) { reject(e); }
    });
  },
  generateCommPlan: (id, onStep) => {
    return new Promise(async (resolve, reject) => {
      try {
        const res = await fetch(`/api/marketing/campanhas/${id}/generate-comm-plan`, { method: 'POST', headers: { 'Content-Type': 'application/json' } });
        if (!res.ok) { let msg = `HTTP ${res.status}`; try { const j = await res.json(); msg = j.error || msg; } catch {} return reject(new Error(msg)); }
        const reader = res.body.getReader(); const decoder = new TextDecoder(); let buf = '';
        while (true) {
          const { done, value } = await reader.read();
          if (value) {
            buf += decoder.decode(value, { stream: !done });
            const lines = buf.split('\n'); buf = done ? '' : (lines.pop() ?? '');
            for (const line of lines) {
              if (!line.startsWith('data: ')) continue;
              let evt; try { evt = JSON.parse(line.slice(6)); } catch { continue; }
              if (evt.type === 'status' && onStep) onStep(evt.step, evt.message);
              else if (evt.type === 'done') resolve(evt);
              else if (evt.type === 'error') reject(new Error(evt.message));
            }
          }
          if (done) break;
        }
      } catch (e) { reject(e); }
    });
  },
  aprovarConceito:  (id, actor)  => campApiCall(`/api/marketing/campanhas/${id}/conceito`, { method: 'PATCH', body: JSON.stringify({ action: 'aprovar', ...(actor||{}) }) }),
  patchConceito:    (id, data)   => campApiCall(`/api/marketing/campanhas/${id}/conceito`, { method: 'PATCH', body: JSON.stringify(data) }),
  generateCopy: (id, onStep) => {
    return new Promise(async (resolve, reject) => {
      try {
        const res = await fetch(`/api/marketing/campanhas/${id}/generate-copy`, { method: 'POST', headers: { 'Content-Type': 'application/json' } });
        const reader = res.body.getReader();
        const decoder = new TextDecoder();
        let buf = '';
        while (true) {
          const { done, value } = await reader.read();
          if (value) {
            buf += decoder.decode(value, { stream: !done });
            const lines = buf.split('\n');
            buf = done ? '' : (lines.pop() ?? '');
            for (const line of lines) {
              if (!line.startsWith('data: ')) continue;
              let evt; try { evt = JSON.parse(line.slice(6)); } catch { continue; }
              if (evt.type === 'status' && onStep) onStep(evt.step, evt.message);
              else if (evt.type === 'done') resolve(evt.data);
              else if (evt.type === 'error') reject(new Error(evt.message));
            }
          }
          if (done) { if (buf.startsWith('data: ')) { let e; try { e=JSON.parse(buf.slice(6)); if(e.type==='done') resolve(e.data); } catch {} } break; }
        }
      } catch (e) { reject(e); }
    });
  },
  patchCopy:        (copyId, b)  => campApiCall(`/api/marketing/copy/${copyId}`,    { method: 'PATCH', body: JSON.stringify(b) }),
  approveCopyAll:   (id)         => campApiCall(`/api/marketing/campanhas/${id}/copy/approve-all`, { method: 'POST', body: JSON.stringify({ approver_name: window.currentUser?.nome || null, approver_email: window.currentUser?.email || null }) }),
  generatePrompts:  (id)         => campApiCall(`/api/marketing/campanhas/${id}/prepare-prompts`, { method: 'POST' }),
  patchPrompt:      (pId, b)     => campApiCall(`/api/marketing/prompts/${pId}`,    { method: 'PATCH', body: JSON.stringify(b) }),
  enviarProducao:   (id, actor)  => campApiCall(`/api/marketing/campanhas/${id}/enviar-producao`, { method: 'POST', body: JSON.stringify(actor||{}) }),
  publicar:         (id, data)   => campApiCall(`/api/marketing/campanhas/${id}/publicar`, { method: 'POST', body: JSON.stringify(data) }),
  generateCanais: (id, onStep) => new Promise(async (resolve, reject) => {
    try {
      const res = await fetch(`/api/marketing/campanhas/${id}/generate-channel-setup`, { method: 'POST', headers: { 'Content-Type': 'application/json' } });
      const reader = res.body.getReader(); const decoder = new TextDecoder(); let buf = '';
      while (true) {
        const { done, value } = await reader.read();
        if (value) {
          buf += decoder.decode(value, { stream: !done });
          const lines = buf.split('\n'); buf = done ? '' : (lines.pop() ?? '');
          for (const line of lines) {
            if (!line.startsWith('data: ')) continue;
            let evt; try { evt = JSON.parse(line.slice(6)); } catch { continue; }
            if (evt.type === 'status' && onStep) onStep(evt.step, evt.message);
            else if (evt.type === 'done') resolve(evt.data);
            else if (evt.type === 'error') reject(new Error(evt.message));
          }
        }
        if (done) break;
      }
    } catch (e) { reject(e); }
  }),
  aprovarCanais:  (id, actor) => campApiCall(`/api/marketing/campanhas/${id}/canais`, { method: 'PATCH', body: JSON.stringify({ action: 'aprovar', ...(actor||{}) }) }),
  patchIdiomas:   (id, data) => campApiCall(`/api/marketing/campanhas/${id}/idiomas`, { method: 'PATCH', body: JSON.stringify(data) }),
  getBriefing:           (bid)  => campApiCall(`/api/marketing/briefings/${bid}`),
  getBrands:             ()     => campApiCall('/api/marketing/brands'),
  availableForCampaign:  ()     => campApiCall('/api/marketing/briefings/available-for-campaign'),
  create:                (body) => campApiCall('/api/marketing/campanhas', { method: 'POST', body: JSON.stringify(body) }),
  requestChanges:        (id, data) => campApiCall(`/api/marketing/campanhas/${id}/approval`, { method: 'POST', body: JSON.stringify(data) }),
  getEstrategia:    (id)         => campApiCall(`/api/marketing/campanhas/${id}/estrategia`),
  generateEstrategia: (id, onStep) => new Promise(async (resolve, reject) => {
    try {
      const res = await fetch(`/api/marketing/campanhas/${id}/generate-estrategia`, {
        method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}',
      });
      const reader = res.body.getReader(); const decoder = new TextDecoder(); let buf = '';
      while (true) {
        const { done, value } = await reader.read();
        if (value) {
          buf += decoder.decode(value, { stream: !done });
          const events = buf.split('\n\n'); buf = done ? '' : (events.pop() || '');
          for (const evt of events) {
            const line = evt.trim(); if (!line.startsWith('data:')) continue;
            let parsed; try { parsed = JSON.parse(line.slice(5).trim()); } catch { continue; }
            if (parsed.type === 'status' && onStep) onStep(parsed.step || 0, parsed.message);
            else if (parsed.type === 'done')  resolve(parsed);
            else if (parsed.type === 'error') reject(new Error(parsed.message));
          }
        }
        if (done) break;
      }
    } catch (e) { reject(e); }
  }),
  aprovarEstrategia: (id, approver) => campApiCall(`/api/marketing/campanhas/${id}/estrategia/approve`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ approver_email: approver?.email, approver_name: approver?.name }),
  }),
};
window.MktCampanhasAPI = { list: CampAPI.list };

// ── Toast global partilhado ────────────────────────────────────────────────────
const MktCampToast = ({ msg, type }) => {
  if (!msg) return null;
  const bg = type === 'error' ? '#dc2626' : type === 'info' ? '#d97706' : '#15803d';
  return (
    <div style={{
      position: 'fixed', bottom: 24, right: 24, zIndex: 9999,
      padding: '12px 20px', borderRadius: 8, fontSize: 13, fontWeight: 500,
      background: bg, color: '#fff', boxShadow: '0 4px 16px rgba(0,0,0,.2)',
      fontFamily: 'var(--font-body, Inter, sans-serif)',
    }}>{msg}</div>
  );
};

// ── Permissões ─────────────────────────────────────────────────────────────────
const MKT_OWNERS_EMAILS = ['fabio.costa@digidelta.pt', 'rui.leitao@digidelta.pt', 'joao.paulino@digidelta.pt'];
// userEmail vem como prop do wrapper (via /api/marketing/me) ou fallback window.currentUser
const canActOnCampaign = (userEmail) => {
  const email = (userEmail || window.currentUser?.email || '').toLowerCase();
  if (!email) return true; // dev sem auth: permitir
  return MKT_OWNERS_EMAILS.includes(email) || ['admin','c_suite'].includes(window.currentUser?.tipo_utilizador || window.currentUser?.role || '');
};
const isMktOwner = canActOnCampaign;
const FINAL_STATUSES = ['em_producao', 'publicado'];

// Campanhas com mecânica de risco alto requerem aprovação executiva (Chairman) antes de produção
const requiresExecutiveApproval = (campanha) => {
  if (!campanha?.briefing) return false;
  let offer = campanha?.briefing?.commercial_offer || campanha?.commercial_offer;
  if (!offer) return false;
  if (typeof offer === 'string') {
    try { offer = JSON.parse(offer); }
    catch {
      console.warn('[requiresExecutiveApproval] parse failed, fail-safe → true');
      return true; // fail-safe: escalar para Chairman quando há dúvida
    }
  }
  return ['large','premium','enterprise'].includes(offer.tier) || ['digirent','trade_in'].includes(offer.type);
};

// ── Kanban ─────────────────────────────────────────────────────────────────────
const KANBAN_COLS = [
  { id: 'briefing',    label: 'Briefing',    color: '#3859D0', statuses: [] },
  { id: 'estrategia',  label: 'Estratégia',  color: '#5B43C5', statuses: ['estrategia_pendente', 'estrategia_gerada', 'estrategia_aprovada', 'conceito_pendente'] },
  { id: 'conceito',    label: 'Conceito',    color: '#94a3b8', statuses: ['conceito_gerado', 'conceito', 'conceito_aprovado', 'canais_pendente', 'canais_gerado', 'canais_aprovado', 'copy_pendente', 'copy_gerado', 'copy_aprovado', 'idiomas_pendente', 'idiomas_gerado', 'idiomas_aprovado', 'prompts_pendente', 'prompts_gerado'] },
  { id: 'orcamento',   label: 'Orçamento',   color: '#0891b2', statuses: ['orcamento_pendente', 'orcamento_gerado', 'orcamento_aprovado'] },
  { id: 'segmentacao', label: 'Target', color: '#7c3aed', statuses: ['segmentacao_pendente', 'segmentacao_gerada', 'segmentacao_aprovada'] },
  { id: 'planeamento', label: 'Planeamento', color: '#0f766e', statuses: ['planeamento_pendente', 'planeamento_aprovado'] },
  { id: 'funil',       label: 'Funil',       color: '#112954', statuses: ['funil_pendente', 'funil_revisto'] },
  { id: 'aprovacao',   label: 'Aprovação',   color: '#ea580c', statuses: ['prompts_aprovado', 'pending_executive', 'em_aprovacao'] },
  { id: 'producao',    label: 'Produção',    color: '#22c55e', statuses: ['em_producao', 'publicado'] },
];

const STATUS_COL = {};
KANBAN_COLS.forEach(col => col.statuses.forEach(s => { STATUS_COL[s] = col; }));

const CANAL_ICON = {
  // IDs antigos (backward compat com briefings e comm_plans existentes)
  instagram: '📸', linkedin: '💼', email: '✉️', whatsapp: '💬',
  ads: '📢', site: '🌐', tiktok: '🎵', led: '💡',
  google_ads: '🔍', facebook: '📘', youtube: '▶️', social: '📱',
  // Novos IDs de canal (briefings 2026+)
  meta_ads: '📢', linkedin_ads: '💼',
  google_ads_search: '🔍', google_ads_display: '🖼',
  muppi_led: '💡', website: '🌐',
  // Formatos específicos gerados pelo conceito
  instagram_post: '📸', instagram_reel: '🎬', instagram_story: '📸', instagram_carrossel: '📸',
  facebook_post: '📘', facebook_reel: '🎬', facebook_story: '📘', facebook_carrossel: '📘',
  linkedin_post: '💼', linkedin_carrossel: '💼', linkedin_video: '🎬',
  youtube_short: '▶️',
};
const CANAL_LABEL = {
  // IDs antigos (backward compat)
  instagram: 'Instagram', linkedin: 'LinkedIn', email: 'Email', whatsapp: 'WhatsApp',
  ads: 'Ads', site: 'Site', tiktok: 'TikTok', led: 'LED/Muppi',
  google_ads: 'Google Ads', facebook: 'Facebook', youtube: 'YouTube', social: 'Social',
  // Novos IDs de canal (briefings 2026+)
  meta_ads: 'Meta Ads', linkedin_ads: 'LinkedIn Ads',
  google_ads_search: 'Google Search', google_ads_display: 'Google Display',
  muppi_led: 'Painéis LED', website: 'Blog', site: 'Blog',
  email_interno: 'Email Interno', whatsapp_interno: 'WhatsApp Interno',
  portal_notificacao: 'Portal · Notificação',
  // Canal composto de comunicação interna
  'email_interno+whatsapp_interno+portal_notificacao': 'Comunicação Interna',
  // Formatos específicos
  instagram_post: 'Instagram · Post', instagram_reel: 'Instagram · Reel',
  instagram_story: 'Instagram · Story', instagram_carrossel: 'Instagram · Carrossel',
  facebook_post: 'Facebook · Post', facebook_reel: 'Facebook · Reel',
  facebook_story: 'Facebook · Story', facebook_carrossel: 'Facebook · Carrossel',
  linkedin_post: 'LinkedIn · Post', linkedin_carrossel: 'LinkedIn · Carrossel',
  linkedin_video: 'LinkedIn · Vídeo', youtube_short: 'YouTube · Short',
};

// ── Helpers ────────────────────────────────────────────────────────────────────
const CampDropdown = ({ label, value, options, onChange, disabled }) => {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!open) return;
    const onClick = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onClick);
    return () => document.removeEventListener('mousedown', onClick);
  }, [open]);
  const current = options.find(o => o.value === value) || options[0];
  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button onClick={() => !disabled && setOpen(v => !v)} disabled={disabled} style={{
        background: '#ffffff', color: disabled ? 'var(--text-dim, #475569)' : 'var(--text, #1e293b)',
        border: '1px solid var(--border, #e2e8f0)', borderRadius: 6,
        padding: '6px 10px', fontSize: 11.5,
        display: 'inline-flex', alignItems: 'center', gap: 8,
        cursor: disabled ? 'not-allowed' : 'pointer', opacity: disabled ? 0.55 : 1,
        fontFamily: 'inherit', transition: 'border-color .15s',
      }}>
        <span style={{ color: 'var(--text-dim, #475569)', fontSize: 10, textTransform: 'uppercase', letterSpacing: '.06em', fontFamily: 'var(--font-mono, monospace)' }}>{label}</span>
        <span style={{ fontWeight: 500, color: disabled ? 'var(--text-dim, #475569)' : 'var(--text, #1e293b)' }}>{current?.label || '—'}</span>
        <span style={{ color: 'var(--text-muted, #64748b)', fontSize: 9 }}>▾</span>
      </button>
      {open && (
        <div className="animate-in" style={{
          position: 'absolute', top: 'calc(100% + 4px)', left: 0, zIndex: 50,
          background: '#ffffff', border: '1px solid var(--border, #e2e8f0)', borderRadius: 6,
          boxShadow: '0 8px 24px rgba(0,0,0,0.12)', minWidth: 180, padding: 4, maxHeight: 280, overflowY: 'auto',
        }}>
          {options.map(o => (
            <button key={o.value} onClick={() => { onChange(o.value); setOpen(false); }} style={{
              display: 'block', width: '100%', textAlign: 'left',
              background: o.value === value ? 'color-mix(in oklch, var(--ai-500, #3859D0) 12%, transparent)' : 'transparent',
              color: o.value === value ? 'var(--ai-500, #3859D0)' : 'var(--text, #1e293b)',
              border: 'none', padding: '7px 10px', borderRadius: 4,
              fontSize: 12, fontWeight: o.value === value ? 600 : 500,
              cursor: 'pointer', fontFamily: 'inherit',
            }}>{o.label}</button>
          ))}
        </div>
      )}
    </div>
  );
};

const CampDateRangePicker = ({ value, onChange }) => {
  const ref = React.useRef(null);
  const [open, setOpen] = React.useState(false);
  const [draftStart, setDraftStart] = React.useState(value.start);
  const [draftEnd, setDraftEnd] = React.useState(value.end);
  const [draftPreset, setDraftPreset] = React.useState(value.preset);
  const [anchor, setAnchor] = React.useState(() => {
    const d = new Date(value.end); d.setDate(1); d.setMonth(d.getMonth() - 1); return d;
  });
  const [pickStart, setPickStart] = React.useState(true);
  React.useEffect(() => {
    if (!open) return;
    const onClick = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onClick);
    return () => document.removeEventListener('mousedown', onClick);
  }, [open]);
  const stripTime = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
  const addDays = (d, n) => { const x = new Date(d); x.setDate(x.getDate() + n); return x; };
  const fmt = (d) => d ? d.toLocaleDateString('pt-PT', { day: 'numeric', month: 'short', year: 'numeric' }).replace('.', '') : '—';
  const fmtShort = (d) => d ? d.toLocaleDateString('pt-PT', { day: 'numeric', month: 'short' }).replace('.', '') : '—';
  const presets = [
    { id: 'all', label: 'Todo o período' }, { id: 'custom', label: 'Personalizado' },
    { id: 'today', label: 'Hoje' }, { id: 'yesterday', label: 'Ontem' },
    { id: 'last_7d', label: 'Últimos 7 dias' }, { id: 'last_30d', label: 'Últimos 30 dias' },
    { id: 'this_month', label: 'Este mês' }, { id: 'last_month', label: 'Mês passado' },
    { id: 'last_90d', label: 'Últimos 90 dias' }, { id: 'this_year', label: 'Este ano' },
  ];
  const presetLabels = Object.fromEntries(presets.map(p => [p.id, p.label]));
  const applyPreset = (id) => {
    if (id === 'all') { setDraftPreset('all'); return; }
    const today = stripTime(new Date()); let s = today, e = today;
    switch (id) {
      case 'today': break; case 'yesterday': s = e = addDays(today, -1); break;
      case 'last_7d': s = addDays(today, -6); break; case 'last_30d': s = addDays(today, -29); break;
      case 'last_90d': s = addDays(today, -89); break;
      case 'this_month': s = new Date(today.getFullYear(), today.getMonth(), 1); break;
      case 'last_month': s = new Date(today.getFullYear(), today.getMonth() - 1, 1); e = new Date(today.getFullYear(), today.getMonth(), 0); break;
      case 'this_year': s = new Date(today.getFullYear(), 0, 1); break;
      case 'custom': return; default: return;
    }
    setDraftPreset(id); setDraftStart(s); setDraftEnd(e);
    setAnchor(new Date(e.getFullYear(), e.getMonth() - 1, 1)); setPickStart(true);
  };
  const handleDayClick = (d) => {
    if (pickStart || !draftStart || (draftStart && draftEnd && d < draftStart)) {
      setDraftStart(d); setDraftEnd(d); setPickStart(false); setDraftPreset('custom');
    } else {
      if (d < draftStart) setDraftStart(d);
      else { setDraftEnd(d); setPickStart(true); }
      setDraftPreset('custom');
    }
  };
  const apply = () => { onChange({ preset: draftPreset, start: draftStart, end: draftEnd }); setOpen(false); };
  const cancel = () => { setDraftStart(value.start); setDraftEnd(value.end); setDraftPreset(value.preset); setOpen(false); };
  const triggerLabel = presetLabels[value.preset] || 'Personalizado';
  const triggerRange = value.preset === 'all' ? '' : `${fmtShort(value.start)} – ${fmt(value.end)}`;
  const nextAnchor = new Date(anchor.getFullYear(), anchor.getMonth() + 1, 1);
  const Month = ({ date }) => {
    const y = date.getFullYear(), m = date.getMonth();
    const firstDay = new Date(y, m, 1).getDay(), daysInMonth = new Date(y, m + 1, 0).getDate();
    const monthLabel = date.toLocaleDateString('pt-PT', { month: 'long', year: 'numeric' }).toUpperCase();
    const cells = [];
    for (let i = 0; i < firstDay; i++) cells.push(null);
    for (let d = 1; d <= daysInMonth; d++) cells.push(new Date(y, m, d));
    const inRange = (d) => draftStart && draftEnd && d >= draftStart && d <= draftEnd;
    const isStart = (d) => draftStart && d.getTime() === stripTime(draftStart).getTime();
    const isEnd = (d) => draftEnd && d.getTime() === stripTime(draftEnd).getTime();
    return (
      <div style={{ marginBottom: 10 }}>
        <div style={{ fontSize: 10.5, fontFamily: 'var(--font-mono, monospace)', color: 'var(--text-dim, #475569)', letterSpacing: '.08em', padding: '4px 6px' }}>{monthLabel}</div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 1, fontSize: 11 }}>
          {cells.map((d, i) => {
            if (!d) return <div key={i} />;
            const r = inRange(d), s = isStart(d), e = isEnd(d), isEdge = s || e;
            return (
              <button key={i} onClick={() => handleDayClick(d)} style={{
                width: 30, height: 30, lineHeight: '30px', textAlign: 'center', padding: 0,
                border: 'none', cursor: 'pointer', fontFamily: 'inherit',
                background: isEdge ? 'var(--ai-500, #3859D0)' : (r ? 'color-mix(in oklch, var(--ai-500, #3859D0) 18%, transparent)' : 'transparent'),
                color: isEdge ? '#fff' : (r ? 'var(--ai-500, #3859D0)' : 'var(--text, #1e293b)'),
                borderRadius: isEdge ? '50%' : (r ? 0 : 4), fontWeight: isEdge ? 600 : 500, fontSize: 11.5,
              }}>{d.getDate()}</button>
            );
          })}
        </div>
      </div>
    );
  };
  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button onClick={() => setOpen(v => !v)} style={{
        background: '#ffffff', color: 'var(--text, #1e293b)',
        border: '1px solid var(--border, #e2e8f0)', borderRadius: 6, padding: '6px 10px', fontSize: 11.5,
        display: 'inline-flex', alignItems: 'center', gap: 8, cursor: 'pointer', fontFamily: 'inherit',
      }}>
        <span style={{ color: 'var(--text-dim, #475569)', fontSize: 10, textTransform: 'uppercase', letterSpacing: '.06em', fontFamily: 'var(--font-mono, monospace)' }}>Período</span>
        <span style={{ fontWeight: 600, color: 'var(--ai-500, #3859D0)', background: 'color-mix(in oklch, var(--ai-500, #3859D0) 12%, transparent)', padding: '1px 6px', borderRadius: 3 }}>{triggerLabel}</span>
        {triggerRange && <span style={{ color: 'var(--text-muted, #64748b)', fontFamily: 'var(--font-mono, monospace)', fontSize: 11 }}>{triggerRange}</span>}
        <span style={{ color: 'var(--text-muted, #64748b)', fontSize: 9 }}>▾</span>
      </button>
      {open && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 6px)', right: 0, zIndex: 60,
          background: '#ffffff', border: '1px solid var(--border, #e2e8f0)', borderRadius: 8,
          boxShadow: '0 12px 32px rgba(0,0,0,0.18)', display: 'flex', minWidth: 560,
        }}>
          <div style={{ width: 190, borderRight: '1px solid var(--border, #e2e8f0)', padding: '6px 0', maxHeight: 460, overflowY: 'auto' }}>
            {presets.map(p => (
              <button key={p.id} onClick={() => applyPreset(p.id)} style={{
                display: 'block', width: '100%', textAlign: 'left',
                background: draftPreset === p.id ? 'color-mix(in oklch, var(--ai-500, #3859D0) 12%, transparent)' : 'transparent',
                color: draftPreset === p.id ? 'var(--ai-500, #3859D0)' : 'var(--text, #1e293b)',
                border: 'none', padding: '8px 14px', fontSize: 12.5, fontWeight: draftPreset === p.id ? 600 : 500,
                cursor: 'pointer', fontFamily: 'inherit',
              }}>{p.label}</button>
            ))}
          </div>
          <div style={{ flex: 1, padding: 14, display: 'flex', flexDirection: 'column' }}>
            <div style={{ display: 'flex', gap: 8, marginBottom: 10 }}>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 9.5, color: 'var(--text-dim, #475569)', textTransform: 'uppercase', letterSpacing: '.08em', fontFamily: 'var(--font-mono, monospace)', marginBottom: 3 }}>Data início</div>
                <div style={{ border: `1px solid ${pickStart ? 'var(--ai-500, #3859D0)' : 'var(--border, #e2e8f0)'}`, borderRadius: 4, padding: '5px 8px', fontSize: 12, background: 'var(--bg, #f8fafc)' }}>{fmt(draftStart)}</div>
              </div>
              <div style={{ alignSelf: 'flex-end', padding: '6px 0', color: 'var(--text-muted, #64748b)' }}>—</div>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 9.5, color: 'var(--text-dim, #475569)', textTransform: 'uppercase', letterSpacing: '.08em', fontFamily: 'var(--font-mono, monospace)', marginBottom: 3 }}>Data fim</div>
                <div style={{ border: `1px solid ${!pickStart ? 'var(--ai-500, #3859D0)' : 'var(--border, #e2e8f0)'}`, borderRadius: 4, padding: '5px 8px', fontSize: 12, background: 'var(--bg, #f8fafc)' }}>{fmt(draftEnd)}</div>
              </div>
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }}>
              <button onClick={() => setAnchor(new Date(anchor.getFullYear(), anchor.getMonth() - 1, 1))} style={{ background: 'none', border: '1px solid var(--border, #e2e8f0)', borderRadius: 4, width: 24, height: 24, color: 'var(--text-muted, #64748b)', cursor: 'pointer' }}>‹</button>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 1, flex: 1, padding: '0 8px' }}>
                {['D','S','T','Q','Q','S','S'].map((d, i) => (
                  <div key={i} style={{ textAlign: 'center', fontSize: 10, color: 'var(--text-dim, #475569)', fontFamily: 'var(--font-mono, monospace)', textTransform: 'uppercase' }}>{d}</div>
                ))}
              </div>
              <button onClick={() => setAnchor(new Date(anchor.getFullYear(), anchor.getMonth() + 1, 1))} style={{ background: 'none', border: '1px solid var(--border, #e2e8f0)', borderRadius: 4, width: 24, height: 24, color: 'var(--text-muted, #64748b)', cursor: 'pointer' }}>›</button>
            </div>
            <div style={{ flex: 1, overflowY: 'auto', maxHeight: 360, padding: '0 30px' }}>
              <Month date={anchor} />
              <Month date={nextAnchor} />
            </div>
            <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 10, paddingTop: 10, borderTop: '1px solid var(--border, #e2e8f0)' }}>
              <button onClick={cancel} className="btn btn-xs">Cancelar</button>
              <button onClick={apply} className="btn btn-xs btn-ai">Aplicar</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

const CampStatusBadge = ({ status }) => {
  // Fase · fase X/8 · cor da fase
  const MAP = {
    estrategia_pendente:['Estratégia',         '#5B43C5', 'rgba(91,67,197,.10)',  '1/8'],
    estrategia_gerada:  ['Estratégia',         '#5B43C5', 'rgba(91,67,197,.14)',  '1/8'],
    estrategia_aprovada:['Estratégia ✓',       '#5B43C5', 'rgba(91,67,197,.18)',  '1/8'],
    conceito_pendente:  ['Conceito',          '#3859D0', 'rgba(56,89,208,.10)', '2/8'],
    conceito_gerado:    ['Conceito',          '#3859D0', 'rgba(56,89,208,.10)', '2/8'],
    conceito:           ['Conceito',          '#3859D0', 'rgba(56,89,208,.10)', '2/8'],
    canais_pendente:    ['Canais Setup',      '#0ea5e9', 'rgba(14,165,233,.10)', '3/8'],
    canais_gerado:      ['Canais Setup',      '#0ea5e9', 'rgba(14,165,233,.10)', '3/8'],
    canais_aprovado:    ['Canais Setup ✓',    '#0ea5e9', 'rgba(14,165,233,.14)', '3/8'],
    copy_pendente:      ['Copy',              '#f59e0b', 'rgba(245,158,11,.10)', '4/8'],
    copy_gerado:        ['Copy',              '#f59e0b', 'rgba(245,158,11,.14)', '4/8'],
    copy_aprovado:      ['Copy ✓',           '#f59e0b', 'rgba(245,158,11,.18)', '4/8'],
    idiomas_pendente:   ['Idiomas',           '#8b5cf6', 'rgba(139,92,246,.10)', '5/8'],
    idiomas_gerado:     ['Idiomas',           '#8b5cf6', 'rgba(139,92,246,.14)', '5/8'],
    idiomas_aprovado:   ['Idiomas ✓',         '#8b5cf6', 'rgba(139,92,246,.18)', '5/8'],
    prompts_pendente:   ['Prompts AI',        '#ec4899', 'rgba(236,72,153,.10)', '6/8'],
    prompts_gerado:     ['Prompts AI',        '#ec4899', 'rgba(236,72,153,.14)', '6/8'],
    geracao:            ['Prompts AI',        '#ec4899', 'rgba(236,72,153,.14)', '6/8'],
    aprovacao:          ['Prompts AI ✓',      '#ec4899', 'rgba(236,72,153,.18)', '6/8'],
    prompts_aprovado:   ['Aguarda Aprovação', '#d97706', 'rgba(217,119,6,.12)',  '7/8'],
    pending_executive:  ['Aguarda Aprovação', '#d97706', 'rgba(217,119,6,.12)',  '7/8'],
    em_producao:        ['Em Produção',        '#15803d', 'rgba(21,128,61,.12)', '8/8'],
    publicado:          ['Publicado',          '#3859D0', 'rgba(56,89,208,.14)', '8/8'],
  };
  const [label, color, bg, step] = MAP[status] || [status, '#94a3b8', 'rgba(148,163,184,.12)', '—'];
  return (
    <div style={{ display: 'inline-flex', alignItems: 'center', gap: 4 }}>
      <span style={{ fontSize: 10, fontWeight: 600, padding: '2px 8px', borderRadius: 99, color, background: bg, fontFamily: 'var(--font-mono, monospace)' }}>{label}</span>
      {step && <span style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'var(--font-mono, monospace)' }}>{step}</span>}
    </div>
  );
};

// ── Banner contextual ──────────────────────────────────────────────────────────
const CampBanner = ({ kpi, onFilter, campanhas = [], userEmail = '' }) => {
  const hour    = new Date().getHours();
  const [seed]  = React.useState(Math.random); // seed fixo por mount → saudação diferente a cada entrada
  const rawName = window.currentUser?.nome_apresentar || window.currentUser?.nome || '';
  const fromEmail = userEmail ? userEmail.split('@')[0].split('.')[0] : '';
  const raw     = rawName.split(' ')[0] || fromEmail || '';
  const first   = raw ? raw.charAt(0).toUpperCase() + raw.slice(1).toLowerCase() : '';
  const pl = (n, s, p) => n === 1 ? s : p;

  const { heading, body } = React.useMemo(() => {
    // ── Grupos de campanhas por estado ──────────────────────────────────────────
    const G = {
      semConceito:   campanhas.filter(c => ['conceito_pendente'].includes(c.status)),
      conceitoOK:    campanhas.filter(c => ['conceito_gerado'].includes(c.status)),
      canaisOK:      campanhas.filter(c => ['canais_pendente','canais_gerado'].includes(c.status)),
      copyOK:        campanhas.filter(c => ['copy_pendente','copy_gerado'].includes(c.status)),
      idiomasOK:     campanhas.filter(c => ['idiomas_pendente','idiomas_gerado'].includes(c.status)),
      promptsOK:     campanhas.filter(c => ['prompts_pendente','prompts_gerado'].includes(c.status)),
      aprovExec:     campanhas.filter(c => ['prompts_aprovado','pending_executive'].includes(c.status)),
      emProducao:    campanhas.filter(c => ['em_producao'].includes(c.status)),
      publicadas:    campanhas.filter(c => c.status === 'publicado'),
    };
    const nomes = (arr) => { if (!arr.length) return ''; if (arr.length <= 2) return arr.map(c=>c.titulo).join(' e '); return `${arr.slice(0,-1).map(c=>c.titulo).join(', ')} e ${arr[arr.length-1].titulo}`; };

    // ── Pool de saudações com humor — contextual ao pipeline real ──────────────
    let pool;
    if (G.copyOK.length > 0) {
      pool = [
        `${first ? `${first}, a` : 'A'} AI fez a parte dela. O copy está gerado${G.copyOK.length > 1 ? ` em ${G.copyOK.length} campanhas` : ` — ${G.copyOK[0].titulo}`}. Agora és tu.`,
        `${first ? `${first}: copy` : 'Copy'} pronto. A bola está no teu campo.`,
        `${first || 'Olá'}${first ? ',' : ''} tens copy para aprovar. A Digi AI não vai fazer esse trabalho por ti.`,
      ];
    } else if (G.aprovExec.length > 0) {
      pool = [
        `${first ? `${first}, o` : 'O'} pipeline parou na aprovação. ${nomes(G.aprovExec)} aguarda luz verde.`,
        `${first || 'Ei'}${first ? ',' : ''} ${G.aprovExec.length} ${pl(G.aprovExec.length, 'campanha está', 'campanhas estão')} à espera de aprovação executiva.`,
      ];
    } else if (G.conceitoOK.length > 0) {
      pool = [
        `${first ? `${first}, ` : ''}${G.conceitoOK.length} ${pl(G.conceitoOK.length, 'conceito gerado', 'conceitos gerados')} à tua espera. A Digi AI já pensou — agora tu.`,
        `${first || 'Olá'}${first ? ',' : ''} tens ${G.conceitoOK.length} ${pl(G.conceitoOK.length, 'conceito', 'conceitos')} para rever e aprovar.`,
        `${first ? `${first}: ` : ''}ideia criativa no forno. Aprova e avança.`,
      ];
    } else if (G.semConceito.length > 0) {
      pool = [
        `${first ? `${first}, ` : ''}${G.semConceito.length} ${pl(G.semConceito.length, 'campanha', 'campanhas')} sem conceito. A Digi AI está à espera de ordem de marcha.`,
        `${first || 'Olá'}${first ? ',' : ''} ${nomes(G.semConceito)} não ${pl(G.semConceito.length, 'vai gerar-se sozinha', 'vão gerar-se sozinhas')}.`,
      ];
    } else if (G.emProducao.length > 0 && kpi.total === G.emProducao.length + G.publicadas.length) {
      pool = [
        `${first ? `${first}, ` : ''}pipeline limpo. ${G.emProducao.length + G.publicadas.length} ${pl(G.emProducao.length + G.publicadas.length, 'campanha', 'campanhas')} em produção ou publicada. Raro, mas acontece.`,
        `${first || 'Olá'}${first ? ',' : ''} tudo a correr. Aproveita — amanhã há mais.`,
      ];
    } else if (kpi.total === 0) {
      pool = [
        `${first ? `${first}, ` : ''}pipeline vazio. Aprova um briefing e dá ordem à Digi AI para trabalhar.`,
        `${first || 'Olá'}${first ? ',' : ''} ainda não há campanhas. Começa por criar uma.`,
      ];
    } else {
      const h = hour < 12 ? 'Bom dia' : hour < 19 ? 'Boa tarde' : 'Boa noite';
      pool = [
        `${h}${first ? `, ${first}` : ''}. ${kpi.total} ${pl(kpi.total, 'campanha activa', 'campanhas activas')} no pipeline.`,
        `${first ? `${first}, tens` : 'Tens'} ${kpi.total} ${pl(kpi.total, 'campanha', 'campanhas')} em curso. Vê o que precisa de atenção.`,
      ];
    }
    const heading = pool[Math.floor(seed * pool.length)];

    // ── Parágrafo fluido e humano sobre todo o pipeline ────────────────────────
    const partes = [];
    if (campanhas.length === 0) {
      partes.push(`Ainda não há campanhas activas. Aprova um briefing e dá a ordem à Digi AI para começar a trabalhar.`);
    } else {
      const lista = (arr) => {
        if (!arr.length) return '';
        if (arr.length === 1) return arr[0].titulo;
        if (arr.length === 2) return `${arr[0].titulo} e ${arr[1].titulo}`;
        return `${arr.slice(0,-1).map(c => c.titulo).join(', ')} e ${arr[arr.length-1].titulo}`;
      };
      if (G.copyOK.length)    partes.push(`${lista(G.copyOK)} ${pl(G.copyOK.length,'tem','têm')} copy gerado e ${pl(G.copyOK.length,'está','estão')} à espera que ${pl(G.copyOK.length,'aproves','aprovem')}.`);
      if (G.aprovExec.length) partes.push(`${lista(G.aprovExec)} ${pl(G.aprovExec.length,'está bloqueada','estão bloqueadas')} na aprovação executiva — precisa de luz verde de ${first || 'ti'}, do Rui ou do Armando.`);
      if (G.conceitoOK.length) partes.push(`${lista(G.conceitoOK)} ${pl(G.conceitoOK.length,'tem','têm')} conceito gerado e ${pl(G.conceitoOK.length,'aguarda','aguardam')} a tua aprovação antes de avançar.`);
      if (G.canaisOK.length)  partes.push(`${lista(G.canaisOK)} ${pl(G.canaisOK.length,'tem','têm')} o setup de canais ${pl(G.canaisOK.length,'pronto','prontos')} — aprova para começar a gerar copy.`);
      if (G.idiomasOK.length) partes.push(`${lista(G.idiomasOK)} ${pl(G.idiomasOK.length,'tem','têm')} traduções geradas para rever.`);
      if (G.promptsOK.length) partes.push(`${lista(G.promptsOK)} ${pl(G.promptsOK.length,'tem','têm')} prompts visuais prontos para aprovares.`);
      if (G.semConceito.length) partes.push(`${lista(G.semConceito)} ainda não ${pl(G.semConceito.length,'tem','têm')} conceito — quando quiseres, a Digi AI gera em segundos.`);
      if (G.emProducao.length) partes.push(`${lista(G.emProducao)} ${pl(G.emProducao.length,'está','estão')} em produção${G.emProducao.length === 1 ? ' — a equipa já pode começar' : ''}.`);
      if (G.publicadas.length) partes.push(`${lista(G.publicadas)} já ${pl(G.publicadas.length,'está publicada','estão publicadas')}.`);
    }
    const body = partes.join(' ');
    return { heading, body };
  }, [kpi, campanhas, hour, first, seed]);

  return (
    <div style={{ flexShrink: 0, padding: '10px 0 20px' }}>
      <div style={{ fontSize: 22, fontWeight: 700, color: 'var(--text, #1d2e38)', fontFamily: 'var(--font-display, Montserrat, sans-serif)', marginBottom: 8, letterSpacing: '-0.02em' }}>
        {heading}
      </div>
      <div style={{ fontSize: 13, color: 'var(--text-muted, #64748b)', lineHeight: 1.75 }}>
        {body}
      </div>
    </div>
  );
};

// ── Modal de confirmação de regenerar ─────────────────────────────────────────
const RegenerateConfirmModal = ({ phase, itemCount, approvedCount, hasManualEdits, onConfirm, onClose }) => {
  const PHASE_MSG = {
    conceito:  hasManualEdits
      ? 'Vai apagar o conceito actual (Big Idea, narrativa, ângulos). Atenção: tens edições manuais feitas após a última geração — essas alterações serão perdidas.'
      : 'Vai apagar o conceito actual (Big Idea, narrativa, ângulos). Edições manuais serão perdidas.',
    canais:    'Vai apagar a configuração de canais (audiências, formatos, orçamentos sugeridos).',
    copy:      itemCount ? `Vai apagar ${itemCount} peças de copy${approvedCount ? ` (${approvedCount} aprovadas)` : ''}.` : 'Vai apagar todo o copy gerado.',
    schedule:  'Vai recalcular o calendário editorial com novas datas.',
    idiomas:   itemCount ? `Vai apagar ${itemCount} traduções${approvedCount ? ` (${approvedCount} aprovadas)` : ''}.` : 'Vai apagar todas as traduções.',
    prompts:   itemCount ? `Vai apagar ${itemCount} prompts AI${approvedCount ? ` (${approvedCount} aprovados)` : ''}.` : 'Vai apagar todos os prompts gerados.',
  };
  const msg = PHASE_MSG[phase] || 'Vai regenerar este conteúdo do zero.';
  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: 12, width: '100%', maxWidth: 420, boxShadow: '0 16px 48px rgba(0,0,0,.2)', display: 'flex', flexDirection: 'column' }}>
        <div style={{ padding: '18px 24px 14px', borderBottom: '1px solid #f1f5f9', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <div style={{ fontSize: 14, fontWeight: 700, color: '#1d2e38', fontFamily: 'Montserrat, sans-serif' }}>Confirmar regeneração</div>
          <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#94a3b8', padding: 4, lineHeight: 1, display: 'flex' }}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
          </button>
        </div>
        <div style={{ padding: '16px 24px 20px' }}>
          <div style={{ fontSize: 13, color: '#475569', lineHeight: 1.6, marginBottom: 8 }}>{msg}</div>
          {approvedCount > 0 && (
            <div style={{ fontSize: 12, color: '#d97706', background: 'rgba(217,119,6,.08)', border: '1px solid rgba(217,119,6,.25)', borderRadius: 6, padding: '8px 12px' }}>
              Atenção: {approvedCount} {approvedCount === 1 ? 'item aprovado será perdido' : 'itens aprovados serão perdidos'}.
            </div>
          )}
        </div>
        <div style={{ padding: '0 24px 18px', display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
          <button onClick={onClose} className="btn" style={{ fontSize: 12 }}>Cancelar</button>
          <button onClick={onConfirm} className="btn" style={{ fontSize: 12, background: '#dc2626', color: '#fff', borderColor: '#dc2626' }}>Regenerar</button>
        </div>
      </div>
    </div>
  );
};

// ── Modal de confirmação de eliminação de campanha ─────────────────────────────
const CampDeleteConfirmModal = ({ campanha, onConfirm, onClose }) => {
  const [confirmText, setConfirmText] = React.useState('');
  const canConfirm = confirmText === 'DELETE';
  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24 }}>
      <div style={{ background: '#fff', borderRadius: 14, width: '100%', maxWidth: 480, boxShadow: '0 24px 80px rgba(0,0,0,.2)', display: 'flex', flexDirection: 'column' }}>
        <div style={{ padding: '18px 24px 14px', borderBottom: '1px solid #f1f5f9' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <div style={{ width: 30, height: 30, borderRadius: 8, background: 'rgba(220,38,38,.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#dc2626" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/>
                <path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 011-1h4a1 1 0 011 1v2"/>
              </svg>
            </div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 14, fontWeight: 700, color: '#1d2e38', fontFamily: 'Montserrat, sans-serif' }}>Eliminar campanha</div>
              <div style={{ fontSize: 11, color: '#94a3b8' }}>Esta acção é permanente e não pode ser revertida</div>
            </div>
            <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#94a3b8', padding: 4, lineHeight: 1, display: 'flex' }}>
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
            </button>
          </div>
        </div>
        <div style={{ padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 16 }}>
          <div style={{ borderRadius: 8, background: 'rgba(220,38,38,.04)', border: '1px solid rgba(220,38,38,.15)', padding: '10px 14px' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
              <div style={{ width: 4, height: 4, borderRadius: '50%', background: '#dc2626', flexShrink: 0 }} />
              <span style={{ fontSize: 12, color: '#1d2e38', fontWeight: 500 }}>{campanha?.titulo || `Campanha #${campanha?.id}`}</span>
            </div>
            <div style={{ fontSize: 11, color: '#64748b', lineHeight: 1.5, paddingLeft: 12 }}>
              Vai apagar o conceito, canais, copy, idiomas e prompts desta campanha. O briefing volta ao estado <strong>Aprovado</strong> e pode ser reutilizado.
            </div>
          </div>
          <div>
            <label style={{ fontSize: 11, fontWeight: 600, color: '#64748b', letterSpacing: '0.04em', display: 'block', marginBottom: 6 }}>
              ESCREVE{' '}
              <span style={{ fontFamily: 'monospace', color: '#dc2626', background: 'rgba(220,38,38,.08)', padding: '1px 5px', borderRadius: 3 }}>DELETE</span>
              {' '}PARA CONFIRMAR
            </label>
            <input autoFocus type="text" value={confirmText}
              onChange={e => setConfirmText(e.target.value)}
              placeholder="DELETE"
              onKeyDown={e => e.key === 'Enter' && canConfirm && onConfirm()}
              style={{ width: '100%', boxSizing: 'border-box', padding: '9px 12px', borderRadius: 8, fontSize: 13, border: `1px solid ${canConfirm ? 'rgba(220,38,38,.4)' : '#e2e8f0'}`, outline: 'none', fontFamily: 'Inter, sans-serif', color: '#1d2e38', letterSpacing: '0.05em', transition: 'border-color 0.15s' }}
            />
          </div>
        </div>
        <div style={{ padding: '14px 24px', borderTop: '1px solid #f1f5f9', display: 'flex', gap: 8, justifyContent: 'space-between' }}>
          <button onClick={onClose} className="btn" style={{ fontSize: 12 }}>Cancelar</button>
          <button onClick={onConfirm} disabled={!canConfirm} className="btn"
            style={{ fontSize: 12, background: canConfirm ? '#dc2626' : '#f1f5f9', color: canConfirm ? '#fff' : '#94a3b8', borderColor: canConfirm ? '#dc2626' : '#e2e8f0', cursor: canConfirm ? 'pointer' : 'default', transition: 'all 0.15s' }}>
            Eliminar campanha
          </button>
        </div>
      </div>
    </div>
  );
};

// ── Modal de pedir alterações / rejeitar fase ─────────────────────────────────
const PhaseActionModal = ({ phase, action, onConfirm, onClose }) => {
  const [notes, setNotes] = React.useState('');
  const isReject = action === 'rejected';
  const title = isReject ? 'Rejeitar fase' : 'Pedir alterações';
  const phaseLabel = { conceito: 'Conceito', canais: 'Canais', copy: 'Copy', idiomas: 'Idiomas', prompts: 'Prompts', producao: 'Produção' }[phase] || phase;
  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.45)', zIndex: 9001, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24 }}>
      <div style={{ background: '#fff', borderRadius: 12, width: '100%', maxWidth: 460, boxShadow: '0 16px 48px rgba(0,0,0,.2)', display: 'flex', flexDirection: 'column' }}>
        <div style={{ padding: '18px 24px 14px', borderBottom: '1px solid #f1f5f9', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <div>
            <div style={{ fontSize: 14, fontWeight: 700, color: '#1d2e38', fontFamily: 'Montserrat, sans-serif' }}>{title} — {phaseLabel}</div>
            <div style={{ fontSize: 11, color: '#94a3b8', marginTop: 2 }}>Trabalho não será apagado. Notas ficam visíveis no painel de aprovações.</div>
          </div>
          <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#94a3b8', padding: 4, lineHeight: 1, display: 'flex' }}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
          </button>
        </div>
        <div style={{ padding: '16px 24px 20px' }}>
          <label style={{ fontSize: 11, fontWeight: 600, color: '#64748b', letterSpacing: '0.04em', display: 'block', marginBottom: 6 }}>NOTAS {isReject ? '(motivo de rejeição)' : '(o que precisa de ser corrigido)'} *</label>
          <textarea autoFocus value={notes} onChange={e => setNotes(e.target.value)} rows={4}
            placeholder={isReject ? 'Indica o motivo da rejeição...' : 'Indica as alterações necessárias...'}
            style={{ width: '100%', boxSizing: 'border-box', padding: '9px 12px', borderRadius: 7, fontSize: 13, border: '1px solid #e2e8f0', outline: 'none', resize: 'vertical', fontFamily: 'Inter, sans-serif', lineHeight: 1.6 }} />
        </div>
        <div style={{ padding: '0 24px 18px', display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
          <button onClick={onClose} className="btn" style={{ fontSize: 12 }}>Cancelar</button>
          <button onClick={() => notes.trim() && onConfirm(notes)} disabled={!notes.trim()} className="btn"
            style={{ fontSize: 12, background: isReject ? '#dc2626' : '#d97706', color: '#fff', borderColor: isReject ? '#dc2626' : '#d97706', opacity: notes.trim() ? 1 : 0.5, cursor: notes.trim() ? 'pointer' : 'default' }}>
            {isReject ? 'Rejeitar' : 'Pedir Alterações'}
          </button>
        </div>
      </div>
    </div>
  );
};

// ── CampanhaCard ───────────────────────────────────────────────────────────────
const NEXT_ACTION_BY_STATUS = {
  conceito_pendente:  'Gerar conceito',
  conceito_gerado:    'Rever conceito',
  conceito:           'Rever conceito',
  canais_pendente:    'Gerar copy',
  canais_gerado:      'Aprovar copy',
  canais_aprovado:    'Gerar idiomas',
  copy_pendente:      'Gerar copy',
  copy_gerado:        'Aprovar copy',
  copy_aprovado:      'Gerar idiomas',
  idiomas_pendente:   'Gerar idiomas',
  idiomas_gerado:     'Aprovar idiomas',
  idiomas_aprovado:   'Gerar prompts',
  prompts_pendente:   'Gerar prompts',
  prompts_gerado:     'Aprovar prompts',
  geracao:            'Gerar criativos',
  aprovacao:          'Aprovar criativos',
  prompts_aprovado:   'Aguarda aprovação',
  pending_executive:  'Aguarda aprovação',
  em_producao:        'Publicar',
  publicado:          'Publicado',
};

const PROGRESS_BY_STATUS = {
  copy_pendente:      { key: 'copy',    label: 'Copy' },
  copy_gerado:        { key: 'copy',    label: 'Copy' },
  copy_aprovado:      { key: 'copy',    label: 'Copy' },
  canais_pendente:    { key: 'copy',    label: 'Copy' },
  canais_gerado:      { key: 'copy',    label: 'Copy' },
  canais_aprovado:    { key: 'copy',    label: 'Copy' },
  idiomas_pendente:   { key: 'idiomas', label: 'Idiomas' },
  idiomas_gerado:     { key: 'idiomas', label: 'Idiomas' },
  idiomas_aprovado:   { key: 'idiomas', label: 'Idiomas' },
  prompts_pendente:   { key: 'prompts', label: 'Prompts' },
  prompts_gerado:     { key: 'prompts', label: 'Prompts' },
  geracao:            { key: 'prompts', label: 'Criativos' },
  aprovacao:          { key: 'prompts', label: 'Criativos' },
  prompts_aprovado:   { key: 'prompts', label: 'Prompts' },
  pending_executive:  { key: 'prompts', label: 'Prompts' },
  em_producao:        { key: 'prompts', label: 'Prompts' },
  publicado:          { key: 'prompts', label: 'Prompts' },
};

const CHAN_ABBR = {
  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',
  instagram: 'Instagram', facebook: 'Facebook', linkedin: 'LinkedIn', google_ads: 'Google Ads',
  site: 'Blog', led: 'LED', tiktok: 'TikTok', youtube: 'YouTube', social: 'Social',
  ads: 'Ads',
};

// SVG icons monochromos 14x14 stroke, sem emojis
const CampanhaChanIcon = ({ ch, size = 14, color = '#64748b' }) => {
  const p = { fill: 'none', stroke: 'currentColor', strokeWidth: 1.6, strokeLinecap: 'round', strokeLinejoin: 'round' };
  const paths = {
    meta_ads:            <g {...p}><path d="M3 11l18-4v10L3 13v-2z"/><path d="M7 12v6"/></g>,
    facebook:            <g {...p}><path d="M3 11l18-4v10L3 13v-2z"/><path d="M7 12v6"/></g>,
    ads:                 <g {...p}><path d="M3 11l18-4v10L3 13v-2z"/><path d="M7 12v6"/></g>,
    linkedin_ads:        <g {...p}><path d="M6 9v9"/><circle cx="6" cy="5.5" r="1"/><path d="M10 18v-5a2 2 0 014 0v5"/><path d="M18 18v-4"/></g>,
    linkedin:            <g {...p}><path d="M6 9v9"/><circle cx="6" cy="5.5" r="1"/><path d="M10 18v-5a2 2 0 014 0v5"/><path d="M18 18v-4"/></g>,
    google_ads_search:   <g {...p}><circle cx="11" cy="11" r="6"/><path d="M20 20l-4-4"/></g>,
    google_ads:          <g {...p}><circle cx="11" cy="11" r="6"/><path d="M20 20l-4-4"/></g>,
    google_ads_display:  <g {...p}><rect x="3" y="4" width="18" height="14" rx="2"/><path d="M10 9l5 3-5 3z"/></g>,
    email:               <g {...p}><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 7l9 6 9-6"/></g>,
    whatsapp:            <g {...p}><path d="M12 2a10 10 0 00-8.5 15.2L2 22l4.9-1.3A10 10 0 1012 2z"/></g>,
    website:             <g {...p}><circle cx="12" cy="12" r="9"/><path d="M3 12h18"/><path d="M12 3a15 15 0 010 18"/></g>,
    site:                <g {...p}><circle cx="12" cy="12" r="9"/><path d="M3 12h18"/><path d="M12 3a15 15 0 010 18"/></g>,
    muppi_led:           <g {...p}><rect x="2" y="4" width="20" height="14" rx="2"/><path d="M9 22h6"/><path d="M12 18v4"/></g>,
    led:                 <g {...p}><rect x="2" y="4" width="20" height="14" rx="2"/><path d="M9 22h6"/><path d="M12 18v4"/></g>,
    instagram:           <g {...p}><rect x="3" y="3" width="18" height="18" rx="4"/><circle cx="12" cy="12" r="4"/></g>,
    youtube:             <g {...p}><rect x="2" y="6" width="20" height="12" rx="3"/><path d="M10 10l5 2-5 2z"/></g>,
    tiktok:              <g {...p}><path d="M9 3v12a3 3 0 11-3-3"/><path d="M9 3a5 5 0 005 5"/></g>,
    social:              <g {...p}><circle cx="12" cy="12" r="9"/><path d="M8 12h8M12 8v8"/></g>,
  };
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" style={{ color, flexShrink: 0 }}>
      {paths[ch] || <circle cx="12" cy="12" r="2" {...p} />}
    </svg>
  );
};

const _firstNameFromEmail = (email) => {
  if (!email) return null;
  const local = String(email).split('@')[0].split('.')[0];
  if (!local) return null;
  return local.charAt(0).toUpperCase() + local.slice(1);
};

const CampanhaCard = ({ c, onClick, onEdit, onDelete, cardIndex, userEmail }) => {
  const [hover,    setHover]    = React.useState(false);
  const [menuOpen, setMenuOpen] = React.useState(false);
  const menuRef    = React.useRef(null);
  const [campUsage, setCampUsage] = React.useState(null);
  React.useEffect(() => {
    if (!c?.id) return;
    fetch(`/api/marketing/campanhas/${c.id}/usage`)
      .then(r => r.ok ? r.json() : null)
      .then(data => { if (data) setCampUsage(data); })
      .catch(() => {});
  }, [c?.id]);
  const bCol     = _brandColor(c.brand_slug);
  const channels = Array.isArray(c.channels) ? c.channels : [];

  // Progresso global — sempre 3 fases (Copy / Idiomas / Prompts)
  const stages = [
    { label: 'Copy',    done: c.num_copy_aprovado    || 0, total: c.num_copy    || 0, color: '#f59e0b' },
    { label: 'Idiomas', done: c.num_idiomas_aprovado || 0, total: c.num_idiomas || 0, color: '#8b5cf6' },
    { label: 'Prompts', done: c.num_prompts_aprovado || 0, total: c.num_prompts || 0, color: '#ec4899' },
  ];

  const nextAction = NEXT_ACTION_BY_STATUS[c.status] || null;
  const ownerName  = _firstNameFromEmail(userEmail);

  React.useEffect(() => {
    if (!menuOpen) return;
    const h = (e) => { if (menuRef.current && !menuRef.current.contains(e.target)) { setMenuOpen(false); } };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, [menuOpen]);

  return (
    <div
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      style={{
        background: '#ffffff',
        borderTop: '1px solid #ECEFF5',
        borderRight: '1px solid #ECEFF5',
        borderBottom: '1px solid #ECEFF5',
        borderLeft: `3px solid ${bCol}`, borderRadius: 8, padding: '12px 14px',
        boxShadow: hover ? '0 4px 12px rgba(0,0,0,0.08)' : '0 1px 3px rgba(0,0,0,0.04)',
        transition: 'box-shadow 150ms ease, transform 150ms ease', cursor: 'pointer', position: 'relative',
        transform: hover ? 'translateY(-1px)' : 'translateY(0)',
      }}
    >
      {/* Header: Marca + Kebab */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
        <span style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: bCol, fontFamily: 'var(--font-mono, monospace)' }}>
          {c.brand_name}
        </span>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }} onClick={e => e.stopPropagation()}>
          <div ref={menuRef} style={{ position: 'relative' }}>
            <button
              onClick={() => setMenuOpen(o => !o)}
              style={{
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                width: 22, height: 22, borderRadius: 6, cursor: 'pointer',
                background: menuOpen ? 'rgba(0,0,0,0.06)' : 'transparent',
                border: 'none', color: '#94A4C4', fontSize: 15, lineHeight: 1,
                transition: 'background 120ms ease',
              }}
              onMouseEnter={e => { if (!menuOpen) e.currentTarget.style.background = 'rgba(0,0,0,0.06)'; }}
              onMouseLeave={e => { if (!menuOpen) e.currentTarget.style.background = 'transparent'; }}
            >⋮</button>
            {menuOpen && (
              <div className="animate-in" style={{
                position: 'absolute', top: 'calc(100% + 4px)', right: 0, zIndex: 9999,
                background: '#ffffff', border: '1px solid #ECEFF5',
                borderRadius: 8, minWidth: 160, padding: 4,
                boxShadow: '0 4px 16px rgba(17,41,84,0.10)',
              }}>
                <button onClick={() => { setMenuOpen(false); onEdit(c); }}
                  style={{ display: 'block', width: '100%', textAlign: 'left', background: 'transparent', border: 'none', padding: '8px 12px', borderRadius: 4, fontSize: 13, color: '#283252', cursor: 'pointer', fontFamily: 'inherit' }}
                  onMouseEnter={e => e.currentTarget.style.background = 'rgba(0,0,0,0.04)'}
                  onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
                >Editar título</button>
                <div style={{ height: 1, background: '#ECEFF5', margin: '4px 0' }} />
                <button onClick={() => { setMenuOpen(false); onDelete(c); }}
                  style={{ display: 'block', width: '100%', textAlign: 'left', background: 'transparent', border: 'none', padding: '8px 12px', borderRadius: 4, fontSize: 13, color: '#DC2626', cursor: 'pointer', fontFamily: 'inherit' }}
                  onMouseEnter={e => e.currentTarget.style.background = 'rgba(220,38,38,0.06)'}
                  onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
                >Apagar</button>
              </div>
            )}
          </div>
        </div>
      </div>

      {/* Content — click area */}
      <div onClick={onClick}>

        {/* Título */}
        <div style={{ fontSize: 13, fontWeight: 700, color: '#1d2e38', fontFamily: 'var(--font-display, Montserrat, sans-serif)', lineHeight: 1.35, marginBottom: 10, overflow: 'hidden', display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical' }}>
          {c.titulo}
        </div>

        {/* Canais (SVG icons) */}
        {channels.length > 0 && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 10 }}>
            {channels.slice(0, 6).map(ch => (
              <span key={ch} title={CHAN_ABBR[ch] || ch} style={{ display: 'inline-flex' }}>
                <CampanhaChanIcon ch={ch} size={13} color="#64748b" />
              </span>
            ))}
            {channels.length > 6 && (
              <span style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'var(--font-mono, monospace)' }}>+{channels.length - 6}</span>
            )}
          </div>
        )}

        {/* Progresso — 3 fases sempre visíveis */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 5, marginBottom: 10 }}>
          {stages.map(s => {
            const complete = s.total > 0 && s.done >= s.total;
            const empty    = s.total === 0;
            const pct      = s.total > 0 ? Math.round((s.done / s.total) * 100) : 0;
            const barCol   = complete ? '#22c55e' : (empty ? '#e2e8f0' : s.color);
            const txtCol   = empty ? '#cbd5e1' : (complete ? '#22c55e' : '#64748b');
            return (
              <div key={s.label}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 2 }}>
                  <span style={{ fontSize: 9, color: txtCol, fontFamily: 'var(--font-mono, monospace)', letterSpacing: '0.06em', textTransform: 'uppercase', fontWeight: 600 }}>{s.label}</span>
                  <span style={{ fontSize: 9, fontWeight: 700, color: txtCol, fontFamily: 'var(--font-mono, monospace)' }}>
                    {empty ? '—' : `${s.done}/${s.total}`}
                    {complete && <span style={{ marginLeft: 4 }}>✓</span>}
                  </span>
                </div>
                <div style={{ height: 3, borderRadius: 2, background: 'rgba(0,0,0,.05)', overflow: 'hidden' }}>
                  <div style={{ height: '100%', width: `${pct}%`, background: barCol, borderRadius: 2, transition: 'width .4s ease' }} />
                </div>
              </div>
            );
          })}
        </div>

        {/* Footer: próxima acção · owner */}
        {(nextAction || ownerName) && (
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 6, borderTop: '1px solid #f1f5f9', paddingTop: 8, fontSize: 10.5, color: '#475569', fontFamily: 'var(--font-body, Inter, sans-serif)', lineHeight: 1.3, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
            {nextAction && (
              <>
                <span style={{ color: bCol, fontWeight: 700 }}>→</span>
                <span style={{ fontWeight: 600 }}>{nextAction}</span>
              </>
            )}
            {ownerName && <span style={{ color: '#94a3b8' }}>· {ownerName}</span>}
          </div>
        )}
      </div>

      {/* Custos/tokens no hover */}
      {hover && campUsage && (campUsage.total_custo > 0 || campUsage.total_tokens > 0) && (
        <div style={{ position: 'absolute', bottom: 2, right: 8, fontSize: 8.5, color: '#94a3b8', fontFamily: 'var(--font-mono, monospace)', background: 'rgba(255,255,255,0.95)', padding: '1px 6px', borderRadius: 4, pointerEvents: 'none' }}>
          €{parseFloat(campUsage.total_custo || 0).toFixed(4).replace(/0+$/, '').replace(/\.$/, '') || '0'}
          {campUsage.total_tokens > 0 && <> · {Number(campUsage.total_tokens).toLocaleString('pt-PT')} tok</>}
        </div>
      )}
    </div>
  );
};

// ── KanbanColumn ───────────────────────────────────────────────────────────────
const KanbanColumn = ({ col, cards, onCardClick, onEdit, onDelete, userEmail }) => {
  return (
    <div style={{ flex: '1 1 0', display: 'flex', flexDirection: 'column', gap: 0, minWidth: 200 }}>
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 8, borderRadius: 10, padding: 4, minHeight: 80 }}>
        {cards.map((c, i) => (
          <CampanhaCard key={c.id} c={c} onClick={() => onCardClick(c)} onEdit={onEdit} onDelete={onDelete} cardIndex={i} userEmail={userEmail} />
        ))}
        {cards.length === 0 && (
          <div style={{ height: 40, borderRadius: 8, border: '1px dashed var(--border, #e2e8f0)', opacity: 0.4 }} />
        )}
      </div>
    </div>
  );
};

// ── Tabs do drawer antigo (não usadas — mantidas para referência) ──────────────
const TabConceito = ({ campanha, onAction, generating }) => {
  const hasConceito = !!(campanha.big_idea);
  const isApproved  = ['canais_pendente','canais_gerado','canais_aprovado','copy_pendente','copy_gerado','copy_aprovado','prompts_pendente','prompts_gerado','em_producao'].includes(campanha.status);

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      {!hasConceito ? (
        <div style={{ textAlign: 'center', padding: '32px 0' }}>
          <div style={{ fontSize: 13, color: 'var(--text-muted, #64748b)', marginBottom: 20 }}>
            Nenhum conceito gerado ainda. A Digi AI vai analisar os 5 blocos do briefing e criar o conceito estratégico da campanha.
          </div>
          <button onClick={() => onAction('generateConceito')} disabled={generating.conceito}
            className="btn btn-ai" style={{ gap: 6 }}>
            {generating.conceito ? 'A gerar…' : 'Gerar Conceito com Digi AI'}
          </button>
        </div>
      ) : (
        <>
          {[
            { key: 'big_idea',       label: 'Big Idea'        },
            { key: 'posicionamento', label: 'Posicionamento'  },
            { key: 'narrativa',      label: 'Narrativa'       },
            { key: 'tom_campanha',   label: 'Tom da Campanha' },
          ].map(f => campanha[f.key] ? (
            <div key={f.key} style={{ background: 'var(--bg-sunken, #f1f5f9)', borderRadius: 8, padding: '14px 16px' }}>
              <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--text-dim, #475569)', fontFamily: 'var(--font-mono, monospace)', marginBottom: 8 }}>
                {f.label}
              </div>
              <div style={{ fontSize: 13, color: 'var(--text, #1e293b)', lineHeight: 1.6 }}>{campanha[f.key]}</div>
            </div>
          ) : null)}
          {!isApproved && (
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
              <button onClick={() => onAction('aprovarConceito')} disabled={generating.approve}
                className="btn btn-ai" style={{ gap: 6 }} data-tutorial-step="approve_conceito_canais">
                ✓ Aprovar Conceito
              </button>
              <button onClick={() => onAction('generateConceito')} disabled={generating.conceito}
                className="btn" style={{ color: 'var(--text-muted, #64748b)' }}>
                {generating.conceito ? 'A gerar…' : 'Regenerar'}
              </button>
            </div>
          )}
          {isApproved && (
            <div style={{ fontSize: 11, color: 'var(--success, #22c55e)', fontWeight: 500 }}>✓ Conceito aprovado — a avançar para copy</div>
          )}
        </>
      )}
    </div>
  );
};

// ── Drawer: tab Copy ───────────────────────────────────────────────────────────
const TabCopy = ({ campanha, copy, onAction, generating }) => {
  const channels  = Array.isArray(campanha.briefing?.channels) ? campanha.briefing.channels : [];
  const hasCopy   = copy.length > 0;
  const canGenerate = ['copy_pendente', 'copy_gerado', 'copy_aprovado', 'prompts_pendente', 'canais_pendente', 'canais_gerado', 'canais_aprovado'].includes(campanha.status)
    || ['conceito_gerado'].includes(campanha.status)
    || campanha.big_idea;

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      {!canGenerate && (
        <div style={{ padding: '12px 16px', background: 'rgba(245,158,11,.08)', borderRadius: 8, fontSize: 12, color: '#f59e0b', border: '1px solid rgba(245,158,11,.2)' }}>
          Aprova o Conceito antes de gerar copy.
        </div>
      )}
      {channels.length === 0 && (
        <div style={{ padding: '12px 16px', background: 'rgba(148,163,184,.06)', borderRadius: 8, fontSize: 12, color: 'var(--text-muted, #64748b)' }}>
          Canais não definidos no Bloco 4 do briefing.
        </div>
      )}
      {!hasCopy && channels.length > 0 && canGenerate && (
        <div style={{ textAlign: 'center', padding: '24px 0' }}>
          <div style={{ fontSize: 13, color: 'var(--text-muted, #64748b)', marginBottom: 16 }}>
            A Digi AI vai gerar copy específico para cada canal: {channels.map(c => CANAL_LABEL[c] || c).join(', ')}.
          </div>
          <button onClick={() => onAction('generateCopy')} disabled={generating.copy}
            className="btn btn-ai">
            {generating.copy ? 'A gerar…' : 'Gerar Copy para todos os canais'}
          </button>
        </div>
      )}
      {hasCopy && (
        <>
          {copy.map(cp => (
            <CopyCard key={cp.id} cp={cp} onApprove={() => onAction('approveCopy', cp.id, 'aprovado')} onFlag={() => onAction('approveCopy', cp.id, 'correcao')} />
          ))}
          <button onClick={() => onAction('generateCopy')} disabled={generating.copy}
            className="btn" style={{ alignSelf: 'flex-start', color: 'var(--text-muted, #64748b)', fontSize: 12 }}>
            {generating.copy ? 'A gerar…' : 'Regenerar todo o copy'}
          </button>
        </>
      )}
    </div>
  );
};

const CopyCard = ({ cp, onApprove, onFlag }) => {
  const [exp, setExp] = React.useState(false);
  const canalLabel = CANAL_LABEL[cp.canal] || cp.canal || '—';
  const isApproved = cp.status === 'aprovado';
  const isFlagged  = cp.status === 'correcao';

  return (
    <div style={{
      background: 'var(--bg-sunken, #f1f5f9)', borderRadius: 8,
      border: `1px solid ${isApproved ? 'rgba(34,197,94,.25)' : isFlagged ? 'rgba(251,146,60,.25)' : 'var(--border, #e2e8f0)'}`,
    }}>
      <div onClick={() => setExp(e => !e)} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 16px', cursor: 'pointer', userSelect: 'none' }}>
        <CanalSvgIcon canal={cp.canal} size={16} />
        <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--text, #1e293b)', flex: 1, fontFamily: 'var(--font-display, Montserrat, sans-serif)' }}>{canalLabel}</span>
        {cp.formato && <span style={{ fontSize: 10, color: 'var(--text-dim, #475569)', fontFamily: 'var(--font-mono, monospace)' }}>{cp.formato}</span>}
        <CampStatusBadge status={cp.status} />
        <span style={{ fontSize: 10, color: 'var(--text-dim, #475569)', marginLeft: 4 }}>{exp ? '▴' : '▾'}</span>
      </div>
      {exp && (
        <div style={{ padding: '0 16px 14px', display: 'flex', flexDirection: 'column', gap: 10, borderTop: '1px solid var(--border, #e2e8f0)' }}>
          {cp.headline && <CopyField label="Headline" value={cp.headline} />}
          {cp.body     && <CopyField label="Body"     value={cp.body} multiline />}
          {cp.cta      && <CopyField label="CTA"      value={cp.cta} />}
          {cp.hashtags && <CopyField label="Hashtags" value={cp.hashtags} mono />}
          {!isApproved && (
            <div style={{ display: 'flex', gap: 8, marginTop: 4 }}>
              <button onClick={onApprove} className="btn btn-ai" style={{ fontSize: 11 }}>✓ Aprovar</button>
              <button onClick={onFlag}    className="btn" style={{ fontSize: 11, color: '#fb923c', borderColor: 'rgba(251,146,60,.3)' }}>⚑ Corrigir</button>
            </div>
          )}
          {isApproved && <div style={{ fontSize: 11, color: 'var(--success, #22c55e)', fontWeight: 500 }}>✓ Copy aprovado</div>}
        </div>
      )}
    </div>
  );
};

const CopyField = ({ label, value, multiline, mono }) => (
  <div>
    <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--text-dim, #475569)', fontFamily: 'var(--font-mono, monospace)', marginBottom: 4 }}>{label}</div>
    <div style={{ fontSize: 12.5, color: 'var(--text, #1e293b)', lineHeight: multiline ? 1.6 : 1.3, fontFamily: mono ? 'var(--font-mono, monospace)' : 'inherit', whiteSpace: multiline ? 'pre-wrap' : 'normal', wordBreak: 'break-word' }}>{value}</div>
  </div>
);

const CopyFieldLight = ({ label, value, multiline, mono }) => (
  <div>
    <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#94a3b8', fontFamily: 'monospace', marginBottom: 4 }}>{label}</div>
    <div style={{ fontSize: 12.5, color: '#1d2e38', lineHeight: multiline ? 1.6 : 1.3, fontFamily: mono ? 'monospace' : 'inherit', whiteSpace: multiline ? 'pre-wrap' : 'normal', wordBreak: 'break-word' }}>{value}</div>
  </div>
);

const AdSpecField = ({ label, value, limit, light }) => {
  if (!value) return null;
  const len = (value || '').length;
  const over = limit && len > limit;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: light ? '#94a3b8' : 'var(--text-dim,#475569)', fontFamily: 'monospace' }}>{label}</span>
        {limit && <span style={{ fontSize: 9, fontFamily: 'monospace', color: over ? '#ef4444' : '#64748b', marginLeft: 'auto' }}>{len}/{limit}</span>}
      </div>
      <div style={{ fontSize: 12, color: over ? '#ef4444' : (light ? '#1d2e38' : 'var(--text,#f1f5f9)'), lineHeight: 1.4, wordBreak: 'break-word' }}>{value}</div>
    </div>
  );
};

const AD_SPEC_LIMITS = {
  instagram: { primary_text: 125, headline: 40, description: 30 },
  facebook:  { primary_text: 125, headline: 40, description: 30 },
  ads:       { primary_text: 125, headline: 40, description: 30 },
  google_ads:{ headline_1: 30, headline_2: 30, headline_3: 30, description_1: 90, description_2: 90 },
  linkedin:  { intro: 150, headline: 70 },
};

const AdSpecsBlock = ({ specs, canal, light }) => {
  const [open, setOpen] = React.useState(false);
  if (!specs || !Object.keys(specs).length) return null;
  const limits = AD_SPEC_LIMITS[canal] || {};
  const isGoogle = canal === 'google_ads';
  return (
    <div style={{ borderTop: `1px solid ${light ? '#f1f5f9' : 'var(--border,#1e293b)'}`, paddingTop: 10, marginTop: 4 }}>
      <button onClick={() => setOpen(o => !o)} style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 6, color: '#3859D0', fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', fontFamily: 'monospace' }}>
        📐 Ad Specs {open ? '▲' : '▼'}
      </button>
      {open && (
        <div style={{ marginTop: 10, display: 'flex', flexDirection: 'column', gap: 8, background: 'rgba(56,89,208,.05)', borderRadius: 6, padding: '10px 12px' }}>
          {isGoogle ? (
            <>
              {[1,2,3].map(n => specs[`headline_${n}`] && <AdSpecField key={`h${n}`} label={`Headline ${n}`} value={specs[`headline_${n}`]} limit={limits[`headline_${n}`]} light={light} />)}
              {[1,2].map(n => specs[`description_${n}`] && <AdSpecField key={`d${n}`} label={`Description ${n}`} value={specs[`description_${n}`]} limit={limits[`description_${n}`]} light={light} />)}
            </>
          ) : (
            <>
              {specs.primary_text  && <AdSpecField label="Primary Text"  value={specs.primary_text}  limit={limits.primary_text}  light={light} />}
              {specs.intro         && <AdSpecField label="Intro Text"     value={specs.intro}          limit={limits.intro}         light={light} />}
              {specs.headline      && <AdSpecField label="Headline"       value={specs.headline}       limit={limits.headline}      light={light} />}
              {specs.description   && <AdSpecField label="Description"    value={specs.description}    limit={limits.description}   light={light} />}
            </>
          )}
        </div>
      )}
    </div>
  );
};

// ── Drawer: tab Prompts ────────────────────────────────────────────────────────
const TabPrompts = ({ campanha, prompts, onAction, generating }) => {
  const canGenerate = campanha.big_idea && (campanha.status !== 'conceito_pendente' && campanha.status !== 'conceito_gerado');
  const hasPrompts  = prompts.length > 0;

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      {!canGenerate && (
        <div style={{ padding: '12px 16px', background: 'rgba(168,85,247,.06)', borderRadius: 8, fontSize: 12, color: '#a855f7', border: '1px solid rgba(168,85,247,.2)' }}>
          Aprova o Conceito e o Copy antes de gerar prompts visuais.
        </div>
      )}
      {!hasPrompts && canGenerate && (
        <div style={{ textAlign: 'center', padding: '24px 0' }}>
          <div style={{ fontSize: 13, color: 'var(--text-muted, #64748b)', marginBottom: 16 }}>
            A Digi AI vai criar prompts de imagem e vídeo para todos os canais visuais da campanha (Instagram, Ads, LinkedIn, Site…).
          </div>
          <button onClick={() => onAction('generatePrompts')} disabled={generating.prompts}
            className="btn btn-ai">
            {generating.prompts ? 'A gerar…' : 'Gerar Prompts Visuais'}
          </button>
        </div>
      )}
      {hasPrompts && (
        <>
          {prompts.map(pr => (
            <PromptCard key={pr.id} pr={pr} onApprove={() => onAction('approvePrompt', pr.id, 'aprovado')} onFlag={() => onAction('approvePrompt', pr.id, 'correcao')} />
          ))}
          <button onClick={() => onAction('generatePrompts')} disabled={generating.prompts}
            className="btn" style={{ alignSelf: 'flex-start', color: 'var(--text-muted, #64748b)', fontSize: 12 }}>
            {generating.prompts ? 'A gerar…' : 'Regenerar prompts'}
          </button>
        </>
      )}
    </div>
  );
};

const PromptCard = ({ pr, onApprove, onFlag }) => {
  const [exp, setExp] = React.useState(false);
  const canalLabel = CANAL_LABEL[pr.canal] || pr.canal || '—';
  const tipoLabel  = pr.tipo === 'imagem' ? 'Imagem' : 'Vídeo';
  const isApproved = pr.status === 'aprovado';
  const isFlagged  = pr.status === 'correcao';

  return (
    <div style={{
      background: 'var(--bg-sunken, #f1f5f9)', borderRadius: 8,
      border: `1px solid ${isApproved ? 'rgba(34,197,94,.25)' : isFlagged ? 'rgba(251,146,60,.25)' : 'var(--border, #e2e8f0)'}`,
    }}>
      <div onClick={() => setExp(e => !e)} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 16px', cursor: 'pointer', userSelect: 'none' }}>
        <CanalSvgIcon canal={pr.canal} size={16} />
        <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--text, #1e293b)', flex: 1, fontFamily: 'var(--font-display, Montserrat, sans-serif)' }}>{canalLabel}</span>
        <span style={{ fontSize: 10, color: 'var(--text-dim, #475569)', fontFamily: 'var(--font-mono, monospace)' }}>{tipoLabel}</span>
        {pr.ratio && <span style={{ fontSize: 10, color: 'var(--text-dim, #475569)', fontFamily: 'var(--font-mono, monospace)' }}>{pr.ratio}</span>}
        <CampStatusBadge status={pr.status} />
        <span style={{ fontSize: 10, color: 'var(--text-dim, #475569)', marginLeft: 4 }}>{exp ? '▴' : '▾'}</span>
      </div>
      {exp && (
        <div style={{ padding: '0 16px 14px', display: 'flex', flexDirection: 'column', gap: 10, borderTop: '1px solid var(--border, #e2e8f0)' }}>
          {pr.prompt_texto && <CopyField label="Prompt" value={pr.prompt_texto} multiline />}
          {pr.estilo       && <CopyField label="Estilo"  value={pr.estilo} />}
          {pr.referencia   && <CopyField label="Referência" value={pr.referencia} />}
          {!isApproved && (
            <div style={{ display: 'flex', gap: 8, marginTop: 4 }}>
              <button onClick={onApprove} className="btn btn-ai" style={{ fontSize: 11 }}>✓ Aprovar</button>
              <button onClick={onFlag}    className="btn" style={{ fontSize: 11, color: '#fb923c', borderColor: 'rgba(251,146,60,.3)' }}>⚑ Corrigir</button>
            </div>
          )}
          {isApproved && <div style={{ fontSize: 11, color: 'var(--success, #22c55e)', fontWeight: 500 }}>✓ Prompt aprovado</div>}
        </div>
      )}
    </div>
  );
};


// ── WorkflowTracker (vertical sidebar) ────────────────────────────────────────
const WorkflowTracker = ({ detail }) => {
  if (!detail) return null;
  const copyItems      = detail.copy    || [];
  const promptItems    = detail.prompts || [];
  const copyApproved   = copyItems.filter(c => c.status === 'aprovado').length;
  const promptApproved = promptItems.filter(p => p.status === 'aprovado').length;
  const proposta       = detail.proposta_json || {};
  const nAngles        = proposta.messaging_angles?.length || 0;

  const steps = [
    { num: 1, label: 'Conceito',
      done:    !!detail.big_idea,
      partial: false,
      sub:     detail.big_idea ? (nAngles > 0 ? `v3 · ${nAngles} ângulos` : 'v1 básico') : null },
    { num: 2, label: 'Copy',
      done:    copyApproved > 0 && copyApproved === copyItems.length && copyItems.length > 0,
      partial: copyItems.length > 0 && copyApproved < copyItems.length,
      sub:     copyItems.length ? `${copyApproved}/${copyItems.length} aprovadas` : (detail.big_idea ? 'por gerar' : null) },
    { num: 3, label: 'Prompts Visuais',
      done:    promptApproved > 0 && promptApproved === promptItems.length && promptItems.length > 0,
      partial: promptItems.length > 0 && promptApproved < promptItems.length,
      sub:     promptItems.length ? `${promptApproved}/${promptItems.length} aprovados` : null },
    { num: 4, label: 'Produção',
      done:    detail.status === 'em_producao',
      partial: false,
      sub:     detail.status === 'em_producao' ? 'publicado' : null },
  ];

  return (
    <div style={{ background: '#ffffff', border: '1px solid var(--border, #ECEFF5)', borderRadius: 10, overflow: 'hidden' }}>
      <div style={{ padding: '12px 16px', borderBottom: '1px solid var(--border, #ECEFF5)' }}>
        <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--text, #283252)', fontFamily: 'var(--font-display, Montserrat, sans-serif)' }}>Estado do Workflow</div>
      </div>
      <div style={{ padding: '12px 16px', display: 'flex', flexDirection: 'column', gap: 0 }}>
        {steps.map((step, i) => {
          const isDone    = step.done;
          const isPartial = step.partial;
          const dotBg     = isDone ? 'var(--ai-500, #3859D0)' : isPartial ? '#fbbf24' : 'var(--border, #ECEFF5)';
          const dotTxt    = isDone || isPartial ? '#fff' : 'var(--text-muted, #94A4C4)';
          return (
            <React.Fragment key={step.label}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <div style={{ width: 26, height: 26, borderRadius: '50%', background: dotBg, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                  {isDone
                    ? <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
                    : <span style={{ fontSize: 10, fontWeight: 700, color: dotTxt, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{step.num}</span>
                  }
                </div>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 12.5, fontWeight: 600, color: isDone || isPartial ? 'var(--text, #283252)' : 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)', lineHeight: 1.2 }}>{step.label}</div>
                  {step.sub && <div style={{ fontSize: 10.5, color: isDone ? 'var(--ai-500, #3859D0)' : isPartial ? '#D97706' : 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)', marginTop: 1 }}>{step.sub}</div>}
                </div>
              </div>
              {i < steps.length - 1 && <div style={{ width: 2, height: 10, background: isDone ? 'var(--ai-500, #3859D0)' : 'var(--border, #ECEFF5)', borderRadius: 1, marginLeft: 12, marginTop: 2, marginBottom: 2 }} />}
            </React.Fragment>
          );
        })}
      </div>
    </div>
  );
};

// ── DigiLivePanel ──────────────────────────────────────────────────────────────
const DigiLivePanel = ({ detail }) => {
  const copyItems   = detail?.copy    || [];
  const promptItems = detail?.prompts || [];
  const proposta    = detail?.proposta_json || {};

  const items = [];
  if (detail?.big_idea) {
    const nAngles = proposta.messaging_angles?.length || 0;
    if (nAngles > 0) {
      items.push({ color: 'green', time: 'Conceito v3', msg: `${nAngles} ângulos · plano ${proposta.comm_plan?.length ? 'S-2→S+2' : 'gerado'}` });
    } else {
      items.push({ color: 'amber', time: 'Conceito v1', msg: 'Estratégia básica — recomenda-se regenerar' });
    }
  }
  copyItems.slice(0, 3).forEach(cp => {
    const done = cp.status === 'aprovado';
    const hl = cp.headline ? ` · "${cp.headline.slice(0, 38)}${cp.headline.length > 38 ? '…' : ''}"` : '';
    items.push({ color: done ? 'green' : 'amber', time: CANAL_LABEL[cp.canal] || cp.canal, msg: `Copy ${done ? 'aprovado' : 'aguarda aprovação'}${hl}` });
  });
  promptItems.slice(0, 2).forEach(pr => {
    const done = pr.status === 'aprovado';
    items.push({ color: done ? 'green' : 'grey', time: CANAL_LABEL[pr.canal] || pr.canal, msg: `Prompt visual ${done ? 'aprovado' : 'gerado'}${pr.ratio ? ' · ' + pr.ratio : ''}` });
  });
  if (items.length === 0) {
    items.push({ color: 'grey', time: 'aguarda', msg: 'Gera o conceito para começar o workflow' });
  }

  const dotColors = { green: '#00A86B', amber: '#D97706', red: '#CF2E2E', grey: '#94A4C4' };

  return (
    <div style={{ background: '#ffffff', border: '1px solid var(--border, #ECEFF5)', borderRadius: 10, overflow: 'hidden' }}>
      <div style={{ padding: '14px 16px 12px', borderBottom: '1px solid var(--border, #ECEFF5)' }}>
        <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, background: '#FFF7ED', border: '1px solid #FED7AA', borderRadius: 20, padding: '3px 10px', fontSize: 10, fontWeight: 700, color: '#C2410C', letterSpacing: '0.04em', marginBottom: 8 }}>
          <span style={{ width: 6, height: 6, borderRadius: '50%', background: '#EF4444', display: 'inline-block', animation: 'none' }} />
          DIGI · LIVE
        </div>
        <div style={{ fontSize: 12.5, fontWeight: 700, color: 'var(--text, #283252)', fontFamily: 'var(--font-display, Montserrat, sans-serif)', lineHeight: 1.2 }}>Actividade da campanha</div>
        <div style={{ fontSize: 11, color: 'var(--text-muted, #94A4C4)', marginTop: 2, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>Acções e eventos recentes</div>
      </div>
      <div>
        {items.map((item, i) => (
          <div key={i} style={{ display: 'flex', gap: 10, padding: '10px 16px', borderBottom: i < items.length - 1 ? '1px solid var(--border, #ECEFF5)' : 'none' }}>
            <div style={{ paddingTop: 4, flexShrink: 0 }}>
              <div style={{ width: 9, height: 9, borderRadius: '50%', background: dotColors[item.color] || '#94A4C4' }} />
            </div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 10.5, fontWeight: 600, color: 'var(--text-muted, #94A4C4)', marginBottom: 2, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{item.time}</div>
              <div style={{ fontSize: 12, color: 'var(--text, #283252)', lineHeight: 1.45, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{item.msg}</div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
};

// ── KpiCards ───────────────────────────────────────────────────────────────────
const KpiCards = ({ detail }) => {
  if (!detail) return null;
  const proposta       = detail.proposta_json || {};
  const copyItems      = detail.copy    || [];
  const promptItems    = detail.prompts || [];
  const angles         = proposta.messaging_angles || [];
  const commPlan       = proposta.comm_plan || [];
  const copyApproved   = copyItems.filter(c => c.status === 'aprovado').length;
  const promptApproved = promptItems.filter(p => p.status === 'aprovado').length;

  const canaisSet = {};
  angles.forEach(a => { (a.canais || []).forEach(c => { canaisSet[c] = true; }); });
  const canaisStr = Object.keys(canaisSet).map(c => (CANAL_LABEL[c] || c).slice(0, 3).toUpperCase()).join(' · ') || (detail.big_idea ? '—' : 'por gerar');

  const firstWeek = commPlan.length ? (commPlan[0]?.planned_week || 'S-2') : null;
  const lastWeek  = commPlan.length ? (commPlan[commPlan.length - 1]?.planned_week || 'S+2') : null;

  const cards = [
    { label: 'Ângulos',         value: angles.length || '—',                          accent: 'var(--ai-500, #3859D0)',  fill: Math.min((angles.length || 0) / 5, 1) },
    { label: 'Copy',            value: `${copyApproved} / ${copyItems.length || '?'}`, accent: 'var(--warning, #f59e0b)', fill: copyItems.length ? copyApproved / copyItems.length : 0 },
    { label: 'Prompts Visuais', value: `${promptApproved} / ${promptItems.length || '?'}`, accent: 'var(--success, #22c55e)', fill: promptItems.length ? promptApproved / promptItems.length : 0 },
    { label: 'Plano',           value: commPlan.length ? `${commPlan.length} peças` : '—', accent: '#7C3AED',             fill: Math.min((commPlan.length || 0) / 20, 1) },
  ];

  return (
    <div style={{ display: 'flex', gap: 12 }}>
      {cards.map((k, i) => (
        <div key={k.label} style={{
          flex: '1 1 0', background: '#ffffff',
          border: '1px solid var(--border, #ECEFF5)',
          borderRadius: 8, padding: '12px 16px 0',
          display: 'flex', flexDirection: 'column', gap: 4, overflow: 'hidden',
        }}>
          <div style={{ fontSize: 9.5, fontWeight: 600, letterSpacing: '0.10em', textTransform: 'uppercase', color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-mono, monospace)', whiteSpace: 'nowrap' }}>{k.label}</div>
          <div style={{ fontSize: 22, fontWeight: 700, fontFamily: 'var(--font-display, Montserrat, sans-serif)', color: k.accent, lineHeight: 1, paddingBottom: 10 }}>{k.value}</div>
          <div style={{ height: 3, background: 'var(--bg-sunken, #eef1f6)', overflow: 'hidden' }}>
            <div style={{ height: '100%', width: `${(k.fill || 0) * 100}%`, background: k.accent, transition: 'width 400ms ease' }} />
          </div>
        </div>
      ))}
    </div>
  );
};

// ── DigiBriefingBar ────────────────────────────────────────────────────────────
const DigiBriefingBar = ({ detail, generating, onAction }) => {
  if (!detail) return null;
  const proposta     = detail.proposta_json || {};
  const angles       = proposta.messaging_angles || [];
  const copyItems    = detail.copy || [];
  const promptItems  = detail.prompts || [];
  const copyApproved = copyItems.filter(c => c.status === 'aprovado').length;
  const allCopyDone  = copyItems.length > 0 && copyApproved === copyItems.length;
  const isProducao   = detail.status === 'em_producao';
  const isApproved   = ['canais_pendente','canais_gerado','canais_aprovado','copy_pendente','copy_gerado','copy_aprovado','prompts_pendente','prompts_gerado','em_producao'].includes(detail.status);

  let msg, actions = [];

  if (!detail.big_idea) {
    msg = <>Sem conceito estratégico. A Digi AI vai criar <strong>personas, ângulos de mensagem e plano de comunicação</strong> a partir do briefing.</>;
    actions = [{ label: generating.conceito ? 'A gerar…' : 'Gerar Conceito', key: 'generateConceito', primary: true, disabled: !!generating.conceito }];
  } else if (angles.length === 0) {
    msg = <>Conceito básico gerado mas sem <strong>ângulos de mensagem, personas ou plano de comunicação</strong>. Recomenda-se actualizar para o Creative Strategy Engine v3.</>;
    actions = [
      { label: generating.conceito ? 'A actualizar…' : 'Actualizar para v3', key: 'generateConceito', primary: true, disabled: !!generating.conceito },
      ...(!isApproved ? [{ label: 'Aprovar e continuar', key: 'aprovarConceito', primary: false, disabled: !!generating.approve }] : []),
    ];
  } else if (copyItems.length === 0) {
    msg = <><strong>{detail.titulo}</strong> tem <strong>{angles.length} ângulo{angles.length !== 1 ? 's' : ''} de mensagem</strong>. Pronto para gerar copy adaptado aos canais do briefing.</>;
    actions = [
      { label: generating.copy ? 'A gerar…' : 'Gerar Copy →', key: 'generateCopy', primary: true, disabled: !!generating.copy },
      ...(!isApproved ? [{ label: 'Aprovar Conceito', key: 'aprovarConceito', primary: false, disabled: !!generating.approve }] : []),
    ];
  } else if (copyApproved === 0) {
    msg = <><strong>{copyItems.length} peças de copy</strong> geradas e a aguardar revisão. Aprova antes de gerar os prompts visuais.</>;
  } else if (!allCopyDone) {
    msg = <><strong>{copyApproved}/{copyItems.length} peças aprovadas.</strong> Podes avançar para os prompts visuais com os canais já aprovados.</>;
    actions = [{ label: generating.prompts ? 'A gerar…' : 'Gerar Prompts Visuais', key: 'generatePrompts', primary: true, disabled: !!generating.prompts }];
  } else if (promptItems.length === 0) {
    msg = <>Copy totalmente aprovado. Gera agora os <strong>prompts visuais</strong> para os canais da campanha.</>;
    actions = [{ label: generating.prompts ? 'A gerar…' : 'Gerar Prompts Visuais', key: 'generatePrompts', primary: true, disabled: !!generating.prompts }];
  } else if (isProducao) {
    msg = <>Campanha <strong>em produção</strong>. Todos os canais aprovados e enviados.</>;
  } else {
    msg = <>Tudo aprovado. Campanha pronta para enviar para produção.</>;
    actions = [{ label: generating.producao ? 'A enviar…' : '→ Enviar para Produção', key: 'enviarProducao', primary: true, disabled: !!generating.producao }];
  }

  return (
    <div style={{ background: '#ffffff', border: '1px solid var(--border, #ECEFF5)', borderRadius: 10, padding: '16px 18px' }}>
      <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, background: 'rgba(56,89,208,.06)', border: '1px solid rgba(56,89,208,.15)', borderRadius: 20, padding: '3px 10px', fontSize: 10, fontWeight: 700, color: 'var(--ai-500, #3859D0)', letterSpacing: '0.04em', marginBottom: 10 }}>
        <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--ai-500, #3859D0)', display: 'inline-block' }} />
        DIGI · CONCEITO
      </div>
      <div style={{ fontSize: 13.5, color: 'var(--text, #283252)', lineHeight: 1.55, marginBottom: actions.length ? 12 : 0, fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{msg}</div>
      {actions.length > 0 && (
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          {actions.map(a => (
            <button key={a.key} onClick={() => onAction(a.key)} disabled={a.disabled} style={{
              padding: '6px 14px', borderRadius: 6, fontSize: 12, fontWeight: 600,
              cursor: a.disabled ? 'not-allowed' : 'pointer', opacity: a.disabled ? 0.6 : 1,
              background: a.primary ? 'var(--ai-500, #3859D0)' : '#ffffff',
              color: a.primary ? '#ffffff' : 'var(--text, #283252)',
              border: a.primary ? 'none' : '1px solid var(--border, #ECEFF5)',
              fontFamily: 'var(--font-body, Inter, sans-serif)', transition: 'background .15s',
            }}>{a.label}</button>
          ))}
        </div>
      )}
    </div>
  );
};

// ── TabEstrategiaFull — estratégia por mercado gerada com base em briefing + KB ──
const ESTRATEGIA_STEPS = [
  { step: 1, label: 'Briefing & Produto',      detail: 'Leitura dos 5 blocos do briefing · USPs · dores · restrições absolutas',                                 duration: 3000 },
  { step: 2, label: 'Preferências de Mercado', detail: 'KB market_preferences por país + segmento (channel weights · decision pattern · cultural codes)',        duration: 5000 },
  { step: 3, label: 'Validação de Dados',      detail: 'Mercados com/sem dados KB · confidence level · gaps a marcar',                                            duration: 1500 },
  { step: 4, label: 'Geração por Mercado',     detail: 'Claude · personas priorizadas · channel_mix (%) · tom cultural · mensagem unificada 3-fases · KPIs',      duration: 25000 },
  { step: 5, label: 'Guardar Estratégia',      detail: 'Persistência estrategia_json e actualização de status',                                                   duration: 1500 },
];

const TabEstrategiaFull = ({ campanha, userEmail, generating: parentGenerating, genStep: parentGenStep, onAction }) => {
  const [data, setData] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState('');
  const [editingCountry, setEditingCountry] = React.useState(null);
  const [editDraft, setEditDraft] = React.useState({});
  const [saving, setSaving] = React.useState(false);
  const stepStartRef = React.useRef(null);
  const [stepPct, setStepPct] = React.useState(0);
  // Elapsed time counter — obrigatório antes de qualquer early return (Rules of Hooks)
  const [elapsedSec, setElapsedSec] = React.useState(0);
  const elapsedRef = React.useRef(null);
  const bCol = _brandColor(campanha?.brand_slug);

  // Usar estado do parent para loading (unifica header CTA + botão interno)
  const isGenerating = !!(parentGenerating?.estrategia);
  const sseStep      = (parentGenStep?.key === 'estrategia' ? parentGenStep?.step : 0) || 0;
  const progressMsg  = parentGenStep?.key === 'estrategia' ? (parentGenStep?.message || '') : '';

  // Fallback local timer — avança steps 1-3 por timing fixo porque o nginx faz
  // buffer dos SSE events e eles chegam todos juntos no final da chamada Claude.
  const [localStep, setLocalStep] = React.useState(0);
  const localTimersRef = React.useRef([]);

  React.useEffect(() => {
    localTimersRef.current.forEach(clearTimeout);
    localTimersRef.current = [];
    if (!isGenerating) { setLocalStep(0); return; }
    // Avança steps 1-3 com durations configurados; pára no 4 (Claude pode demorar 2+ min)
    let cum = 0;
    [1, 2, 3].forEach(stepNum => {
      const def = ESTRATEGIA_STEPS.find(s => s.step === stepNum);
      cum += (def?.duration || 3000);
      localTimersRef.current.push(setTimeout(() => setLocalStep(stepNum + 1), cum));
    });
    setLocalStep(1);
    return () => localTimersRef.current.forEach(clearTimeout);
  }, [isGenerating]);

  // SSE override: se o backend enviar um step superior ao local, usa o SSE
  const activeStep = Math.max(sseStep, localStep);

  // Timer de progresso por step
  React.useEffect(() => {
    if (!isGenerating || activeStep <= 0) { setStepPct(0); return; }
    stepStartRef.current = Date.now();
    setStepPct(0);
  }, [activeStep]);

  React.useEffect(() => {
    if (!isGenerating || activeStep <= 0) return;
    const def = ESTRATEGIA_STEPS.find(s => s.step === activeStep);
    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);
  }, [activeStep, isGenerating]);

  const totalSteps = ESTRATEGIA_STEPS.length;
  const doneSteps  = ESTRATEGIA_STEPS.filter(s => s.step < activeStep).length;
  const overallPct = isGenerating ? Math.round(((doneSteps + stepPct / 100) / totalSteps) * 100) : 0;

  const fetchStrategy = React.useCallback(() => {
    setLoading(true);
    campApiCall(`/api/marketing/campanhas/${campanha.id}/estrategia`)
      .then(d => { setData(d); setLoading(false); })
      .catch(e => { setError(e.message); setLoading(false); });
  }, [campanha?.id]);

  React.useEffect(() => { if (campanha?.id) fetchStrategy(); }, [campanha?.id, fetchStrategy]);

  React.useEffect(() => {
    if (isGenerating) {
      setElapsedSec(0);
      elapsedRef.current = setInterval(() => setElapsedSec(s => s + 1), 1000);
    } else {
      clearInterval(elapsedRef.current);
      setElapsedSec(0);
    }
    return () => clearInterval(elapsedRef.current);
  }, [isGenerating]);

  const approve = async () => {
    try {
      await campApiCall(`/api/marketing/campanhas/${campanha.id}/estrategia/approve`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ approver_email: userEmail, approver_name: userEmail?.split('@')[0] }),
      });
      fetchStrategy();
    } catch (e) { setError(e.message); }
  };

  if (loading) return <div style={{ padding: 40, textAlign: 'center', fontSize: 13, color: 'var(--fg-3)' }}>A carregar estratégia...</div>;

  const fmtElapsed = (s) => s < 60 ? `${s}s` : `${Math.floor(s/60)}m${String(s%60).padStart(2,'0')}s`;

  // ── Loading state de geração (controlado pelo parent via props) ──
  if (isGenerating) {
    const EsDot = ({ done, active }) => {
      if (done) return (
        <div style={{ width: 16, height: 16, borderRadius: '50%', background: 'rgba(21,128,61,.12)', border: '1.5px solid rgba(21,128,61,.3)', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <svg width="8" height="8" viewBox="0 0 10 10" fill="none"><polyline points="1.5,5 4,7.5 8.5,2.5" stroke="#15803d" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>
        </div>
      );
      if (active) return (
        <div style={{ width: 16, height: 16, borderRadius: '50%', border: '2px solid rgba(91,67,197,.25)', borderTopColor: '#5B43C5', flexShrink: 0, animation: 'espin 0.7s linear infinite' }} />
      );
      return <div style={{ width: 16, height: 16, borderRadius: '50%', border: '1.5px solid #e2e8f0', flexShrink: 0 }} />;
    };
    return (
      <div style={{ background: 'var(--bg-elev,#fff)', border: '1px solid var(--border,#e2e8f0)', borderRadius: 10, padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 16 }}>
        <style>{`
          @keyframes cpulse { 0%,100%{opacity:1} 50%{opacity:.25} }
          @keyframes espin  { to { transform: rotate(360deg); } }
        `}</style>
        {/* Aviso: não fechar */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 12px', background: '#fffbeb', border: '1px solid #fde68a', borderRadius: 6 }}>
          <span style={{ fontSize: 13 }}>⚠</span>
          <span style={{ fontSize: 12, color: '#92400e', fontWeight: 500 }}>Não feches esta janela nem saias deste ecrã — a geração está em curso.</span>
        </div>
        {/* Barra de progresso + tempo */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <div style={{ flex: 1, height: 5, borderRadius: 99, background: 'var(--bg-app,#f5f6f8)', overflow: 'hidden' }}>
            <div style={{ height: '100%', borderRadius: 99, background: 'linear-gradient(90deg,#5B43C5,#7c6de0)', width: `${overallPct}%`, transition: 'width 0.15s linear' }} />
          </div>
          <span style={{ fontSize: 11, fontWeight: 700, color: '#5B43C5', fontFamily: 'monospace', flexShrink: 0, minWidth: 32 }}>{overallPct}%</span>
          <span style={{ fontSize: 11, fontWeight: 600, color: '#94a3b8', fontFamily: 'monospace', flexShrink: 0, minWidth: 36 }}>{fmtElapsed(elapsedSec)}</span>
        </div>
        {/* Tabela de steps */}
        <table style={{ width: '100%', borderCollapse: 'collapse', tableLayout: 'fixed' }}>
          <colgroup><col style={{ width: 28 }} /><col style={{ width: 220 }} /><col /></colgroup>
          <thead>
            <tr>
              <th style={{ padding: '4px 8px 4px 0', fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#94a3b8', fontFamily: 'monospace', textAlign: 'left' }} />
              <th style={{ padding: '4px 8px', fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#94a3b8', fontFamily: 'monospace', textAlign: 'left' }}>SECÇÃO</th>
              <th style={{ padding: '4px 8px', fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#94a3b8', fontFamily: 'monospace', textAlign: 'left' }}>DETALHE</th>
            </tr>
          </thead>
          <tbody>
            {ESTRATEGIA_STEPS.map((s) => {
              const done   = activeStep > s.step;
              const active = activeStep === s.step;
              return (
                <tr key={s.step}>
                  <td style={{ padding: '8px 8px 8px 0', verticalAlign: 'middle' }}><EsDot done={done} active={active} /></td>
                  <td style={{ padding: '8px', fontSize: 13, fontWeight: active ? 600 : 400, color: done ? '#15803d' : active ? '#1d2e38' : '#94a3b8', fontFamily: 'var(--font-body,Inter,sans-serif)', borderBottom: '1px solid #f1f5f9' }}>{s.label}</td>
                  <td style={{ padding: '8px', fontSize: 12, color: active ? '#64748b' : '#cbd5e1', fontFamily: 'var(--font-body,Inter,sans-serif)', borderBottom: '1px solid #f1f5f9' }}>{s.detail}</td>
                </tr>
              );
            })}
          </tbody>
        </table>
        {error && <div style={{ padding: '8px 12px', background: 'rgba(220,38,38,.06)', border: '1px solid rgba(220,38,38,.2)', borderRadius: 6, fontSize: 12, color: '#dc2626' }}>{error}</div>}
      </div>
    );
  }

  const estrategia = data?.estrategia_json;
  const approved   = !!data?.estrategia_approved_at;

  // ── Estado vazio ──
  if (!estrategia) {
    return (
      <div style={{ background: 'var(--bg-surface,#fff)', borderRadius: 'var(--radius-xl,14px)', boxShadow: 'var(--shadow-card)', padding: '32px 28px', textAlign: 'center' }}>
        <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'var(--fg-3)', fontFamily: 'var(--font-mono)', marginBottom: 8 }}>Estratégia · fase 1</div>
        <div style={{ fontSize: 20, fontWeight: 700, color: 'var(--dd-primary-900,#112954)', fontFamily: 'var(--font-sans,Montserrat,sans-serif)', marginBottom: 10 }}>Ainda sem estratégia definida</div>
        <p style={{ fontSize: 13, color: 'var(--fg-2)', lineHeight: 1.6, maxWidth: 560, margin: '0 auto 20px', fontFamily: 'var(--font-text,Inter,sans-serif)' }}>
          Gerada 100% a partir do briefing aprovado. Adapta canais, tom, mensagem-chave e personas às preferências reais de cada mercado, enriquecendo com a KB <code style={{ fontSize: 11, background: 'var(--bg-sunken)', padding: '1px 5px', borderRadius: 3, fontFamily: 'var(--font-mono)' }}>market_preferences</code>. Respeita os guardrails do bloco 5 e alimenta todas as fases seguintes.
        </p>
        <button onClick={() => onAction && onAction('generateEstrategia')} disabled={isGenerating} className="btn btn-ai" style={{ padding: '10px 24px', fontSize: 13 }}>
          {isGenerating ? (progressMsg || 'A gerar...') : 'Gerar Estratégia →'}
        </button>
        {error && <div style={{ marginTop: 14, fontSize: 12, color: '#dc2626' }}>{error}</div>}
      </div>
    );
  }

  // ── Edição inline por mercado ──
  const startEdit = (m) => {
    setEditingCountry(m.country);
    setEditDraft({ tom: m.tom || '', personas_priorizadas: m.personas_priorizadas ? JSON.parse(JSON.stringify(m.personas_priorizadas)) : [] });
  };
  const saveEdit = async () => {
    setSaving(true);
    try {
      await campApiCall(`/api/marketing/campanhas/${campanha.id}/estrategia`, {
        method: 'PATCH', body: JSON.stringify({ country: editingCountry, ...editDraft }),
      });
      setEditingCountry(null);
      fetchStrategy();
    } catch (e) { setError(e.message); }
    setSaving(false);
  };

  // ── Renderização estratégia por mercado ──
  const markets = estrategia.markets || [];

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
      {/* Header status + actions */}
      <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: approved ? 'var(--green-100,#E1F7E6)' : 'var(--dd-blue-100,#EBEFF9)', color: approved ? 'var(--green-700,#1F8A52)' : 'var(--dd-primary-600,#3859D0)' }}>
          {approved ? 'Aprovada' : 'Gerada — aguarda aprovação'}
        </span>
        <span style={{ fontSize: 11, color: 'var(--fg-3)' }}>
          {markets.length} mercado{markets.length !== 1 ? 's' : ''} · gerado {data.estrategia_generated_at ? new Date(data.estrategia_generated_at).toLocaleString('pt-PT') : '—'}
        </span>
        {approved && data.estrategia_approved_at && (
          <span style={{ fontSize: 11, color: 'var(--green-700, #1F8A52)' }}>
            · aprovada {new Date(data.estrategia_approved_at).toLocaleString('pt-PT')}{data.estrategia_approved_by ? ` por ${data.estrategia_approved_by.split('@')[0]}` : ''}
          </span>
        )}
        <div style={{ marginLeft: 'auto', display: 'flex', gap: 6, alignItems: 'center' }}>
          <button
            onClick={() => onAction && onAction('generateEstrategia')}
            disabled={isGenerating}
            className="btn"
            style={{ height: 28, padding: '0 12px', fontSize: 12 }}
            title="Regenerar estratégia — invalida a aprovação"
          >Regenerar</button>
          {!approved && (
            <button onClick={approve} className="btn btn-ai" style={{ height: 28, padding: '0 14px', fontSize: 12 }}>
              Aprovar Estratégia
            </button>
          )}
        </div>
      </div>

      {error && <div style={{ padding: 12, background: 'rgba(220,38,38,.06)', border: '1px solid rgba(220,38,38,.2)', borderRadius: 8, fontSize: 12, color: '#dc2626' }}>{error}</div>}

      {/* Síntese executiva */}
      {estrategia.sintese_campanha && (() => {
        const sc = estrategia.sintese_campanha;
        const anc = sc.ancoras || {};
        return (
          <div style={{ background: 'var(--bg-surface,#fff)', borderRadius: 'var(--radius-xl,14px)', boxShadow: 'var(--shadow-card)', padding: '20px 24px', borderTop: `3px solid ${bCol}` }}>
            <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: bCol, fontFamily: 'var(--font-mono)', marginBottom: 14 }}>Síntese Executiva da Campanha</div>
            {/* Fio condutor */}
            {sc.fio_condutor && (
              <div style={{ background: bCol + '08', border: `1px solid ${bCol}30`, borderRadius: 8, padding: '12px 16px', marginBottom: 16 }}>
                <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: bCol, fontFamily: 'var(--font-mono)', marginBottom: 4 }}>Fio Condutor · presente em todo o conteúdo</div>
                <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--fg-1)', fontFamily: 'var(--font-sans,Montserrat,sans-serif)', lineHeight: 1.4, fontStyle: 'italic' }}>"{ct(sc.fio_condutor)}"</div>
              </div>
            )}
            {/* Objectivo com pontos numerados + Abordagem */}
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, marginBottom: 16 }}>
              {sc.objectivo_concreto && (() => {
                const paras = splitPoints(sc.objectivo_concreto);
                return (
                  <div>
                    <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--fg-3)', fontFamily: 'var(--font-mono)', marginBottom: 6 }}>Objectivo</div>
                    {paras.map(({ num, txt }, i) => (
                      <div key={i} style={{ display: 'flex', gap: 8, marginBottom: i < paras.length - 1 ? 6 : 0, alignItems: 'flex-start' }}>
                        {num && <span style={{ fontSize: 10, fontWeight: 700, color: bCol, fontFamily: 'var(--font-mono)', flexShrink: 0, minWidth: 16, paddingTop: 2 }}>{num}.</span>}
                        <div style={{ fontSize: 12, color: 'var(--fg-1)', lineHeight: 1.6 }}>{txt}</div>
                      </div>
                    ))}
                  </div>
                );
              })()}
              {sc.abordagem && (() => {
                const paras = splitPoints(sc.abordagem);
                return (
                  <div>
                    <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--fg-3)', fontFamily: 'var(--font-mono)', marginBottom: 6 }}>Como chegamos lá</div>
                    {paras.map(({ num, txt }, i) => (
                      <div key={i} style={{ display: 'flex', gap: 8, marginBottom: i < paras.length - 1 ? 6 : 0, alignItems: 'flex-start' }}>
                        {num && <span style={{ fontSize: 10, fontWeight: 700, color: bCol, fontFamily: 'var(--font-mono)', flexShrink: 0, minWidth: 16, paddingTop: 2 }}>{num}.</span>}
                        <div style={{ fontSize: 12, color: 'var(--fg-1)', lineHeight: 1.6 }}>{txt}</div>
                      </div>
                    ))}
                  </div>
                );
              })()}
            </div>
            {/* Âncoras */}
            {(anc.dor_dominante || anc.usp_principal || anc.persona_principal) && (
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8, marginBottom: sc.diferenca_por_mercado ? 12 : 0 }}>
                {[
                  { label: 'Dor Dominante', val: anc.dor_dominante, col: '#dc2626' },
                  { label: 'USP Principal', val: anc.usp_principal, col: '#059669' },
                  { label: 'Persona Principal', val: anc.persona_principal, col: '#8b5cf6' },
                ].filter(x => x.val).map(({ label, val, col }) => (
                  <div key={label} style={{ padding: '8px 10px', background: col + '08', borderRadius: 6, borderLeft: `3px solid ${col}` }}>
                    <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: col, fontFamily: 'var(--font-mono)', marginBottom: 3 }}>{label}</div>
                    <div style={{ fontSize: 11, color: 'var(--fg-1)', lineHeight: 1.4 }}>{ct(val)}</div>
                  </div>
                ))}
              </div>
            )}
            {sc.diferenca_por_mercado && (
              <div style={{ marginTop: 10, fontSize: 11, color: 'var(--fg-3)', fontStyle: 'italic', lineHeight: 1.5, paddingTop: 10, borderTop: '1px solid var(--border-1,#ECEFF5)' }}>
                <span style={{ fontWeight: 700, fontStyle: 'normal' }}>Mercados: </span>{ct(sc.diferenca_por_mercado)}
              </div>
            )}
          </div>
        );
      })()}

      {/* Oferta comercial */}
      {(() => {
        let offer = campanha?.commercial_offer || campanha?.briefing?.commercial_offer;
        if (!offer) return null;
        if (typeof offer === 'string') { try { offer = JSON.parse(offer); } catch { return null; } }
        if (!offer?.type || offer.type === 'sem_oferta_especifica') return null;
        const MECH_LABEL = { digirent: 'Digirent', printplan: 'PrintPlan', voucher: 'Voucher on Demand', direct_discount: 'Desconto Directo', trade_in: 'Trade-In' };
        const TIER_LABEL = { standard: 'Standard', premium: 'Premium', large: 'Large', enterprise: 'Enterprise', tailor_made: 'Tailor-Made' };
        const CTA_LABEL  = { demo_request: 'Pedido de demonstração', sample_request: 'Pedido de amostra', quote_request: 'Pedido de orçamento', call_back: 'Callback comercial' };
        return (
          <div style={{ background: 'var(--bg-surface,#fff)', borderRadius: 'var(--radius-md,8px)', boxShadow: 'var(--shadow-card)', padding: '14px 18px', borderLeft: `3px solid ${bCol}` }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: offer.details ? 8 : 0 }}>
              <span style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: '#fff', background: bCol, borderRadius: 4, padding: '2px 7px', fontFamily: 'var(--font-mono)', flexShrink: 0 }}>Oferta Comercial</span>
              <span style={{ fontSize: 13, fontWeight: 700, color: 'var(--fg-1)', fontFamily: 'var(--font-text,Inter,sans-serif)' }}>{MECH_LABEL[offer.type] || offer.type}</span>
              {offer.tier && <span style={{ fontSize: 10, color: 'var(--fg-3)', fontFamily: 'var(--font-mono)', padding: '1px 6px', borderRadius: 3, background: 'var(--border-1,#ECEFF5)' }}>{TIER_LABEL[offer.tier] || offer.tier}</span>}
              {offer.primary_cta && <span style={{ fontSize: 11, color: 'var(--fg-2)', marginLeft: 4 }}>· CTA: {CTA_LABEL[offer.primary_cta] || offer.primary_cta}</span>}
              {offer.negotiable !== undefined && <span style={{ fontSize: 10, color: 'var(--fg-3)', marginLeft: 'auto', fontFamily: 'var(--font-mono)' }}>{offer.negotiable ? 'Negociável' : 'Valor fixo'}</span>}
            </div>
            {offer.details && <div style={{ fontSize: 12, color: 'var(--fg-2)', lineHeight: 1.6 }}>{ct(offer.details)}</div>}
          </div>
        );
      })()}

      {/* Notas gerais */}
      {estrategia.notas_gerais && (() => {
        const raw = ct(estrategia.notas_gerais);
        // Split por pontos numerados (1) (2) (3) ou quebras de linha
        const parts = raw.split(/\s*\((\d+)\)\s*/).reduce((acc, seg, i, arr) => {
          if (/^\d+$/.test(seg)) return acc; // é o número, skip
          const prev = arr[i - 1];
          const num = /^\d+$/.test(prev) ? prev : null;
          const txt = seg.trim();
          if (txt) acc.push({ num, txt });
          return acc;
        }, []);
        const paras = parts.length > 1 ? parts : raw.split(/\n+/).filter(Boolean).map(t => ({ num: null, txt: t }));
        return (
          <div style={{ background: 'var(--bg-surface,#fff)', borderLeft: `3px solid ${bCol}`, borderRadius: 'var(--radius-md,8px)', padding: '16px 20px', boxShadow: 'var(--shadow-card)' }}>
            <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: bCol, fontFamily: 'var(--font-mono)', marginBottom: 12 }}>Notas cross-market</div>
            {paras.map(({ num, txt }, i) => (
              <div key={i} style={{ display: 'flex', gap: 10, marginBottom: i < paras.length - 1 ? 10 : 0, alignItems: 'flex-start' }}>
                {num && <span style={{ fontSize: 10, fontWeight: 700, color: bCol, fontFamily: 'var(--font-mono)', flexShrink: 0, minWidth: 18, paddingTop: 2 }}>{num}.</span>}
                <p style={{ margin: 0, fontSize: 13, color: 'var(--fg-1)', lineHeight: 1.7, fontFamily: 'var(--font-text,Inter,sans-serif)' }}>{txt}</p>
              </div>
            ))}
          </div>
        );
      })()}

      {/* Cards por mercado */}
      {markets.map((m, i) => {
        const confCol = m.confidence === 'high' ? 'var(--green-700,#1F8A52)' : m.confidence === 'medium' ? 'var(--pumpkin-700,#E96D17)' : 'var(--red-700,#B53B3B)';
        return (
          <div key={i} style={{ background: 'var(--bg-surface,#fff)', borderRadius: 'var(--radius-xl,14px)', boxShadow: 'var(--shadow-card)', padding: '20px 24px' }}>
            {/* Header país */}
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 16, paddingBottom: 12, borderBottom: '1px solid var(--border-1,#ECEFF5)' }}>
              <span style={{ fontSize: 11, fontWeight: 700, color: '#fff', background: bCol, borderRadius: 'var(--radius-xs,4px)', padding: '3px 8px', fontFamily: 'var(--font-mono)', letterSpacing: '0.04em' }}>{m.country}</span>
              <span style={{ fontSize: 16, fontWeight: 700, color: 'var(--dd-primary-900,#112954)', fontFamily: 'var(--font-sans,Montserrat,sans-serif)' }}>{m.language_variant || m.country}</span>
              <span style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em', color: confCol, padding: '2px 8px', border: `1px solid ${confCol}`, borderRadius: 'var(--radius-pill)' }}>
                {m.confidence} confidence
              </span>
              <button onClick={() => editingCountry === m.country ? setEditingCountry(null) : startEdit(m)}
                className="btn" style={{ marginLeft: 'auto', fontSize: 11, height: 26, padding: '0 10px' }}>
                {editingCountry === m.country ? 'Cancelar' : '✎ Editar'}
              </button>
            </div>

            {/* Formulário de edição inline */}
            {editingCountry === m.country && (
              <div style={{ marginBottom: 16, padding: '14px 16px', background: 'var(--bg-sunken,#f8fafc)', borderRadius: 8, border: '1px solid var(--border-1,#ECEFF5)' }}>
                <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--fg-3)', fontFamily: 'var(--font-mono)', marginBottom: 12 }}>Editar {m.country}</div>
                {/* Tom */}
                <div style={{ marginBottom: 12 }}>
                  <label style={{ fontSize: 10, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.06em', color: 'var(--fg-3)', fontFamily: 'var(--font-mono)', display: 'block', marginBottom: 4 }}>Tom</label>
                  <textarea value={editDraft.tom} onChange={e => setEditDraft(d => ({ ...d, tom: e.target.value }))}
                    rows={2} style={{ width: '100%', padding: '7px 10px', borderRadius: 6, border: '1px solid var(--border-1,#ECEFF5)', fontSize: 13, fontFamily: 'inherit', resize: 'vertical', boxSizing: 'border-box' }} />
                </div>
                {/* Personas */}
                <div style={{ marginBottom: 12 }}>
                  <label style={{ fontSize: 10, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.06em', color: 'var(--fg-3)', fontFamily: 'var(--font-mono)', display: 'block', marginBottom: 6 }}>Personas priorizadas</label>
                  {(editDraft.personas_priorizadas || []).map((p, j) => (
                    <div key={j} style={{ display: 'flex', gap: 6, marginBottom: 6, alignItems: 'center' }}>
                      <select value={p.peso} onChange={e => { const ps = [...editDraft.personas_priorizadas]; ps[j] = { ...p, peso: e.target.value }; setEditDraft(d => ({ ...d, personas_priorizadas: ps })); }}
                        style={{ fontSize: 11, padding: '4px 6px', borderRadius: 4, border: '1px solid var(--border-1)', background: 'var(--bg)', flexShrink: 0 }}>
                        <option value="primary">Primary</option>
                        <option value="secondary">Secondary</option>
                      </select>
                      <input value={p.nome} onChange={e => { const ps = [...editDraft.personas_priorizadas]; ps[j] = { ...p, nome: e.target.value }; setEditDraft(d => ({ ...d, personas_priorizadas: ps })); }}
                        placeholder="Nome da persona" style={{ flex: 1, fontSize: 12, padding: '4px 8px', borderRadius: 4, border: '1px solid var(--border-1)', fontFamily: 'inherit' }} />
                      <button onClick={() => { const ps = editDraft.personas_priorizadas.filter((_, k) => k !== j); setEditDraft(d => ({ ...d, personas_priorizadas: ps })); }}
                        style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#dc2626', fontSize: 14, padding: '0 4px' }}>×</button>
                    </div>
                  ))}
                  <button onClick={() => setEditDraft(d => ({ ...d, personas_priorizadas: [...(d.personas_priorizadas || []), { nome: '', peso: 'secondary', razao: '' }] }))}
                    className="btn" style={{ fontSize: 11, height: 26, padding: '0 10px', marginTop: 4 }}>+ Persona</button>
                </div>
                <div style={{ display: 'flex', gap: 6, justifyContent: 'flex-end' }}>
                  <button onClick={() => setEditingCountry(null)} className="btn" style={{ fontSize: 11 }}>Cancelar</button>
                  <button onClick={saveEdit} disabled={saving} className="btn btn-ai" style={{ fontSize: 11 }}>
                    {saving ? 'A guardar...' : '✓ Guardar'}
                  </button>
                </div>
              </div>
            )}

            {/* Personas (read-only) */}
            {editingCountry !== m.country && (m.personas_priorizadas || []).length > 0 && (
              <div style={{ marginBottom: 16 }}>
                <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--fg-3)', fontFamily: 'var(--font-mono)', marginBottom: 10 }}>Personas priorizadas</div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                  {m.personas_priorizadas.map((p, j) => {
                    const isPrimary = p.peso === 'primary';
                    const cleanDash = (t) => t ? t.replace(/\u2014|\u2013/g, ',').replace(/ , /g, ', ').replace(/,{2,}/g, ',') : t;
                    return (
                      <div key={j} style={{ display: 'flex', gap: 0, alignItems: 'stretch', background: isPrimary ? bCol + '06' : 'var(--bg-sunken,#f8fafc)', borderRadius: 8, borderLeft: `3px solid ${isPrimary ? bCol : '#e2e8f0'}`, overflow: 'hidden' }}>
                        <div style={{ width: 72, flexShrink: 0, display: 'flex', alignItems: 'flex-start', justifyContent: 'center', paddingTop: 11, paddingBottom: 10 }}>
                          <span style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em', padding: '2px 6px', borderRadius: 4, background: isPrimary ? bCol : '#e2e8f0', color: isPrimary ? '#fff' : 'var(--fg-3)', fontFamily: 'var(--font-mono)', whiteSpace: 'nowrap' }}>{p.peso}</span>
                        </div>
                        <div style={{ flex: 1, minWidth: 0, padding: '10px 12px 10px 0' }}>
                          <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--fg-1)', fontFamily: 'var(--font-text,Inter,sans-serif)', lineHeight: 1.4, marginBottom: p.razao ? 5 : 0 }}>{cleanDash(p.nome)}</div>
                          {p.razao && <div style={{ fontSize: 12, color: 'var(--fg-2)', lineHeight: 1.65, fontFamily: 'var(--font-text,Inter,sans-serif)' }}>{cleanDash(p.razao)}</div>}
                        </div>
                      </div>
                    );
                  })}
                </div>
              </div>
            )}

            {/* Channel fit KB */}
            {(m.channel_mix?.length > 0 || m.sugestoes_alternativas?.length > 0) && (() => {
              const hasLow    = (m.channel_mix || []).some(ch => ch.fit === 'low');
              const hasSugest = (m.sugestoes_alternativas || []).length > 0;
              const fitCol = { high: '#059669', medium: '#d97706', low: '#dc2626', sem_dados: '#94a3b8' };
              const fitBg  = { high: '#f0fdf4', medium: '#fffbeb', low: '#fef2f2', sem_dados: '#f8fafc' };
              return (
                <div style={{ marginBottom: 16, borderRadius: 8, border: '1px solid var(--border-1,#ECEFF5)', background: 'var(--bg-sunken,#f8fafc)', padding: '12px 14px' }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 10 }}>
                    <span style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.07em', textTransform: 'uppercase', color: 'var(--fg-3)', fontFamily: 'var(--font-mono)' }}>
                      Fit de Canal · KB {m.kb_confidence ? `· ${m.kb_confidence}` : ''}
                    </span>
                    {hasLow && <span style={{ fontSize: 10, fontWeight: 700, color: '#dc2626', padding: '1px 6px', borderRadius: 4, background: '#fef2f2', border: '1px solid #fecaca' }}>⚠ Canal com baixa adopção</span>}
                  </div>

                  {/* Todos os canais aprovados */}
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginBottom: hasSugest ? 10 : 0 }}>
                    {(m.channel_mix || []).map((ch, k) => (
                      <div key={k} style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>
                        <div style={{ flex: 1 }}>
                          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
                            <span style={{ fontSize: 11, fontWeight: 700, padding: '1px 7px', borderRadius: 4, background: fitBg[ch.fit], color: fitCol[ch.fit], fontFamily: 'var(--font-mono)', border: `1px solid ${fitCol[ch.fit]}30` }}>{ch.canal}</span>
                            <span style={{ fontSize: 11, fontWeight: 600, color: fitCol[ch.fit] }}>{ch.pct != null ? `${ch.pct}%` : 'sem dados'} adopção</span>
                            <span style={{ fontSize: 10, color: 'var(--fg-3)', fontStyle: 'italic' }}>· {ch.label}</span>
                          </div>
                          <div style={{ height: 4, background: '#e2e8f0', borderRadius: 2, overflow: 'hidden', maxWidth: 200 }}>
                            <div style={{ height: '100%', width: `${ch.pct || 0}%`, background: fitCol[ch.fit], borderRadius: 2, transition: 'width .3s' }} />
                          </div>
                          {ch.notes && <div style={{ fontSize: 11, color: '#64748b', marginTop: 4, lineHeight: 1.5 }}>{ct(ch.notes)}</div>}
                        </div>
                      </div>
                    ))}
                  </div>

                  {/* Sugestões alternativas */}
                  {hasSugest && (
                    <div style={{ borderTop: `1px solid ${hasLow ? '#fecaca' : '#fde68a'}`, paddingTop: 8, marginTop: 4 }}>
                      <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: '#059669', fontFamily: 'var(--font-mono)', marginBottom: 6 }}>Canais com maior adopção neste mercado</div>
                      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
                        {(m.sugestoes_alternativas || []).map((s, k) => (
                          <div key={k} style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '5px 10px', borderRadius: 6, background: '#f0fdf4', border: '1px solid #bbf7d0' }}>
                            <span style={{ fontSize: 11, fontWeight: 700, color: '#059669', fontFamily: 'var(--font-mono)' }}>{s.canal}</span>
                            <span style={{ fontSize: 11, color: '#059669', fontWeight: 600 }}>{s.pct}%</span>
                          </div>
                        ))}
                      </div>
                    </div>
                  )}
                </div>
              );
            })()}

            {/* Channel mix */}
            {(m.channel_mix || []).length > 0 && (
              <div style={{ marginBottom: 16 }}>
                <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--fg-3)', fontFamily: 'var(--font-mono)', marginBottom: 8 }}>Channel mix · todos cobrem as 3 fases</div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                  {m.channel_mix.map((ch, j) => {
                    const cCol = CANAL_COLORS[ch.canal] || bCol;
                    const f3 = ch.como_cobre_3_fases || {};
                    return (
                      <div key={j} style={{ padding: '10px 12px', background: 'var(--bg-sunken,#f8fafc)', borderRadius: 'var(--radius-md,8px)', borderLeft: `3px solid ${cCol}` }}>
                        {/* Header canal */}
                        <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginBottom: f3.awareness || ch.papel_no_mix ? 8 : 0 }}>
                          <span style={{ fontSize: 11, fontWeight: 700, padding: '2px 8px', borderRadius: 'var(--radius-xs,4px)', background: cCol + '18', color: cCol, fontFamily: 'var(--font-mono)' }}>{ch.canal}</span>
                          {ch.budget_pct != null && <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--fg-1)', fontFamily: 'var(--font-mono)' }}>{ch.budget_pct}%</span>}
                          {Array.isArray(ch.formatos) && ch.formatos.length > 0 && <span style={{ fontSize: 11, color: 'var(--fg-3)' }}>· {ch.formatos.join(', ')}</span>}
                          {ch.papel_no_mix && <span style={{ fontSize: 11, color: 'var(--fg-2)', fontStyle: 'italic', flex: 1, textAlign: 'right' }}>{ct(ch.papel_no_mix)}</span>}
                        </div>
                        {/* 3 fases */}
                        {(f3.awareness || f3.consideration || f3.decision) && (
                          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 6 }}>
                            {[['awareness','A','#3859D0'], ['consideration','C','#8b5cf6'], ['decision','D','#059669']].map(([key, abbr, col]) => f3[key] && (
                              <div key={key} style={{ padding: '6px 8px', background: col + '08', borderRadius: 6, borderTop: `2px solid ${col}` }}>
                                <div style={{ fontSize: 8, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: col, fontFamily: 'var(--font-mono)', marginBottom: 3 }}>{key}</div>
                                <div style={{ fontSize: 11, color: 'var(--fg-2)', lineHeight: 1.4 }}>{ct(f3[key])}</div>
                              </div>
                            ))}
                          </div>
                        )}
                        {ch.razao && <div style={{ fontSize: 10, color: 'var(--fg-3)', fontStyle: 'italic', marginTop: 4 }}>{ct(ch.razao)}</div>}
                      </div>
                    );
                  })}
                </div>
              </div>
            )}

            {/* Tom (read-only quando não editando) */}
            {editingCountry !== m.country && m.tom && (
              <div style={{ marginBottom: 16 }}>
                <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--fg-3)', fontFamily: 'var(--font-mono)', marginBottom: 6 }}>Tom</div>
                <div style={{ fontSize: 13, color: 'var(--fg-1)', fontStyle: 'italic', fontFamily: 'var(--font-sans,Montserrat,sans-serif)', lineHeight: 1.55 }}>{ct(m.tom)}</div>
              </div>
            )}

            {/* Mensagem unificada — 3 fases presentes em todo o conteúdo */}
            {(m.mensagem_unificada || m.funil) && (() => {
              const mu = m.mensagem_unificada || m.funil;
              const decisionKey = mu.decision || mu.conversion;
              const phaseColors = { awareness: '#3859D0', consideration: '#8b5cf6', decision: '#059669', conversion: '#059669' };
              return (
                <div style={{ marginBottom: 16 }}>
                  <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--fg-3)', fontFamily: 'var(--font-mono)', marginBottom: 8 }}>
                    Mensagem unificada · presente em todo o conteúdo
                  </div>
                  <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 10 }}>
                    {[
                      { key: 'awareness',    label: 'Awareness',     col: '#3859D0', val: mu.awareness },
                      { key: 'consideration',label: 'Consideration', col: '#8b5cf6', val: mu.consideration },
                      { key: 'decision',     label: 'Decision',      col: '#059669', val: decisionKey },
                    ].filter(p => p.val).map(({ key, label, col, val }) => (
                      <div key={key} style={{ padding: '10px 12px', background: col + '08', borderRadius: 'var(--radius-md,8px)', borderTop: `2px solid ${col}` }}>
                        <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: col, fontFamily: 'var(--font-mono)', marginBottom: 5 }}>{label}</div>
                        <div style={{ fontSize: 12, color: 'var(--fg-1)', lineHeight: 1.5, fontFamily: 'var(--font-text,Inter,sans-serif)' }}>{ct(val)}</div>
                      </div>
                    ))}
                  </div>
                </div>
              );
            })()}

            {/* KPIs */}
            {Array.isArray(m.kpis) && m.kpis.length > 0 && (
              <div style={{ marginBottom: 16 }}>
                <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--fg-3)', fontFamily: 'var(--font-mono)', marginBottom: 8 }}>KPIs</div>
                <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
                  {m.kpis.map((k, j) => (
                    <span key={j} style={{ fontSize: 11, padding: '3px 10px', borderRadius: 'var(--radius-pill)', background: 'var(--dd-blue-100,#EBEFF9)', color: 'var(--dd-primary-900,#112954)', fontFamily: 'var(--font-text,Inter,sans-serif)' }}>
                      {k.metrica}: <strong>{k.target}</strong>{k.canal ? ` (${k.canal})` : ''}
                    </span>
                  ))}
                </div>
              </div>
            )}

            {/* Market evidence */}
            {m.market_evidence && (
              <div style={{ padding: '10px 14px', background: 'var(--dd-blue-50,#F9FAFF)', borderLeft: `3px solid ${bCol}`, borderRadius: '0 8px 8px 0' }}>
                <div style={{ fontSize: 9, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: bCol, fontFamily: 'var(--font-mono)', marginBottom: 4 }}>Baseado na KB</div>
                <div style={{ fontSize: 12, color: 'var(--fg-2)', lineHeight: 1.55, fontFamily: 'var(--font-text,Inter,sans-serif)' }}>{ct(m.market_evidence)}</div>
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
};

// ── TabBriefingFull — resumo visual do briefing aprovado ──────────────────────
const TabBriefingFull = ({ campanha }) => {
  const [heroImage,  setHeroImage]  = React.useState(null);
  const [summaries,  setSummaries]  = React.useState(null);
  const [loading,    setLoading]    = React.useState(false);
  const bCol = _brandColor(campanha?.brand_slug || campanha?.briefing?.brand_slug);

  // Usar campanha.briefing directamente (já incluído no GET /campanhas/:id)
  // Mapear estrutura flat para block1/block2/... que o componente espera
  const briefing = React.useMemo(() => {
    if (!campanha?.briefing) return null;
    const f = campanha.briefing;
    return {
      ...f,
      block1: { commercial_name: f.commercial_name, elevator_pitch: f.elevator_pitch, usps: f.usps, geo_markets: f.geo_markets, applications: f.applications, certifications: f.certifications },
      block2: { decision_maker: f.decision_maker, end_user: f.end_user, pain_points: f.pain_points, motivators: f.motivators, objections: f.objections, purchase_trigger: f.purchase_trigger },
      block3: { positioning_narrative: f.positioning_narrative, differentiation_args: f.differentiation_args, competitors: f.competitors, market_trends: f.market_trends },
      block4: { objective: f.objective, channels: f.channels, tone: f.tone, key_message: f.key_message, timeline_start: f.timeline_start, timeline_end: f.timeline_end, commercial_offer: f.commercial_offer },
      block5: { prohibited_claims: f.prohibited_claims, off_brand_messages: f.off_brand_messages },
      brand_name: f.brand_name || campanha?.brand_name,
      brand_slug: f.brand_slug || campanha?.brand_slug,
    };
  }, [campanha?.briefing, campanha?.brand_name, campanha?.brand_slug]);

  // Buscar hero image e summaries em background (não bloqueiam render)
  React.useEffect(() => {
    if (!campanha?.id) return;
    campApiCall(`/api/marketing/campanhas/${campanha.id}/product-image`).then(d => setHeroImage(d?.image?.url || null)).catch(() => {});
    campApiCall(`/api/marketing/campanhas/${campanha.id}/briefing-summary`).then(d => setSummaries(d || {})).catch(() => {});
  }, [campanha?.id]);

  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 BSection = ({ title, children }) => (
    <div style={{ background: '#fff', border: '1px solid var(--border, #e2e8f0)', borderRadius: 10, padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 14 }}>
      <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--text-dim, #94a3b8)', fontFamily: 'var(--font-mono, monospace)', paddingBottom: 10, borderBottom: '1px solid var(--border, #e2e8f0)' }}>{title}</div>
      {children}
    </div>
  );

  const BRow = ({ label, value }) => value ? (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
      <div style={{ fontSize: 10, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--text-dim, #94a3b8)', fontFamily: 'var(--font-mono, monospace)' }}>{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;

  if (!campanha) return <div style={{ fontSize: 13, color: 'var(--text-muted)', padding: 24 }}>A carregar…</div>;
  if (!briefing) return <div style={{ fontSize: 13, color: 'var(--text-muted)', padding: 24 }}>Briefing não encontrado.</div>;

  const lSt = { fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--text-dim, #94a3b8)', fontFamily: 'var(--font-mono, monospace)', marginBottom: 3 };
  const vSt = { fontSize: 13, color: 'var(--text, #1d2e38)', lineHeight: 1.55, fontFamily: 'var(--font-body, Inter, sans-serif)' };
  const OBJ  = { awareness: 'Awareness', lead_gen: 'Geração de leads', conversion: 'Conversão', retention: 'Retenção / Upsell' };
  const MECH = { digirent: 'Digirent', printplan: 'PrintPlan', voucher: 'Voucher on Demand', direct_discount: 'Desconto Directo', trade_in: 'Trade-In' };
  const TIER = { standard: 'Standard', premium: 'Premium', tailor_made: 'Tailor-Made', tailor_made_rappel: 'TM Rappel', tailor_made_renda: 'TM Renda' };
  const CHAN_L = { meta_ads:'Meta Ads', linkedin_ads:'LinkedIn', google_ads_search:'Google Search', google_ads_display:'Google Display', email:'Email', whatsapp:'WhatsApp', website:'Website', muppi_led:'LED', linkedin:'LinkedIn', google_ads:'Google Ads', instagram:'Instagram', facebook:'Facebook' };
  const MECANICA_COLOR = { digirent:'#7C3AED', printplan:'#D97706', voucher:'#0EA5E9', direct_discount:'#DC2626', trade_in:'#059669' };

  const b1 = briefing.block1 || {};
  const b2 = briefing.block2 || {};
  const b4 = briefing.block4 || {};
  const b5 = briefing.block5 || {};

  const hasRestrictions = [b5.prohibited_claims, b5.off_brand_messages, b5.competitors_not_name, b5.out_of_scope_markets, b5.other_restrictions].some(v => Array.isArray(v) && v.length);

  const productName = b1.commercial_name || campanha?.titulo || '';
  const pitch       = b1.elevator_pitch || '';
  const usps        = (b1.usps || []).filter(Boolean).slice(0, 3);
  const decisor     = b2.decision_maker || '';
  const pain        = (b2.pain_points || []).filter(Boolean)[0] || '';
  const channels    = (b4.channels || []).map(ch => CHAN_L[ch] || ch).filter(Boolean);
  const period      = [b4.timeline_start, b4.timeline_end].filter(Boolean).join(' → ');
  const keyMsg      = b4.key_message || '';
  const offer       = b4.commercial_offer
    ? (typeof b4.commercial_offer === 'string' ? JSON.parse(b4.commercial_offer) : b4.commercial_offer)
    : null;

  const restrictions = [
    ...(b5.prohibited_claims    || []).filter(Boolean),
    ...(b5.off_brand_messages   || []).filter(Boolean),
    ...(b5.competitors_not_name || []).filter(Boolean),
  ];

  const Chip = ({ children, color }) => (
    <span style={{
      fontSize: 11, fontWeight: 500, padding: '3px 10px', borderRadius: 99,
      background: (color || bCol) + '12', color: color || bCol,
      fontFamily: 'var(--font-body, Inter, sans-serif)', whiteSpace: 'nowrap',
    }}>{children}</span>
  );

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

  const capSt = { fontSize: 10, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.06em', color: 'var(--fg-3, #94A4C4)', fontFamily: 'var(--font-text, Inter, sans-serif)', marginBottom: 4 };

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

      {/* ── CARD PRODUTO ── */}
      <div style={{ background: 'var(--bg-surface, #fff)', borderRadius: 'var(--radius-xl, 14px)', boxShadow: 'var(--shadow-card)', overflow: 'hidden', display: 'flex' }}>

        {/* Coluna imagem — estreita */}
        <div style={{ width: 180, flexShrink: 0, background: 'var(--dd-blue-100, #EBEFF9)', display: 'flex', alignItems: 'center', justifyContent: 'center', borderRight: '1px solid var(--border-1, #ECEFF5)', padding: 16 }}>
          {heroImage
            ? <img src={heroImage} alt={productName} style={{ maxWidth: '100%', maxHeight: 140, objectFit: 'contain', display: 'block' }} />
            : <span style={{ fontSize: 10, color: 'var(--fg-3)', fontFamily: 'var(--font-mono)' }}>SEM IMAGEM</span>
          }
        </div>

        {/* Coluna texto */}
        <div style={{ flex: 1, padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 12, justifyContent: 'center' }}>

          {/* Nome + pitch truncado */}
          <div style={{ borderLeft: `3px solid ${bCol}`, paddingLeft: 12 }}>
            <div style={{ fontSize: 16, fontWeight: 700, color: 'var(--dd-primary-900, #112954)', fontFamily: 'var(--font-sans, Montserrat, sans-serif)', lineHeight: 1.25, marginBottom: 4 }}>
              {productName}
            </div>
            {pitch && (
              <div style={{ fontSize: 12, color: 'var(--fg-2, #404968)', fontFamily: 'var(--font-text, Inter, sans-serif)', lineHeight: 1.5, display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}>
                {pitch}
              </div>
            )}
          </div>

          {/* Chips */}
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
            {channels.slice(0, 5).map((ch, i) => <Chip key={i}>{ch}</Chip>)}
            {period && <Chip color="var(--fg-3)">{period}</Chip>}
            {offer && <Chip color={MECANICA_COLOR[offer.type] || 'var(--fg-3)'}>{MECH[offer.type] || offer.type}{offer.tier ? ` · ${TIER[offer.tier] || offer.tier}` : ''}</Chip>}
          </div>
        </div>
      </div>

      {/* ── OFERTA COMERCIAL ── */}
      {offer && offer.type && offer.type !== 'sem_oferta_especifica' && (() => {
        const offerColor = MECANICA_COLOR[offer.type] || bCol;
        const tierLabels = { standard: 'Standard', premium: 'Premium', large: 'Large', enterprise: 'Enterprise', tailor_made: 'Tailor-Made' };
        const ctaLabels  = { demo_request: 'Pedido de demonstração', sample_request: 'Pedido de amostra', quote_request: 'Pedido de orçamento', call_back: 'Callback comercial' };
        return (
          <div style={{ background: 'var(--bg-surface,#fff)', borderRadius: 'var(--radius-xl,14px)', boxShadow: 'var(--shadow-card)', padding: '16px 20px', borderLeft: '4px solid #3859D0' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12, paddingBottom: 10, borderBottom: '1px solid var(--border-1,#ECEFF5)' }}>
              <span style={{ fontSize: 10, fontWeight: 700, color: '#fff', background: '#3859D0', borderRadius: 'var(--radius-xs,4px)', padding: '2px 8px', fontFamily: 'var(--font-mono)', letterSpacing: '0.04em', flexShrink: 0 }}>OFERTA COMERCIAL</span>
              <span style={{ fontSize: 13, fontWeight: 700, color: '#3859D0', fontFamily: 'var(--font-display, Montserrat, sans-serif)' }}>{MECH[offer.type] || offer.type}</span>
              {offer.tier && <span style={{ fontSize: 10, fontWeight: 600, color: 'var(--fg-3,#94A4C4)', fontFamily: 'var(--font-mono)', padding: '2px 7px', borderRadius: 4, background: 'var(--border-1,#ECEFF5)' }}>{tierLabels[offer.tier] || offer.tier}</span>}
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {offer.details && (
                <div style={{ fontSize: 13, color: 'var(--fg-2,#404968)', fontFamily: 'var(--font-text,Inter,sans-serif)', lineHeight: 1.6 }}>{offer.details}</div>
              )}
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 16, marginTop: 4 }}>
                {offer.primary_cta && (
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
                    <span style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--fg-3,#94A4C4)', fontFamily: 'var(--font-mono)' }}>CTA Principal</span>
                    <span style={{ fontSize: 12, color: 'var(--fg-1,#283252)', fontFamily: 'var(--font-text,Inter,sans-serif)', fontWeight: 500 }}>{ctaLabels[offer.primary_cta] || offer.primary_cta}</span>
                  </div>
                )}
                <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
                  <span style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--fg-3,#94A4C4)', fontFamily: 'var(--font-mono)' }}>Negociável</span>
                  <span style={{ fontSize: 12, color: 'var(--fg-1,#283252)', fontFamily: 'var(--font-text,Inter,sans-serif)', fontWeight: 500 }}>{offer.negotiable ? 'Sim — condições finais fecham com o comercial' : 'Não'}</span>
                </div>
              </div>
            </div>
          </div>
        );
      })()}

      {/* ── BLOCOS B1–B5 — resumos AI ── */}
      {summaries && (() => {
        // Parser **bold** → <strong> com cor da marca
        const renderText = (text, isWarning) => {
          if (!text) return null;
          const boldColor = isWarning ? 'var(--fg-danger,#B53B3B)' : 'var(--dd-primary-900,#112954)';
          const parts = text.split(/\*\*([^*]+)\*\*/g);
          return parts.map((part, i) =>
            i % 2 === 1
              ? <strong key={i} style={{ fontWeight: 700, color: boldColor }}>{part}</strong>
              : part
          );
        };

        const tSt = { fontSize: 13, color: 'var(--fg-2,#404968)', fontFamily: 'var(--font-text,Inter,sans-serif)', lineHeight: 1.7, margin: 0 };

        const BCard = ({ num, title, text, isWarning }) => !text ? null : (
          <div style={{ background: 'var(--bg-surface,#fff)', borderRadius: 'var(--radius-xl,14px)', boxShadow: 'var(--shadow-card)', padding: '16px 20px' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10, paddingBottom: 10, borderBottom: '1px solid var(--border-1,#ECEFF5)' }}>
              <span style={{ fontSize: 10, fontWeight: 700, color: '#fff', background: isWarning ? 'var(--fg-danger,#B53B3B)' : bCol, borderRadius: 'var(--radius-xs,4px)', padding: '2px 7px', fontFamily: 'var(--font-mono)', letterSpacing: '0.04em', flexShrink: 0 }}>0{num}</span>
              <span style={{ fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--fg-1,#283252)', fontFamily: 'var(--font-text,Inter,sans-serif)' }}>{title}</span>
            </div>
            <p style={tSt}>{renderText(text, isWarning)}</p>
          </div>
        );

        // B6 — Score do briefing
        const apr = summaries.apreciation || {};
        const breakdown = apr.breakdown || {};
        const scoreTotal = apr.scoreTotal;
        const hasScore = scoreTotal !== null && scoreTotal !== undefined;

        const ScoreBar = ({ score }) => {
          const col = score >= 85 ? 'var(--green-700,#1F8A52)' : score >= 70 ? 'var(--pumpkin-700,#E96D17)' : 'var(--red-700,#B53B3B)';
          return (
            <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
              <div style={{ flex: 1, height: 4, background: 'var(--border-1,#ECEFF5)', borderRadius: 99, overflow: 'hidden' }}>
                <div style={{ width: `${score}%`, height: '100%', background: col, borderRadius: 99, transition: 'width .4s ease' }} />
              </div>
              <span style={{ fontSize: 11, fontWeight: 700, color: col, fontFamily: 'var(--font-mono)', minWidth: 32 }}>{score}</span>
            </div>
          );
        };

        const BLOCK_LABELS = { b1: 'Produto', b2: 'Cliente', b3: 'Mercado', b4: 'Campanha', b5: 'Restrições' };

        return (
          <>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
              <BCard num={1} title="Produto"      text={summaries.b1} />
              <BCard num={2} title="Cliente-alvo" text={summaries.b2} />
            </div>
            <BCard num={3} title="Mercado"    text={summaries.b3} />
            <BCard num={4} title="Campanha"   text={summaries.b4} />
            <BCard num={5} title="Restrições" text={summaries.b5} isWarning />

            {/* B6 — Avaliação Digi AI */}
            {(hasScore || summaries.b6) && (
              <div style={{ background: 'var(--bg-surface,#fff)', borderRadius: 'var(--radius-xl,14px)', boxShadow: 'var(--shadow-card)', padding: '16px 20px' }}>
                {/* Header */}
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10, paddingBottom: 10, borderBottom: '1px solid var(--border-1,#ECEFF5)' }}>
                  <span style={{ fontSize: 10, fontWeight: 700, color: '#fff', background: 'var(--purple-700,#5B43C5)', borderRadius: 'var(--radius-xs,4px)', padding: '2px 7px', fontFamily: 'var(--font-mono)', letterSpacing: '0.04em' }}>06</span>
                  <span style={{ fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--fg-1,#283252)', fontFamily: 'var(--font-text,Inter,sans-serif)' }}>Avaliação Digi AI</span>
                  {hasScore && (
                    <span style={{ marginLeft: 'auto', fontSize: 18, fontWeight: 700, color: scoreTotal >= 85 ? 'var(--green-700,#1F8A52)' : scoreTotal >= 70 ? 'var(--pumpkin-700,#E96D17)' : 'var(--red-700,#B53B3B)', fontFamily: 'var(--font-mono)' }}>
                      {scoreTotal}<span style={{ fontSize: 11, fontWeight: 500, color: 'var(--fg-3)' }}>/100</span>
                    </span>
                  )}
                </div>
                {/* Resumo texto */}
                {summaries.b6 && (
                  <p style={{ ...tSt, marginBottom: 12 }}>{renderText(summaries.b6)}</p>
                )}
              </div>
            )}
          </>
        );
      })()}
    </div>
  );
};

// ── PhaseLoadingState — loading state consistente com steps + progress + tempo ─
// Usado durante gerações AI (Estratégia, Conceito, Segmentação). Cada fase passa
// os seus próprios steps + activeStep controlado pelo parent via SSE ou timing.
const PhaseLoadingState = ({ steps, activeStep, elapsedSec, error, brandColor = '#5B43C5' }) => {
  const [stepPct, setStepPct] = React.useState(0);
  const stepStartRef = React.useRef(null);

  React.useEffect(() => {
    if (activeStep <= 0) { setStepPct(0); return; }
    stepStartRef.current = Date.now();
    setStepPct(0);
  }, [activeStep]);

  React.useEffect(() => {
    if (activeStep <= 0) return;
    const def = steps.find(s => s.step === activeStep);
    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);
  }, [activeStep, steps]);

  const totalSteps = steps.length;
  const doneSteps  = steps.filter(s => s.step < activeStep).length;
  const overallPct = Math.round(((doneSteps + stepPct / 100) / totalSteps) * 100);
  const fmtElapsed = s => s < 60 ? `${s}s` : `${Math.floor(s/60)}m${String(s%60).padStart(2,'0')}s`;

  const Dot = ({ done, active }) => {
    if (done) return (
      <div style={{ width: 16, height: 16, borderRadius: '50%', background: 'rgba(21,128,61,.12)', border: '1.5px solid rgba(21,128,61,.3)', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <svg width="8" height="8" viewBox="0 0 10 10" fill="none"><polyline points="1.5,5 4,7.5 8.5,2.5" stroke="#15803d" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>
      </div>
    );
    if (active) return (
      <div style={{ width: 16, height: 16, borderRadius: '50%', border: `2px solid rgba(91,67,197,.25)`, borderTopColor: brandColor, flexShrink: 0, animation: 'espin 0.7s linear infinite' }} />
    );
    return <div style={{ width: 16, height: 16, borderRadius: '50%', border: '1.5px solid var(--border, #e2e8f0)', flexShrink: 0 }} />;
  };

  return (
    <div style={{ background: 'var(--bg-elev,#fff)', border: '1px solid var(--border, #e2e8f0)', borderRadius: 10, padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 16 }}>
      <style>{`
        @keyframes espin { to { transform: rotate(360deg); } }
      `}</style>
      {/* Aviso: não fechar */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 12px', background: '#fffbeb', border: '1px solid #fde68a', borderRadius: 6 }}>
        <span style={{ fontSize: 13 }}>⚠</span>
        <span style={{ fontSize: 12, color: '#92400e', fontWeight: 500 }}>Não feches esta janela nem saias deste ecrã — a geração está em curso.</span>
      </div>
      {/* Barra de progresso + tempo */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
        <div style={{ flex: 1, height: 5, borderRadius: 99, background: 'var(--bg-app,#f5f6f8)', overflow: 'hidden' }}>
          <div style={{ height: '100%', borderRadius: 99, background: `linear-gradient(90deg,${brandColor},${brandColor}cc)`, width: `${overallPct}%`, transition: 'width 0.15s linear' }} />
        </div>
        <span style={{ fontSize: 11, fontWeight: 700, color: brandColor, fontFamily: 'var(--font-mono)', flexShrink: 0, minWidth: 32 }}>{overallPct}%</span>
        <span style={{ fontSize: 11, fontWeight: 600, color: '#94a3b8', fontFamily: 'var(--font-mono)', flexShrink: 0, minWidth: 36 }}>{fmtElapsed(elapsedSec || 0)}</span>
      </div>
      {/* Tabela de steps */}
      <table style={{ width: '100%', borderCollapse: 'collapse', tableLayout: 'fixed' }}>
        <colgroup><col style={{ width: 28 }} /><col style={{ width: 240 }} /><col /></colgroup>
        <thead>
          <tr>
            <th />
            <th style={{ padding: '4px 8px', fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#94a3b8', fontFamily: 'var(--font-mono)', textAlign: 'left' }}>SECÇÃO</th>
            <th style={{ padding: '4px 8px', fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#94a3b8', fontFamily: 'var(--font-mono)', textAlign: 'left' }}>DETALHE</th>
          </tr>
        </thead>
        <tbody>
          {steps.map(s => {
            const done   = activeStep > s.step;
            const active = activeStep === s.step;
            return (
              <tr key={s.step}>
                <td style={{ padding: '8px 8px 8px 0', verticalAlign: 'middle' }}><Dot done={done} active={active} /></td>
                <td style={{ padding: '8px', fontSize: 13, fontWeight: active ? 600 : 400, color: done ? '#15803d' : active ? 'var(--text)' : '#94a3b8', fontFamily: 'var(--font-body, Inter, sans-serif)', borderBottom: '1px solid var(--border, #f1f5f9)' }}>{s.label}</td>
                <td style={{ padding: '8px', fontSize: 12, color: active ? '#64748b' : '#cbd5e1', fontFamily: 'var(--font-body, Inter, sans-serif)', borderBottom: '1px solid var(--border, #f1f5f9)' }}>{s.detail}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
      {error && <div style={{ padding: '8px 12px', background: 'rgba(220,38,38,.06)', border: '1px solid rgba(220,38,38,.2)', borderRadius: 6, fontSize: 12, color: '#dc2626' }}>{error}</div>}
    </div>
  );
};

// Steps para cada fase — usados por PhaseLoadingState
const CONCEITO_STEPS = [
  { step: 1, label: 'Estratégia & Briefing',      detail: 'Leitura da estratégia aprovada + 5 blocos do briefing · guardrails do bloco 5',                                  duration: 3000 },
  { step: 2, label: 'USPs × Dores × Personas',    detail: 'Mapeamento 1:1 dos USPs às dores dominantes e personas priorizadas por mercado',                                 duration: 4000 },
  { step: 3, label: 'Big Idea & Ângulos por USP', detail: 'Claude · big idea unificada · 1 ângulo por USP (cada um integra as 3 fases do funil na mesma peça)',              duration: 22000 },
  { step: 4, label: 'Abordagem por Canal',        detail: 'Direcção criativa por canal — email (sequência) · website (blog + página produto) · Ads (formatos) · WhatsApp',   duration: 10000 },
  { step: 5, label: 'Hooks & Cronograma',         detail: 'Hooks por canal · cronograma orgânico (comm_plan) — que passa depois para Planeamento',                          duration: 8000 },
  { step: 6, label: 'Variantes por Mercado',      detail: 'Reinterpretação idiomática por país (não tradução literal) · tom cultural · ângulos locais',                     duration: 8000 },
  { step: 7, label: 'Guardar Conceito',           detail: 'Persistência do proposta_json · actualização de status',                                                          duration: 1500 },
];

// Steps para Orçamento — determinístico + análise Meta Ads opcional
const ORCAMENTO_STEPS_WITH_META = [
  { step: 1, label: 'Briefing & Estratégia',       detail: 'Targets briefing (leads · CPL · CTR) · budget_weight por mercado · USPs · timeline',             duration: 2000  },
  { step: 2, label: 'Histórico Meta Ads',          detail: 'listCampaigns + insights 180 dias · filtro por produto · CPL ponderado por spend',               duration: 25000 },
  { step: 3, label: 'Fórmula 3 Âncoras',          detail: 'CPL blended (briefing + histórico + KB B2B PT/ES) · margem por confiança · factor CTR · Haiku',  duration: 8000  },
  { step: 4, label: 'Distribuição por Canal',      detail: 'Paid media only · budget_weight estratégia por mercado · split canal por pct KB',                duration: 2000  },
  { step: 5, label: 'Guardar Orçamento',           detail: 'campanha_orcamento por country × canal · supersede rows anteriores',                             duration: 1500  },
  { step: 6, label: 'Meta Ads Analyst — Dados',    detail: 'Skills: ad-creative · data-analyst · forecast · meta-ads-b2b · histórico filtrado',             duration: 8000  },
  { step: 7, label: 'Meta Ads Analyst — Análise',  detail: 'Claude Sonnet 4.6 — diagnóstico · orçamento · recomendações · previsão · campanhas analisadas', duration: 25000 },
];
const ORCAMENTO_STEPS_NO_META = [
  { step: 1, label: 'Briefing & Estratégia',       detail: 'Targets briefing (leads · CPL · CTR) · budget_weight por mercado · USPs · timeline',             duration: 2000 },
  { step: 2, label: 'Distribuição por Canal',      detail: 'Paid media only · budget_weight estratégia por mercado · split canal por pct KB',                duration: 2000 },
  { step: 3, label: 'Guardar Orçamento',           detail: 'campanha_orcamento por country × canal · supersede rows anteriores',                             duration: 1500 },
];

// ── PhaseEmptyState — empty state consistente para todas as fases ─────────────
// Segue o mesmo padrão da Estratégia (empty state): card branco centrado com
// eyebrow label (fase X), title, descrição e CTA primário. Usado nos tabs
// Orçamento · Segmentação · Planeamento · Funil.
const PhaseEmptyState = ({ label, title, description, ctaLabel, onCta, disabled, disabledReason, error, loading, secondaryCta }) => (
  <div style={{ background: 'var(--bg-surface,#fff)', borderRadius: 'var(--radius-xl,14px)', boxShadow: 'var(--shadow-card, 0 1px 3px rgba(17,41,84,.08), 0 4px 16px rgba(17,41,84,.06))', padding: '32px 28px', textAlign: 'center' }}>
    <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'var(--fg-3, var(--text-dim))', fontFamily: 'var(--font-mono)', marginBottom: 8 }}>
      {label}
    </div>
    <div style={{ fontSize: 20, fontWeight: 700, color: 'var(--dd-primary-900, var(--text, #112954))', fontFamily: 'var(--font-display, Montserrat, sans-serif)', marginBottom: 10 }}>
      {title}
    </div>
    <p style={{ fontSize: 13, color: 'var(--fg-2, var(--text-muted))', lineHeight: 1.6, maxWidth: 560, margin: '0 auto 20px', fontFamily: 'var(--font-text, Inter, sans-serif)' }}>
      {description}
    </p>
    <div style={{ display: 'flex', gap: 8, justifyContent: 'center', flexWrap: 'wrap' }}>
      {onCta && (
        <button onClick={onCta} disabled={disabled || loading} className="btn btn-ai" style={{ padding: '10px 24px', fontSize: 13 }}>
          {loading ? 'A gerar...' : ctaLabel}
        </button>
      )}
      {secondaryCta && (
        <button onClick={secondaryCta.onClick} className="btn" style={{ padding: '10px 20px', fontSize: 13 }}>
          {secondaryCta.label}
        </button>
      )}
    </div>
    {disabled && disabledReason && (
      <div style={{ marginTop: 14, fontSize: 12, color: 'var(--text-muted)', fontStyle: 'italic' }}>{disabledReason}</div>
    )}
    {error && <div style={{ marginTop: 14, fontSize: 12, color: '#dc2626' }}>{error}</div>}
  </div>
);

// ── TabPlaneamento — timeline por canal + estrutura de anúncios Meta ─────────
const TabPlaneamento = ({ campanha, copy, prompts, userEmail }) => {
  const bCol = _brandColor(campanha?.brand_slug);
  const [rows,      setRows]      = React.useState([]);
  const [loading,   setLoading]   = React.useState(true);
  const [generating,setGenerating]= React.useState(false);
  const [approving, setApproving] = React.useState(false);
  const [saving,    setSaving]    = React.useState({});
  const [toast,     setToast]     = React.useState('');
  const [genStep,   setGenStep]   = React.useState(0);
  const [genElapsed,setGenElapsed]= React.useState(0);
  const [ganttZoom, setGanttZoom] = React.useState('days');
  const [tooltip,   setTooltip]   = React.useState(null); // { x, y, gr } // 'days' | 'weeks' | 'months'
  const planTimersRef  = React.useRef([]);
  const planElapsedRef = React.useRef(null);

  const showToast = msg => { setToast(msg); setTimeout(() => setToast(''), 2500); };

  // Canais activos para steps dinâmicos
  const planChannels = new Set((campanha?.estrategia_json?.markets || []).flatMap(m => (m.channel_mix||[]).map(ch => ch.canal)));
  const hasPaidAds = [...planChannels].some(c => ['meta_ads','linkedin_ads','google_ads_search','google_ads_display'].includes(c));
  const hasEmail   = planChannels.has('email');
  const hasSocial  = [...planChannels].some(c => ['instagram','facebook','linkedin'].includes(c));
  const hasBlog    = planChannels.has('website');

  const PLAN_STEPS = [
    { label: 'Briefing & Estratégia',          detail: 'Timeline · canais activos · mercados · objectivo · comm_plan do conceito',                   duration: 2000 },
    { label: 'Âncora temporal',                detail: `Data efectiva = max(${campanha?.briefing?.timeline_start || 'hoje'}, hoje + 5 dias)`,          duration: 1500 },
    { label: 'Comunicação interna',            detail: 'Briefing equipa comercial 2 dias antes do primeiro conteúdo externo (email + WA + portal)',    duration: 2000 },
    ...(hasPaidAds ? [{ label: 'Paid Ads — datas',       detail: 'Meta/LinkedIn/Google · período = briefing timeline · múltiplas campanhas simultâneas permitidas', duration: 3000 }] : []),
    ...(hasEmail   ? [{ label: 'Email — anti-saturação', detail: 'Máx 1 email/semana por marca · intercalação cross-marca (Mimaki/Decal/BIOND) · conflito = semana seguinte', duration: 4000 }] : []),
    ...(hasSocial  ? [{ label: 'Social orgânico',        detail: 'Mínimo 2 dias entre posts no mesmo perfil · produtos diferentes podem intercalar',   duration: 4000 }] : []),
    ...(hasBlog    ? [{ label: 'Blog & Website',          detail: 'Data de publicação · website por marca+mercado+língua',                              duration: 2000 }] : []),
    { label: 'Conflitos cross-campanha',       detail: 'Verifica existing_schedule de todas as marcas · marca itens needs_review se irresolvível',    duration: 5000 },
    { label: 'Guardar Planeamento',            detail: 'campanha_plano_datas por canal × mercado · slots vazios visíveis no Gantt',                   duration: 1500 },
  ].map((s, i) => ({ ...s, step: i + 1 }));

  const load = () => {
    setLoading(true);
    campApiCall(`/api/marketing/campanhas/${campanha.id}/planeamento`)
      .then(d => setRows(d.rows || []))
      .finally(() => setLoading(false));
  };
  React.useEffect(() => { if (campanha?.id) load(); }, [campanha?.id]);
  React.useEffect(() => () => { clearInterval(planElapsedRef.current); planTimersRef.current.forEach(clearTimeout); }, []);

  const generate = async () => {
    setGenerating(true);
    setGenStep(1); setGenElapsed(0);
    planElapsedRef.current = setInterval(() => setGenElapsed(s => s + 1), 1000);
    let cum = 0;
    planTimersRef.current = PLAN_STEPS.map(s => {
      cum += s.duration; return setTimeout(() => setGenStep(s.step + 1 <= PLAN_STEPS.length ? s.step + 1 : PLAN_STEPS.length), cum);
    });
    const cleanup = () => { clearInterval(planElapsedRef.current); planTimersRef.current.forEach(clearTimeout); setGenStep(0); setGenElapsed(0); };
    try {
      const d = await campApiCall(`/api/marketing/campanhas/${campanha.id}/planeamento/generate`, { method: 'POST' });
      cleanup();
      setRows(d.rows || []);
      showToast('Planeamento gerado');
    } catch(e) { cleanup(); showToast('Erro: ' + e.message); }
    setGenerating(false);
  };

  const approve = async () => {
    if (!window.confirm('Aprovar planeamento? A campanha avança para revisão do Funil Multicanal.')) return;
    setApproving(true);
    try {
      await campApiCall(`/api/marketing/campanhas/${campanha.id}/planeamento/approve`, {
        method: 'POST', body: JSON.stringify({ user_email: userEmail })
      });
      setRows(rs => rs.map(r => ({ ...r, approved_at: new Date().toISOString(), approved_by: userEmail })));
      showToast('Planeamento aprovado');
    } catch(e) { showToast('Erro: ' + e.message); }
    setApproving(false);
  };

  const updateRow = async (row, field, val) => {
    const updated = { ...row, [field]: val };
    // Validação local: end_date não pode ser anterior a start_date
    if ((field === 'start_date' || field === 'end_date') && updated.start_date && updated.end_date) {
      if (new Date(updated.end_date) < new Date(updated.start_date)) {
        showToast('Data fim tem de ser posterior à data início');
        return;
      }
    }
    setRows(rs => rs.map(r => r.id === row.id ? updated : r));
    setSaving(s => ({ ...s, [row.id]: true }));
    try {
      await campApiCall(`/api/marketing/campanhas/${campanha.id}/planeamento/${row.id}`, {
        method: 'PUT',
        body: JSON.stringify({ start_date: updated.start_date, end_date: updated.end_date, budget_fase: updated.budget_fase, notas: updated.notas })
      });
      setSaving(s => ({ ...s, [row.id]: false }));
    } catch(e) { showToast('Erro ao guardar: ' + (e.message || '')); setSaving(s => ({ ...s, [row.id]: false })); }
  };

  // Calcular duração de cada canal em semanas — nunca negativa
  const durationWeeks = (r) => {
    if (!r.start_date || !r.end_date) return null;
    const diff = (new Date(r.end_date) - new Date(r.start_date)) / (7 * 864e5);
    return diff < 0 ? 0 : Math.round(diff);
  };

  const thS = { fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', letterSpacing: '0.06em', textTransform: 'uppercase', color: 'var(--text-dim)', padding: '8px 14px', background: 'var(--bg-sunken)', borderBottom: '1px solid var(--border)', whiteSpace: 'nowrap' };
  const tdS = { fontSize: 12, padding: '9px 14px', borderBottom: '1px solid var(--border)', verticalAlign: 'middle' };
  const inpS = { fontSize: 12, padding: '4px 8px', borderRadius: 5, border: '1px solid var(--border)', background: 'var(--bg)', color: 'var(--text)', fontFamily: 'var(--font-mono)' };

  const hasAnuncios = !!(campanha?.proposta_json?.comm_plan?.length || campanha?.canais_setup);

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
      {/* Status strip + actions — padrão consistente */}
      {rows.length > 0 && (() => {
        const allApproved = rows.every(r => r.approved_at);
        return (
          <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: allApproved ? 'var(--green-100,#E1F7E6)' : 'var(--dd-blue-100,#EBEFF9)', color: allApproved ? 'var(--green-700,#1F8A52)' : 'var(--dd-primary-600,#3859D0)' }}>
              {allApproved ? 'Aprovado' : 'Gerado — aguarda aprovação'}
            </span>
            <span style={{ fontSize: 11, color: 'var(--fg-3, var(--text-muted))' }}>
              {rows.length} canais planeados
            </span>
            <div style={{ marginLeft: 'auto', display: 'flex', gap: 6, alignItems: 'center' }}>
              <button className="btn" onClick={generate} disabled={generating} style={{ height: 28, padding: '0 12px', fontSize: 12 }}>
                {generating ? 'A gerar...' : 'Regenerar'}
              </button>
              {!allApproved && (
                <button className="btn btn-ai" onClick={approve} disabled={approving} style={{ height: 28, padding: '0 14px', fontSize: 12 }}>
                  {approving ? 'A aprovar...' : 'Aprovar Planeamento'}
                </button>
              )}
            </div>
          </div>
        );
      })()}

      {/* ── Loading state com steps das regras anti-saturação ── */}
      {generating && (
        <PhaseLoadingState
          steps={PLAN_STEPS}
          activeStep={genStep}
          elapsedSec={genElapsed}
          brandColor="#7c3aed"
        />
      )}

      {/* ── Timeline integrada — todos os eventos da campanha por data ── */}
      {loading && !generating && <div style={{ padding: 32, color: 'var(--text-muted)', fontSize: 13 }}>A carregar...</div>}

      {!loading && !generating && rows.length === 0 && (
        <PhaseEmptyState
          label="Planeamento · fase 5"
          title="Ainda sem planeamento definido"
          description="Timeline integrada de todos os eventos da campanha: Meta Ads, Email, Social Orgânico, Blog, WA. Datas calculadas com base no briefing respeitando regras de anti-saturação — mínimo 2 dias entre posts orgânicos no mesmo perfil, máximo 1 email/semana por marca, intercalação entre marcas do ecossistema."
          ctaLabel="Gerar Planeamento →"
          onCta={generate}
          loading={generating}
          disabled={!campanha?.estrategia_json}
          disabledReason={!campanha?.estrategia_json ? 'A estratégia precisa de ser gerada primeiro.' : null}
        />
      )}

      {!loading && !generating && rows.length > 0 && (() => {
        const commPlan = campanha?.proposta_json?.comm_plan || [];

        // ── GANTT ─────────────────────────────────────────────────────────────
        const today = new Date(); today.setHours(0,0,0,0);
        const toD = (s) => { const d = new Date(s); d.setHours(0,0,0,0); return d; };
        const fmtD = (d) => d.toLocaleDateString('pt-PT', { day:'2-digit', month:'short' });

        // Construir linhas do Gantt — uma por canal/actividade
        const GANTT_COLOR = {
          meta_ads:'#3859D0', linkedin_ads:'#0a66c2', google_ads_search:'#ea4335',
          google_ads_display:'#34a853', email:'#7c3aed', email_externo:'#7c3aed',
          whatsapp:'#16a34a', instagram:'#e1306c', facebook:'#1877f2', linkedin:'#0a66c2',
          blog:'#0891b2', website:'#0891b2', youtube:'#ff0000',
          email_interno:'#92400e', muppi_led:'#d97706', default:'#6b7280',
        };

        // Linhas de planeamento (canais com período)
        const ganttRows = rows
          .filter(r => r.start_date)
          .map(r => ({
            id: r.id, label: CANAL_LABEL[r.canal] || r.canal,
            canal: r.canal, color: GANTT_COLOR[r.canal] || GANTT_COLOR.default,
            start: toD(r.start_date), end: r.end_date ? toD(r.end_date) : toD(r.start_date),
            notas: r.notas, budget: r.budget_fase, tipo: 'periodo', row: r,
            needs_review: false,
          }));

        // Eventos pontuais do comm_plan (publicações, envios)
        // Simplificar labels para caber numa linha no Gantt
        const shortLabel = (item) => {
          const ct = item.content_type || '';
          const titulo = item.titulo || '';
          const body = item.body || '';
          if (ct === 'briefing_interno') return 'Briefing Comercial';
          if (ct === 'campanha_paid') return titulo; // naming convention já curto
          if (ct === 'email_html') {
            if (/kick.?off/i.test(titulo))  return 'Kick-off · Campanha';
            if (/reminder/i.test(titulo))   return 'Reminder · ROI';
            if (/last.?call/i.test(titulo)) return 'Urgência Fim Stock';
            return titulo.split(/[—–]/)[0].trim().slice(0, 28);
          }
          if (ct === 'social_post' || ct === 'ad') {
            const adM = titulo.match(/AD-(\d+)/);
            const adNum = adM ? `AD-${adM[1].padStart(2,'0')}` : '';
            const words = body.replace(/[,.:;]/g,'').split(/\s+/).slice(0,3).join(' ');
            return adNum ? `${adNum} · ${words}` : words.slice(0,30);
          }
          if (ct === 'artigo_blog') {
            const words = (body || titulo).replace(/[,.:;]/g,'').split(/\s+/).slice(0,3).join(' ');
            return `Blog · ${words}`;
          }
          return titulo.split(/[—–]/)[0].trim().slice(0, 30);
        };

        const commRows = commPlan.map((item, i) => {
          const ds = item.planned_date || item.data_envio || item.data_publicacao;
          if (!ds) return null;
          const d = toD(ds.slice(0,10));
          const de = item.planned_end_date ? toD(item.planned_end_date.slice(0,10)) : d;
          const tipo = item.item_type || 'evento';
          const canal = item.canal || 'social';
          return {
            id: 'c_'+i,
            label: shortLabel(item),
            fullLabel: item.titulo || CANAL_LABEL[canal] || canal,
            fullBody: item.body || null,
            canal, color: GANTT_COLOR[canal] || GANTT_COLOR.default,
            start: d, end: de,
            notas: item.body?.slice(0,80),
            tipo,
            content_type: item.content_type || null,
            is_interno: item.content_type === 'briefing_interno',
            needs_review: item.needs_review || false,
          };
        }).filter(Boolean);

        // Canal "site/website" = sempre blog posts no planeamento (landing pages são produção)
        // Excluir content_types que pertencem à fase de produção, não ao planeamento de publicação
        const EXCLUDE_CONTENT_TYPES = new Set(['landing_page', 'landing_page_html', 'lp']);
        const filteredCommRows = commRows.filter(r => !EXCLUDE_CONTENT_TYPES.has(r.content_type));

        // Agrupar por categoria
        const CATEGORIES = [
          {
            id: 'interno', label: 'Comunicação Interna', color: '#92400e',
            match: (r) => r.canal?.includes('interno') || r.is_interno,
          },
          {
            id: 'paid', label: 'Paid Media', color: '#3859D0',
            match: (r) => ['meta_ads','linkedin_ads','google_ads_search','google_ads_display','muppi_led'].includes(r.canal)
              && r.content_type !== 'social_post',
          },
          {
            id: 'email', label: 'Email', color: '#7c3aed',
            match: (r) => ['email','email_externo','email_html'].includes(r.canal) || r.canal === 'email',
          },
          {
            id: 'social', label: 'Social Media', color: '#e1306c',
            match: (r) => ['instagram','facebook','linkedin','social'].includes(r.canal)
              || r.content_type === 'social_post',
          },
          {
            id: 'website', label: 'Blog', color: '#0891b2',
            match: (r) => ['site','website','blog','artigo_blog','youtube'].includes(r.canal) || ['artigo_blog','blog_post','artigo'].includes(r.content_type),
          },
        ];

        // Só comm_plan items — ganttRows (simples "Meta Ads", "Email") são redundantes com as categorias
        const allCandidates = [...filteredCommRows];
        // Agrupar e ordenar por categoria
        const grouped = CATEGORIES.map(cat => ({
          ...cat,
          rows: allCandidates.filter(r => cat.match(r)).sort((a,b) => a.start - b.start),
        })).filter(c => c.rows.length > 0);

        const allRows = grouped.flatMap(c => [{ ...c, isCategory: true }, ...c.rows]);
        if (!allRows.length) return <div style={{ padding: 32, color: 'var(--text-muted)', fontSize: 13 }}>Sem eventos planeados.</div>;

        // Janela temporal: 1 Jan 2026 → 31 Dez 2026 (calendário completo)
        const gStart = new Date(2026, 0, 1);
        const gEnd   = new Date(2026, 11, 31);
        const totalDays = Math.ceil((gEnd - gStart) / 864e5);

        // Pixels por dia conforme zoom
        const PX_PER_DAY = { days: 40, weeks: 14, months: 5 };
        const pxPerDay = PX_PER_DAY[ganttZoom] || 14;
        const totalWidth = Math.max(totalDays * pxPerDay, 600);

        // Posição em px no Gantt (não %)
        const px = (d) => Math.max(0, Math.min(totalWidth, ((d - gStart) / 864e5) * pxPerDay));
        const todayPx = px(today);

        // Meses — sempre visíveis como nível superior (independente do zoom)
        const monthMarkers = [];
        const mc = new Date(gStart.getFullYear(), gStart.getMonth(), 1);
        while (mc <= gEnd) {
          monthMarkers.push({
            key: mc.toISOString().slice(0,7),
            label: mc.toLocaleDateString('pt-PT', { month: 'long' }).charAt(0).toUpperCase() + mc.toLocaleDateString('pt-PT', { month: 'long' }).slice(1),
            px: px(mc),
            width: Math.max(0, px(new Date(mc.getFullYear(), mc.getMonth()+1, 1)) - px(mc)),
          });
          mc.setMonth(mc.getMonth() + 1);
        }

        // Helper: número da semana ISO do ano
        const getISOWeek = (d) => {
          const dt = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
          dt.setUTCDate(dt.getUTCDate() + 4 - (dt.getUTCDay() || 7));
          const y = new Date(Date.UTC(dt.getUTCFullYear(), 0, 1));
          return Math.ceil((((dt - y) / 864e5) + 1) / 7);
        };

        // Marcadores de detalhe (semanas ou dias) — nível inferior
        const headerMarkers = [];
        const cur = new Date(gStart);
        if (ganttZoom === 'days') {
          while (cur <= gEnd) {
            const isMonday = cur.getDay() === 1;
            const wk = isMonday ? getISOWeek(cur) : null;
            headerMarkers.push({
              key: cur.toISOString().slice(0,10),
              label: String(cur.getDate()),
              weekNum: wk, // mostra nº semana às segundas-feiras
              px: px(cur),
              isWeekend: cur.getDay()===0||cur.getDay()===6,
            });
            cur.setDate(cur.getDate() + 1);
          }
        } else if (ganttZoom === 'months') {
          // Zoom meses: mostrar semanas como nível inferior
          while (cur <= gEnd) {
            const mon = new Date(cur); mon.setDate(cur.getDate() - ((cur.getDay()+6)%7));
            if (!headerMarkers.length || headerMarkers[headerMarkers.length-1].key !== mon.toISOString().slice(0,10)) {
              const wk = getISOWeek(mon);
              headerMarkers.push({ key: mon.toISOString().slice(0,10), label: `S${wk}`, px: px(mon) });
            }
            cur.setDate(cur.getDate() + 7);
          }
        } else { // weeks (default)
          while (cur <= gEnd) {
            const mon = new Date(cur); mon.setDate(cur.getDate() - ((cur.getDay()+6)%7));
            if (!headerMarkers.length || headerMarkers[headerMarkers.length-1].key !== mon.toISOString().slice(0,10)) {
              const wk = getISOWeek(mon);
              headerMarkers.push({ key: mon.toISOString().slice(0,10), label: `S${wk} · ${fmtD(mon)}`, px: px(mon) });
            }
            cur.setDate(cur.getDate() + 7);
          }
        }

        const headerWeeks = headerMarkers; // alias
        const pct = (d) => px(d); // alias

        const ROW_H = 36;
        const LABEL_W = 280;

        // Scroll ref callback — só executa quando o zoom muda, não em re-renders do tooltip
        const scrollToToday = (el) => {
          if (el && el._ganttZoom !== ganttZoom) {
            el.scrollLeft = Math.max(0, todayPx - 200);
            el._ganttZoom = ganttZoom; // marcar como scrolled para este zoom
          }
        };

        return (
          <div style={{ background: 'var(--bg-card,#fff)', border: '1px solid var(--border)', borderRadius: 10, overflow: 'hidden' }}>
            {/* Toolbar zoom */}
            <div style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '6px 12px', borderBottom: '1px solid var(--border)', background: 'var(--bg-sunken)', justifyContent: 'flex-end' }}>
              <span style={{ fontSize: 10, color: 'var(--text-dim)', marginRight: 4 }}>Zoom:</span>
              {[['days','Dias'],['weeks','Semanas'],['months','Meses']].map(([z, l]) => (
                <button key={z} onClick={() => setGanttZoom(z)}
                  style={{ fontSize: 10, padding: '2px 10px', borderRadius: 4, cursor: 'pointer', border: `1px solid ${ganttZoom===z ? 'var(--ai-500,#3859D0)' : 'var(--border)'}`, background: ganttZoom===z ? 'var(--ai-500,#3859D0)' : 'var(--bg)', color: ganttZoom===z ? '#fff' : 'var(--text-muted)', fontWeight: ganttZoom===z ? 700 : 400 }}>
                  {l}
                </button>
              ))}
            </div>
            {/* Gantt — scroll lateral, labels sticky */}
            <div style={{ overflowX: 'auto' }} ref={scrollToToday}>
              <div style={{ width: LABEL_W + totalWidth, minWidth: LABEL_W + 400 }}>
                {/* Header dois níveis: meses (topo) + semanas/dias (baixo) */}
                <div style={{ position: 'sticky', top: 0, zIndex: 20 }}>
                  {/* Nível 1 — Meses */}
                  <div style={{ display: 'flex', borderBottom: '1px solid var(--border)', background: '#112954' }}>
                    <div style={{ width: LABEL_W, flexShrink: 0, borderRight: '1px solid rgba(255,255,255,.15)', position: 'sticky', left: 0, background: '#112954', zIndex: 21, height: 22, display:'flex', alignItems:'center', padding:'0 12px' }}>
                      <span style={{ fontSize:9, fontWeight:700, fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.06em', color:'rgba(255,255,255,.5)' }}>Canal / Actividade</span>
                    </div>
                    <div style={{ flex:1, position:'relative', height:22, overflow:'visible' }}>
                      {monthMarkers.map(m => (
                        <div key={m.key} style={{ position:'absolute', left:m.px, top:0, bottom:0, borderLeft:'1px solid rgba(255,255,255,.2)', paddingLeft:6, display:'flex', alignItems:'center', overflow:'hidden', width:m.width }}>
                          <span style={{ fontSize:10, fontWeight:700, fontFamily:'var(--font-mono)', color:'#fff', whiteSpace:'nowrap', letterSpacing:'.04em' }}>{m.label}</span>
                        </div>
                      ))}
                      <div style={{ position:'absolute', left:todayPx, top:0, bottom:0, width:pxPerDay, background:'rgba(239,68,68,.15)', zIndex:10 }} />
                    </div>
                  </div>
                  {/* Nível 2 — Semanas / Dias */}
                  <div style={{ display:'flex', borderBottom:'2px solid var(--border)', background:'var(--bg-sunken)' }}>
                    <div style={{ width:LABEL_W, flexShrink:0, borderRight:'1px solid var(--border)', position:'sticky', left:0, background:'var(--bg-sunken)', zIndex:21, height:20 }} />
                    <div style={{ flex:1, position:'relative', height:20, overflow:'visible' }}>
                      {/* Fins de semana no header */}
                      {ganttZoom === 'days' && headerMarkers.filter(w => w.isWeekend).map(w => (
                        <div key={'weh_'+w.key} style={{ position:'absolute', left:w.px, top:0, bottom:0, width:pxPerDay, background:'rgba(0,0,0,.025)' }} />
                      ))}
                      {headerMarkers.map(w => (
                        <div key={w.key} style={{ position:'absolute', left:w.px, top:0, bottom:0, borderLeft:'1px solid var(--border)', paddingLeft:3, display:'flex', flexDirection:'column', justifyContent:'center' }}>
                          {w.weekNum != null && (
                            <span style={{ fontSize:7, fontFamily:'var(--font-mono)', color:'var(--ai-500,#3859D0)', fontWeight:700, lineHeight:1, whiteSpace:'nowrap' }}>S{w.weekNum}</span>
                          )}
                          <span style={{ fontSize:8, fontFamily:'var(--font-mono)', color:'var(--text-dim)', whiteSpace:'nowrap', opacity: w.isWeekend ? 0.5 : 1, lineHeight:1 }}>{w.label}</span>
                        </div>
                      ))}
                      <div style={{ position:'absolute', left:todayPx, top:0, bottom:0, width:pxPerDay, background:'rgba(239,68,68,.12)', zIndex:10 }} />
                    </div>
                  </div>
                </div>{/* fim sticky header */}
                {/* Rows — categorias + actividades */}
                {allRows.map((gr, ri) => {
                if (gr.isCategory) return (
                  <div key={'cat_'+gr.id} style={{ display:'flex', borderBottom:'1px solid var(--border)', background:'var(--bg-sunken)', height:26 }}>
                    <div style={{ width:LABEL_W, flexShrink:0, padding:'0 14px', display:'flex', alignItems:'center', gap:6, borderRight:'1px solid var(--border)', position:'sticky', left:0, background:'var(--bg-sunken)', zIndex:10 }}>
                      <span style={{ width:3, height:12, borderRadius:2, background:gr.color, flexShrink:0 }} />
                      <span style={{ fontSize:10, fontWeight:700, fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.06em', color:gr.color }}>{gr.label}</span>
                    </div>
                    <div style={{ flex:1, position:'relative' }}>
                      {headerMarkers.map(w => <div key={w.key} style={{ position:'absolute', left:w.px, top:0, bottom:0, borderLeft:'1px solid var(--border)', opacity:.2 }} />)}
                      <div style={{ position:'absolute', left:todayPx, top:0, bottom:0, width:pxPerDay, background:'rgba(239,68,68,.08)' }} />
                    </div>
                  </div>
                );
                return (
                <div key={gr.id} style={{ display:'flex', borderBottom:'1px solid var(--border)', minHeight:ROW_H, background:'var(--bg-card,#fff)' }}>
                  {/* Label — sticky à esquerda */}
                  <div style={{ width:LABEL_W, flexShrink:0, padding:'0 12px 0 22px', display:'flex', alignItems:'center', gap:6, borderRight:'1px solid var(--border)', position:'sticky', left:0, background:'var(--bg-card,#fff)', zIndex:10 }}>
                    <span style={{ width:6, height:6, borderRadius:'50%', background:gr.is_interno?'#92400e':gr.color, flexShrink:0 }} />
                    <div style={{ fontSize:11, fontWeight:600, color:gr.is_interno?'#92400e':'var(--text)', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{gr.label}</div>
                  </div>
                  {/* Barra Gantt */}
                  <div style={{ flex:1, position:'relative', minHeight:ROW_H }}>
                    {/* Grid lines */}
                    {headerMarkers.map(w => <div key={w.key} style={{ position:'absolute', left:w.px, top:0, bottom:0, borderLeft:'1px solid var(--border)', opacity:0.2 }} />)}
                    {/* Fins de semana — coluna de pxPerDay (só zoom dias) */}
                    {ganttZoom === 'days' && headerMarkers.filter(w => w.isWeekend).map(w => (
                      <div key={'we_'+w.key} style={{ position:'absolute', left:w.px, top:0, bottom:0, width:pxPerDay, background:'rgba(0,0,0,.025)', zIndex:1 }} />
                    ))}
                    {/* Hoje — coluna de pxPerDay */}
                    <div style={{ position:'absolute', left:todayPx, top:0, bottom:0, width:pxPerDay, background:'rgba(239,68,68,.1)', zIndex:5 }} />
                    {(() => {
                      const lPx = px(gr.start);
                      // One-shot: barra de 1 dia. Período: duração real.
                      const oneDayPx = pxPerDay;
                      const rPx = gr.tipo==='periodo'
                        ? px(new Date(gr.end.getTime()+864e5))
                        : lPx + oneDayPx;
                      const wPx = Math.max(rPx - lPx, 2);
                      const isShort = gr.tipo !== 'periodo'; // one-shot = barra mais fina
                      return (
                        <div
                          onMouseEnter={e => setTooltip({ x: e.clientX, y: e.clientY, gr })}
                          onMouseMove={e => setTooltip(t => t ? { ...t, x: e.clientX, y: e.clientY } : null)}
                          onMouseLeave={() => setTooltip(null)}
                          style={{ position:'absolute', left:lPx, width:wPx,
                            top:0, bottom:0,
                            background: gr.needs_review?'#f59e0b':gr.is_interno?'#92400e':gr.color,
                            display:'flex', alignItems:'center', paddingLeft: wPx > 20 ? 8 : 0,
                            overflow:'hidden', cursor:'pointer', zIndex:6,
                            opacity: isShort ? 0.75 : 0.9,
                          }}>
                          {wPx > 35 && <span style={{ fontSize:9, color:'#fff', fontWeight:700, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis', maxWidth:'100%', letterSpacing:'.02em' }}>
                            {gr.needs_review ? 'Rever' : gr.label.slice(0, Math.floor(wPx/7))}
                          </span>}
                        </div>
                      );
                    })()}
                    {gr.row && saving[gr.row.id] && <div style={{ position:'absolute', right:6, top:'50%', transform:'translateY(-50%)', fontSize:10, color:'#16a34a', fontWeight:700 }}>✓</div>}
                  </div>
                </div>
                );
                })}
              </div>{/* fim inner div totalWidth */}
            </div>{/* fim scroll wrapper */}

            {/* Legenda */}
            <div style={{ padding: '8px 14px', borderTop: '1px solid var(--border)', display: 'flex', gap: 16, flexWrap: 'wrap', background: 'var(--bg-sunken)' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 10, color: 'var(--text-muted)' }}>
                <div style={{ width: 12, height: 3, background: '#ef4444', borderRadius: 1 }} /> Hoje
              </div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 10, color: 'var(--text-muted)' }}>
                <div style={{ width: 12, height: 10, background: '#3859D0', borderRadius: 2 }} /> Período
              </div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 10, color: 'var(--text-muted)' }}>
                <div style={{ width: 10, height: 10, background: '#7c3aed', borderRadius: '50%' }} /> Evento pontual
              </div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 10, color: 'var(--text-muted)' }}>
                <div style={{ width: 12, height: 10, background: '#f59e0b', borderRadius: 2 }} /> Requer revisão
              </div>
            </div>
          </div>
        );
      })()}

      {/* Tooltip hover — popup flutuante com info completa */}
      {tooltip && (() => {
        const { gr } = tooltip;
        const fmtD = (d) => d instanceof Date && !isNaN(d) ? d.toLocaleDateString('pt-PT', { day:'2-digit', month:'short' }) : '—';
        const CONTENT_LABEL = {
          briefing_interno: 'Comunicação Interna', campanha_paid: 'Paid Media',
          email_html: 'Email', social_post: 'Social Orgânico', ad: 'Anúncio',
          artigo_blog: 'Blog', default: CANAL_LABEL[gr.canal] || gr.canal,
        };
        const ctLabel = CONTENT_LABEL[gr.content_type] || CONTENT_LABEL.default;
        const status = gr.needs_review ? 'Requer revisão' : gr.approved ? 'Aprovado' : 'Planeado';
        const statusColor = gr.needs_review ? '#d97706' : gr.approved ? '#16a34a' : '#3859D0';
        // Posição: seguir cursor, evitar sair do viewport
        const vpW = window.innerWidth; const vpH = window.innerHeight;
        const popW = 320; const popH = 220;
        const left = tooltip.x + 16 + popW > vpW ? tooltip.x - popW - 8 : tooltip.x + 16;
        const top  = tooltip.y + 16 + popH > vpH ? tooltip.y - popH - 8 : tooltip.y + 16;
        return (
          <div style={{ position:'fixed', left, top, width:popW, background:'#fff', borderRadius:10, boxShadow:'0 8px 32px rgba(17,41,84,.18)', border:'1px solid var(--border)', zIndex:99999, pointerEvents:'none', overflow:'hidden' }}>
            {/* Header colorido */}
            <div style={{ background: gr.is_interno?'#92400e':gr.color, padding:'10px 14px' }}>
              <div style={{ fontSize:9, fontWeight:700, fontFamily:'var(--font-mono)', textTransform:'uppercase', letterSpacing:'.08em', color:'rgba(255,255,255,.7)', marginBottom:3 }}>{ctLabel}</div>
              <div style={{ fontSize:12, fontWeight:700, color:'#fff', lineHeight:1.3 }}>{gr.fullLabel || gr.label}</div>
            </div>
            {/* Corpo */}
            <div style={{ padding:'10px 14px', display:'flex', flexDirection:'column', gap:8 }}>
              {/* Datas */}
              <div style={{ display:'flex', alignItems:'center', gap:8 }}>
                <div style={{ fontSize:10, fontFamily:'var(--font-mono)', color:'var(--text-dim)', textTransform:'uppercase', letterSpacing:'.06em' }}>Data</div>
                <div style={{ fontSize:11, fontWeight:600, color:'var(--text)' }}>
                  {fmtD(gr.start)}{gr.tipo==='periodo' && gr.end>gr.start ? ` → ${fmtD(gr.end)}` : ''}
                </div>
              </div>
              {/* Conteúdo / tema */}
              {(gr.fullBody || gr.notas) && (
                <div>
                  <div style={{ fontSize:10, fontFamily:'var(--font-mono)', color:'var(--text-dim)', textTransform:'uppercase', letterSpacing:'.06em', marginBottom:3 }}>Conteúdo</div>
                  <div style={{ fontSize:11, color:'var(--text)', lineHeight:1.5, display:'-webkit-box', WebkitLineClamp:3, WebkitBoxOrient:'vertical', overflow:'hidden' }}>
                    {gr.fullBody || gr.notas}
                  </div>
                </div>
              )}
              {/* Status */}
              <div style={{ display:'flex', alignItems:'center', gap:6, paddingTop:4, borderTop:'1px solid var(--border)' }}>
                <span style={{ width:6, height:6, borderRadius:'50%', background:statusColor, flexShrink:0 }} />
                <span style={{ fontSize:10, fontWeight:600, color:statusColor }}>{status}</span>
                {gr.budget && <span style={{ marginLeft:'auto', fontSize:10, fontFamily:'var(--font-mono)', color:'var(--text-muted)' }}>€{gr.budget}</span>}
              </div>
            </div>
          </div>
        );
      })()}

      {toast && <div style={{ position: 'fixed', bottom: 24, right: 24, background: 'var(--text)', color: '#fff', padding: '10px 18px', borderRadius: 8, fontSize: 13, zIndex: 9999 }}>{toast}</div>}
    </div>
  );
};

// ── TabTarget — Targets por canal (Ads + CRM) sem toggle ─────────────────────
const _AUDIENCE_SIZE_COLOR = { 'Pequeno (<10K)': '#b91c1c', 'Médio (10K-100K)': '#a16207', 'Grande (>100K)': '#15803d', 'Pequeno (<500)': '#b91c1c', 'Médio (500-5000)': '#a16207', 'Grande (>5000)': '#15803d' };
const _ADS_CHANNELS_SET = new Set(['meta_ads','linkedin_ads','google_ads_search','google_ads_display','muppi_led']);
const _CRM_CHANNELS_SET = new Set(['email','whatsapp']);

const TabTarget = ({ campanha, userEmail, onAction }) => {
  const bCol = _brandColor(campanha?.brand_slug);
  const [rows,      setRows]      = React.useState([]);
  const [audiences, setAudiences] = React.useState([]);
  const [loading,   setLoading]   = React.useState(true);
  const [generating,setGenerating]= React.useState(false);
  const [toast,     setToast]     = React.useState('');
  const [genError,  setGenError]  = React.useState(null);
  const [editing,   setEditing]   = React.useState(null);
  const [genStep,   setGenStep]   = React.useState(0);
  const [genElapsed,setGenElapsed]= React.useState(0);
  const genTimersRef     = React.useRef([]);
  const genElapsedRef    = React.useRef(null);
  // Modal de filtros manuais
  const [showFilters, setShowFilters] = React.useState(false);
  const [crmMeta,     setCrmMeta]     = React.useState(null); // dados do /crm/meta
  const [userFilters, setUserFilters] = React.useState({
    // Meta Ads
    meta_interesses: [], meta_comportamentos: [], meta_idade_min: '', meta_idade_max: '',
    // Email CRM
    email_profile_seg1: [], email_sources_grupo: [],
    // WhatsApp CRM
    wa_cargo: [], wa_lost_janela_min: '', wa_lost_janela_max: '',
  });

  const strategyChannels = new Set((campanha?.estrategia_json?.markets || []).flatMap(m => (m.channel_mix||[]).map(ch => ch.canal)));
  const hasAds = [...strategyChannels].some(c => _ADS_CHANNELS_SET.has(c));
  const hasCrm = [...strategyChannels].some(c => _CRM_CHANNELS_SET.has(c));
  const adsCanais = [...strategyChannels].filter(c => _ADS_CHANNELS_SET.has(c));
  const crmCanais = [...strategyChannels].filter(c => _CRM_CHANNELS_SET.has(c));

  // Steps dinâmicos conforme canais aprovados no briefing — step numbers auto-sequenciais
  const hasEmail = crmCanais.includes('email');
  const hasWA    = crmCanais.includes('whatsapp');
  const TARGET_STEPS = [
    {
      label: 'Briefing · Estratégia · Conceito',
      detail: `Canais activos: ${[...adsCanais,...crmCanais].map(c=>CANAL_LABEL[c]||c).join(', ')} · Objectivo · Personas · USPs · Restrições`,
      duration: 2000,
    },
    ...(hasAds ? [{
      label: `Interesses Meta Ads`,
      detail: `meta-ads-b2b skill · pesquisa IDs reais por keyword · ${adsCanais.map(c=>CANAL_LABEL[c]||c).join(', ')}`,
      duration: 8000,
    }] : []),
    ...(hasAds ? [{
      label: 'Target Ads — Geração',
      detail: `Claude Sonnet · interesses confirmados Meta · 1 ad set por país · guardrails briefing`,
      duration: 15000,
    }] : []),
    ...(hasEmail ? [{
      label: 'Target Email — BD Gestor',
      detail: `Massivo · profile_seg1 (Impressão Digital) · sem equipa/stages · 10k+ contactos`,
      duration: 15000,
    }] : []),
    ...(hasWA ? [{
      label: 'Target WA — BD Gestor',
      detail: `Reactivação (OPs perdidas produto) + Aceleração (AG Decisão) · cargo decisor`,
      duration: 15000,
    }] : []),
    {
      label: 'Contagens Reais + Guardar',
      detail: `BD Gestor: n_entidades + n_contactos reais · campanha_segmentacao_plano × mercado`,
      duration: 1500,
    },
  ].map((s, i) => ({ ...s, step: i + 1 }));

  const showToast = (msg) => { setToast(msg); setTimeout(() => setToast(''), 2500); };

  const openFilters = () => {
    setShowFilters(true);
    if (!crmMeta) campApiCall('/api/marketing/crm/meta').then(d => setCrmMeta(d)).catch(() => {});
  };

  const load = () => {
    setLoading(true);
    Promise.all([
      campApiCall(`/api/marketing/campanhas/${campanha.id}/segmentacao`),
      campApiCall('/api/marketing/crm-audiences-list').catch(() => ({ rows: [] }))
    ]).then(([segD, audD]) => {
      setRows(segD.rows || []);
      setAudiences(audD.rows || []);
    }).finally(() => setLoading(false));
  };
  React.useEffect(() => { if (campanha?.id) load(); }, [campanha?.id]);

  const generate = async () => {
    setGenerating(true);
    setGenError(null);
    setGenStep(1);
    setGenElapsed(0);
    genElapsedRef.current = setInterval(() => setGenElapsed(s => s + 1), 1000);
    let cum = 0;
    genTimersRef.current = TARGET_STEPS.map(s => {
      cum += s.duration;
      return setTimeout(() => setGenStep(s.step + 1 <= TARGET_STEPS.length ? s.step + 1 : TARGET_STEPS.length), cum);
    });
    const cleanup = () => {
      clearInterval(genElapsedRef.current);
      genTimersRef.current.forEach(clearTimeout);
      genTimersRef.current = [];
      setGenStep(0); setGenElapsed(0);
    };
    try {
      if (hasAds) {
        const d = await campApiCall(`/api/marketing/campanhas/${campanha.id}/publicos-ads/generate`, { method: 'POST', body: JSON.stringify({ user_email: userEmail, user_filters: userFilters }) });
        if (d.error) throw new Error(d.error);
      }
      if (hasCrm) {
        const d = await campApiCall(`/api/marketing/campanhas/${campanha.id}/segmentos-crm/generate`, { method: 'POST', body: JSON.stringify({ user_email: userEmail, user_filters: userFilters }) });
        if (d.error) throw new Error(d.error);
      }
      cleanup();
      load();
      showToast('Targets gerados');
      if (onAction) onAction('refreshCampanha');
    } catch(e) {
      cleanup();
      setGenError(e.message || 'Erro desconhecido');
      showToast('Erro: ' + (e.message || 'Erro desconhecido'));
    }
    setGenerating(false);
  };
  React.useEffect(() => () => { clearInterval(genElapsedRef.current); genTimersRef.current.forEach(clearTimeout); }, []);

  const approve = async () => {
    await campApiCall(`/api/marketing/campanhas/${campanha.id}/segmentacao/approve`, {
      method: 'POST', body: JSON.stringify({ user_email: userEmail })
    });
    load();
    showToast('Targets confirmados');
    if (onAction) onAction('refreshCampanha');
  };

  const saveEdit = async (row, updates) => {
    try {
      await campApiCall(`/api/marketing/campanhas/${campanha.id}/segmentacao/${row.id}`, {
        method: 'PUT', body: JSON.stringify({ ...updates, user_email: userEmail })
      });
      setRows(rs => rs.map(r => r.id === row.id ? { ...r, ...updates } : r));
      setEditing(null);
      showToast('Guardado');
    } catch { showToast('Erro ao guardar'); }
  };

  // Agrupar rows por canal — Ads primeiro, depois CRM
  const CANAL_ORDER = ['meta_ads','linkedin_ads','google_ads_search','google_ads_display','muppi_led','email','whatsapp'];
  const rowsByCanal = {};
  for (const r of rows) {
    if (!rowsByCanal[r.canal]) rowsByCanal[r.canal] = [];
    rowsByCanal[r.canal].push(r);
  }
  const orderedCanais = Object.keys(rowsByCanal).sort((a, b) => {
    const ia = CANAL_ORDER.indexOf(a); const ib = CANAL_ORDER.indexOf(b);
    return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
  });
  const allConfirmed = rows.length > 0 && rows.every(r => r.approved_at);
  const STATUSES_AFTER_TARGET = ['segmentacao_aprovada','planeamento_pendente','planeamento_gerado','planeamento_aprovado','funil_pendente','funil_gerado','funil_aprovado','pending_executive','em_aprovacao','aprovado'];
  const isConfirmed = STATUSES_AFTER_TARGET.includes(campanha?.status) || allConfirmed;

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

      {/* Status strip */}
      {rows.length > 0 && !generating && (
        <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: isConfirmed ? 'var(--green-100,#E1F7E6)' : 'var(--dd-blue-100,#EBEFF9)',
            color:      isConfirmed ? 'var(--green-700,#1F8A52)' : 'var(--dd-primary-600,#3859D0)' }}>
            {isConfirmed ? 'Confirmado' : 'Gerado — aguarda confirmação'}
          </span>
          <span style={{ fontSize: 11, color: 'var(--fg-3,var(--text-muted))' }}>
            {orderedCanais.length} canal{orderedCanais.length !== 1 ? 'ais' : ''} · {[...new Set(rows.map(r=>r.country))].join(' + ')}
          </span>
          <div style={{ marginLeft: 'auto', display: 'flex', gap: 6 }}>
            <button className="btn" onClick={openFilters} style={{ height: 28, padding: '0 12px', fontSize: 12 }}>Filtros</button>
            <button className="btn" onClick={generate} disabled={generating} style={{ height: 28, padding: '0 12px', fontSize: 12 }}>Regenerar</button>
            {!isConfirmed && (
              <button className="btn btn-ai" onClick={approve} style={{ height: 28, padding: '0 14px', fontSize: 12 }}>Confirmar Targets</button>
            )}
          </div>
        </div>
      )}

      {/* Cards resumo de audiência — canal × país × total */}
      {!loading && !generating && rows.length > 0 && (() => {
        // Agregar: por canal × país, somar contactos (CRM) ou usar audience_size (Ads)
        const summaryMap = {};
        for (const r of rows) {
          const key = `${r.canal}__${r.country}`;
          const aj = typeof r.audiencia_json === 'string' ? JSON.parse(r.audiencia_json||'{}') : (r.audiencia_json||{});
          if (!summaryMap[key]) summaryMap[key] = { canal: r.canal, country: r.country, n_contactos: 0, n_entidades: 0, audience_size: null, isAds: _ADS_CHANNELS_SET.has(r.canal) };
          summaryMap[key].n_contactos += (aj.n_contactos || 0);
          summaryMap[key].n_entidades += (aj.n_entidades || 0);
          // Para Ads: usar audience_size guardado (já formatado como número no backend)
          if (!summaryMap[key].audience_size && aj.audience_size) summaryMap[key].audience_size = aj.audience_size;
        }
        const summaries = Object.values(summaryMap).sort((a,b) => {
          const CANAL_ORDER = ['meta_ads','linkedin_ads','google_ads_search','google_ads_display','muppi_led','email','whatsapp'];
          return (CANAL_ORDER.indexOf(a.canal)+99||99) - (CANAL_ORDER.indexOf(b.canal)+99||99);
        });
        const CANAL_COLOR = { meta_ads:'#3859D0', linkedin_ads:'#0a66c2', google_ads_search:'#ea4335', google_ads_display:'#34a853', email:'#7c3aed', whatsapp:'#16a34a', muppi_led:'#0891b2' };
        return (
          <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
            {summaries.map(s => {
              const color = CANAL_COLOR[s.canal] || '#3859D0';
              const mainNum = s.isAds
                ? (s.audience_size || '—')
                : (s.n_contactos > 0 ? s.n_contactos.toLocaleString('pt-PT') : s.n_entidades.toLocaleString('pt-PT'));
              const subLabel = s.isAds
                ? 'alcance estimado'
                : (s.n_contactos > 0 ? 'contactos' : 'empresas');
              return (
                <div key={`${s.canal}__${s.country}`} style={{ flex: '1 1 160px', background: 'var(--bg-card,#fff)', border: `1px solid var(--border)`, borderTop: `4px solid ${color}`, borderRadius: 10, padding: '14px 18px', minWidth: 140 }}>
                  <div style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', textTransform: 'uppercase', letterSpacing: '.06em', color, marginBottom: 2 }}>
                    {CANAL_LABEL[s.canal] || s.canal}
                  </div>
                  <div style={{ fontSize: 11, color: 'var(--text-dim)', marginBottom: 10 }}>{s.country}</div>
                  <div style={{ fontSize: 28, fontWeight: 800, fontFamily: 'var(--font-display)', color: 'var(--text)', lineHeight: 1 }}>{mainNum}</div>
                  <div style={{ fontSize: 10, color: 'var(--text-muted)', marginTop: 4 }}>{subLabel}</div>
                </div>
              );
            })}
          </div>
        );
      })()}

      {/* Loading state */}
      {generating && <PhaseLoadingState steps={TARGET_STEPS} activeStep={genStep} elapsedSec={genElapsed} brandColor="#7c3aed" />}
      {loading && !generating && <div style={{ padding: 32, color: 'var(--text-muted)', fontSize: 13 }}>A carregar...</div>}

      {/* Erro */}
      {genError && !generating && (
        <div style={{ padding: '14px 18px', background: 'rgba(220,38,38,.06)', border: '1px solid rgba(220,38,38,.25)', borderRadius: 10, display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
          <div style={{ fontSize: 13, color: '#991B1B' }}>Erro: {genError}</div>
          <button className="btn" onClick={generate} style={{ height: 28, fontSize: 12, color: '#991B1B' }}>Tentar novamente</button>
        </div>
      )}

      {/* Empty state */}
      {!loading && !generating && rows.length === 0 && !genError && (
        <PhaseEmptyState
          label="Target · fase 4"
          title="Ainda sem targets definidos"
          description={`Análise de audiências e segmentos com base no briefing, estratégia e conceito.${hasAds ? ' Ads: interesses, cargos, lookalikes, geo por canal (Meta / LinkedIn / Google).' : ''}${hasCrm ? ' CRM: filtros Gestor para envio Email / WhatsApp (sector, cargo, região, equipamento).' : ''}`}
          ctaLabel="Gerar Targets →"
          onCta={generate}
          loading={generating}
          disabled={!campanha?.estrategia_json || (!hasAds && !hasCrm)}
          disabledReason={!campanha?.estrategia_json ? 'A estratégia precisa de ser gerada primeiro.' : 'Nenhum canal Ads ou CRM na estratégia.'}
        />
      )}

      {/* Targets por canal — mesmo padrão visual do Orçamento */}
      {!loading && !generating && orderedCanais.map(canal => {
        const canalRows = rowsByCanal[canal];
        const isCrm = _CRM_CHANNELS_SET.has(canal);
        const isAds = _ADS_CHANNELS_SET.has(canal);
        return (
          <div key={canal} style={{ background: 'var(--bg-card,#fff)', border: '1px solid var(--border)', borderRadius: 10, overflow: 'hidden' }}>
            {/* Canal header */}
            <div style={{ padding: '10px 16px', borderBottom: '1px solid var(--border)', display: 'flex', alignItems: 'center', gap: 10 }}>
              <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--text)', fontFamily: 'var(--font-display)', textTransform: 'uppercase', letterSpacing: '.04em' }}>{CANAL_LABEL[canal] || canal}</div>
              <span style={{ fontSize: 10, padding: '1px 7px', borderRadius: 3, fontFamily: 'var(--font-mono)', fontWeight: 600,
                background: isAds ? 'rgba(56,89,208,.1)' : 'rgba(124,58,237,.1)',
                color: isAds ? '#3859D0' : '#7c3aed' }}>
                {isAds ? 'Ads' : 'CRM'}
              </span>
            </div>

            {/* Rows por mercado */}
            {canalRows.map(row => {
              const aj = typeof row.audiencia_json === 'string' ? JSON.parse(row.audiencia_json || '{}') : (row.audiencia_json || {});
              const td = aj.targeting_details || {};
              // Extrair só o número do audience_size (remover categoria se ainda existir)
              const rawSize = aj.audience_size || '';
              const cleanSize = rawSize.replace(/^(Grande|Médio|Pequeno|Muy Grande|Muy Pequeño)\s*/i, '').replace(/^\(|\)$/g,'').trim() || rawSize;
              const sizeColor = 'var(--navy, #112954)';
              const isEditing = editing === row.id;
              return (
                <div key={row.id} style={{ padding: '12px 16px', borderBottom: '1px solid var(--border)', borderLeft: row.approved_at ? '3px solid var(--success,#16a34a)' : '3px solid transparent' }}>
                  {/* Mercado + nome Meta (naming convention) + tamanho estimado */}
                  <div style={{ display: 'flex', alignItems: 'flex-start', gap: 8, marginBottom: 10 }}>
                    {/* Country badge apenas para CRM — Ads já tem [PT]/[ES] no nome */}
                    {isCrm && <span style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', background: 'var(--bg-sunken)', padding: '2px 7px', borderRadius: 3, color: 'var(--text-dim)', flexShrink: 0, marginTop: 2 }}>{row.country}</span>}
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--text)', fontFamily: 'var(--font-mono)' }}>{row.audiencia_nome || '—'}</div>
                      {isAds && cleanSize && (
                        <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 4 }}>
                          <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', textTransform: 'uppercase', color: 'var(--text-dim)', letterSpacing: '.06em' }}>Alcance estimado</div>
                          <div style={{ fontSize: 14, fontWeight: 800, fontFamily: 'var(--font-display)', color: '#3859D0' }}>{cleanSize}</div>
                        </div>
                      )}
                    </div>
                    {!isConfirmed && (
                      <button className="btn" style={{ height: 22, padding: '0 8px', fontSize: 10, flexShrink: 0 }} onClick={() => setEditing(isEditing ? null : row.id)}>{isEditing ? 'Fechar' : 'Editar'}</button>
                    )}
                  </div>

                  {/* Targeting details — Ads: interesses+comportamentos | CRM: filtros Gestor + contagens reais */}
                  {isAds && td && (
                    <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                      {/* Localização + Idade + Género — como Meta Ads Manager */}
                      <div style={{ display: 'flex', gap: 20, fontSize: 12 }}>
                        {td.geo?.length > 0 && <div><div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', textTransform: 'uppercase', color: 'var(--text-dim)', letterSpacing: '.06em' }}>Localização</div><div style={{ color: 'var(--text)' }}>{td.geo.join(', ')}</div></div>}
                        <div><div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', textTransform: 'uppercase', color: 'var(--text-dim)', letterSpacing: '.06em' }}>Idade</div><div style={{ color: 'var(--text)' }}>{td.idade_min||25} – {td.idade_max||65}+</div></div>
                        <div><div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', textTransform: 'uppercase', color: 'var(--text-dim)', letterSpacing: '.06em' }}>Género</div><div style={{ color: 'var(--text)' }}>Todos</div></div>
                      </div>
                      {/* Definição detalhada — como "Definição do público-alvo detalhada" no Meta */}
                      <div style={{ background: 'var(--bg-sunken)', borderRadius: 6, padding: '8px 10px', display: 'flex', flexDirection: 'column', gap: 5 }}>
                        <div style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', textTransform: 'uppercase', color: 'var(--text-dim)', letterSpacing: '.06em' }}>Definição do público-alvo detalhada</div>
                        {td.interesses?.length > 0 && (
                          <div style={{ fontSize: 11, color: 'var(--text)', lineHeight: 1.5 }}>
                            <span style={{ color: 'var(--text-dim)' }}>Interesses: </span>{td.interesses.join(', ')}
                          </div>
                        )}
                        {td.comportamentos?.length > 0 && (
                          <div style={{ fontSize: 11, color: 'var(--text)', lineHeight: 1.5 }}>
                            <span style={{ color: 'var(--text-dim)' }}>E que também correspondem a — Comportamentos: </span>{td.comportamentos.join(', ')}
                          </div>
                        )}
                        {td.cargos?.length > 0 && (
                          <div style={{ fontSize: 11, color: 'var(--text)', lineHeight: 1.5 }}>
                            <span style={{ color: 'var(--text-dim)' }}>Cargos: </span>{td.cargos.join(', ')}
                          </div>
                        )}
                        {td.exclusoes?.length > 0 && (
                          <div style={{ fontSize: 11, color: '#991B1B', lineHeight: 1.5 }}>
                            <span style={{ color: '#991B1B' }}>Excluir: </span>{td.exclusoes.join(', ')}
                          </div>
                        )}
                      </div>
                    </div>
                  )}
                  {isCrm && (
                    <div style={{ background: 'var(--bg-sunken)', borderRadius: 6, padding: '8px 10px', display: 'flex', flexDirection: 'column', gap: 4 }}>
                      {/* Badge objectivo + Contagens reais do Gestor */}
                      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8, flexWrap: 'wrap' }}>
                        {aj.objectivo && (() => {
                          const OBJ = { pipeline: ['Pipeline', '#3859D0'], reactivacao: ['Reactivação', '#d97706'], aceleracao: ['Aceleração', '#059669'] };
                          const [label, color] = OBJ[aj.objectivo] || ['—', 'var(--text-dim)'];
                          return <span style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', padding: '2px 8px', borderRadius: 3, background: color+'18', color, textTransform: 'uppercase', letterSpacing: '.06em' }}>{label}</span>;
                        })()}
                        {aj.n_entidades != null && (
                          <>
                            <div>
                              <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', textTransform: 'uppercase', color: 'var(--text-dim)', letterSpacing: '.06em' }}>Empresas</div>
                              <div style={{ fontSize: 16, fontWeight: 800, fontFamily: 'var(--font-display)', color: 'var(--text)' }}>{(aj.n_entidades||0).toLocaleString('pt-PT')}</div>
                            </div>
                            <div>
                              <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', textTransform: 'uppercase', color: 'var(--text-dim)', letterSpacing: '.06em' }}>Contactos</div>
                              <div style={{ fontSize: 16, fontWeight: 800, fontFamily: 'var(--font-display)', color: 'var(--text)' }}>{(aj.n_contactos||0).toLocaleString('pt-PT')}</div>
                            </div>
                          </>
                        )}
                      </div>
                      {/* Filtros aplicados */}
                      {(() => {
                        const fg = td.filtros_gestor || {};
                        return (
                          <div style={{ display: 'flex', flexWrap: 'wrap', gap: '3px 12px', fontSize: 11, color: 'var(--text-muted)' }}>
                            {fg.pais?.length > 0 && <span><strong style={{ color: 'var(--text-dim)' }}>País:</strong> {fg.pais.join(', ')}</span>}
                            {fg.equipa?.length > 0 && <span><strong style={{ color: 'var(--text-dim)' }}>Equipa:</strong> {fg.equipa.join(', ')}</span>}
                            {fg.profile_seg1?.length > 0 && <span><strong style={{ color: 'var(--text-dim)' }}>Perfil:</strong> {fg.profile_seg1.join(', ')}</span>}
                            {fg.stages?.length > 0 && <span><strong style={{ color: 'var(--text-dim)' }}>Stages:</strong> {fg.stages.join(', ')}</span>}
                            {fg.lost_produto?.length > 0 && <span><strong style={{ color: '#d97706' }}>Produto perdido:</strong> {fg.lost_produto.slice(0,3).join(', ')}</span>}
                            {fg.lost_janela && <span style={{ color: '#d97706' }}>OPs perdidas {fg.lost_janela.min}-{fg.lost_janela.max}m</span>}
                            {fg.sources_grupo?.length > 0 && <span><strong style={{ color: 'var(--text-dim)' }}>Origem:</strong> {fg.sources_grupo.join(', ')}</span>}
                            {fg.com_op_activa && <span style={{ color: '#15803d' }}>Com OP activa</span>}
                            {fg.tem_email && <span>Email OK</span>}
                            {fg.tem_telefone && <span>Tel. OK</span>}
                          </div>
                        );
                      })()}
                    </div>
                  )}

                  {/* CRM audience link */}
                  {isCrm && (
                    <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 6 }}>
                      <span style={{ fontSize: 10, fontFamily: 'var(--font-mono)', color: 'var(--text-dim)', textTransform: 'uppercase' }}>CRM:</span>
                      {row.crm_audience_nome
                        ? <span style={{ fontSize: 11, color: 'var(--text)', fontWeight: 600 }}>{row.crm_audience_nome} ({row.crm_audience_count?.toLocaleString('pt-PT')||'?'})</span>
                        : isEditing
                          ? <select style={{ fontSize: 11, padding: '2px 6px', borderRadius: 4, border: '1px solid var(--border)', background: 'var(--bg)', color: 'var(--text)' }} defaultValue=""
                              onChange={e => saveEdit(row, { crm_audience_uuid: e.target.value||null, audiencia_nome: row.audiencia_nome, audiencia_json: aj })}>
                              <option value="">— Sem audiência CRM —</option>
                              {audiences.map(a => <option key={a.id} value={a.id}>{a.nome} ({a.contagem_ultima?.toLocaleString('pt-PT')||'?'})</option>)}
                            </select>
                          : <span style={{ fontSize: 11, color: 'var(--text-muted)' }}>Não associada</span>
                      }
                    </div>
                  )}

                  {aj.notas && <div style={{ fontSize: 11, color: 'var(--text-muted)', fontStyle: 'italic', marginTop: 4 }}>{aj.notas}</div>}
                </div>
              );
            })}
          </div>
        );
      })}

      {/* Modal de Filtros de Target */}
      {showFilters && (() => {
        const uf = userFilters;
        const setUf = (patch) => setUserFilters(prev => ({ ...prev, ...patch }));
        const toggleArr = (key, val) => setUf({ [key]: uf[key].includes(val) ? uf[key].filter(x => x !== val) : [...uf[key], val] });
        const nActive = [uf.meta_interesses.length, uf.meta_comportamentos.length, uf.meta_idade_min?1:0, uf.email_profile_seg1.length, uf.email_sources_grupo.length, uf.wa_cargo.length, uf.wa_lost_janela_min?1:0].reduce((a,b)=>a+b,0);
        const profileSegs = crmMeta?.profile_segs || [];
        const seg1Opts = [...new Set(profileSegs.flatMap(s => (s.valores||[]).filter(v=>v.atributo==='1').map(v=>v.valor)))];
        const cargosOpts = (crmMeta?.cargos || []).filter(c => !['Outros','OUTROS'].includes(c)).slice(0, 20);
        const sourcesOpts = crmMeta?.sources_grupos || ['MKT','FEIRAS','RH'];
        const META_INTERESTS = ['Digital printing','Print on demand','Textile industry','Textile printing','Screen printing','Small business','Entrepreneurship','Fashion design','Custom gifts and clothing','Manufacturing','Business networking','Graphic design'];
        const META_BEHAVIORS = ['Small business owners','Business decision makers'];

        const Label = ({ children }) => <div style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', textTransform: 'uppercase', letterSpacing: '.06em', color: 'var(--text-dim)', marginBottom: 6 }}>{children}</div>;
        const Chips = ({ opts, sel, onToggle, color='#3859D0' }) => (
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 5, marginBottom: 12 }}>
            {opts.map(o => { const a = sel.includes(o); return (
              <button key={o} onClick={() => onToggle(o)} style={{ fontSize: 11, padding: '3px 10px', borderRadius: 20, cursor: 'pointer', border: `1px solid ${a?color:'var(--border)'}`, background: a?color:'var(--bg)', color: a?'#fff':'var(--text-muted)', fontWeight: a?600:400 }}>{o}</button>
            ); })}
          </div>
        );

        return (
          <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.5)', zIndex: 9100, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24 }}>
            <div style={{ background: '#fff', borderRadius: 14, width: '100%', maxWidth: 700, maxHeight: '87vh', display: 'flex', flexDirection: 'column', boxShadow: '0 24px 60px rgba(0,0,0,.25)' }}>
              <div style={{ padding: '18px 24px', borderBottom: '1px solid var(--border)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexShrink: 0 }}>
                <div>
                  <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--text)', fontFamily: 'var(--font-display)' }}>Filtros de Target</div>
                  <div style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 2 }}>Deixar em branco = geração automática pelo agente.</div>
                </div>
                <button onClick={() => setShowFilters(false)} style={{ background: 'none', border: 'none', fontSize: 20, cursor: 'pointer', color: 'var(--text-muted)' }}>×</button>
              </div>

              <div style={{ flex: 1, overflow: 'auto', padding: '20px 24px' }}>

                {/* Meta Ads */}
                {hasAds && (
                  <div style={{ marginBottom: 24 }}>
                    <div style={{ fontSize: 12, fontWeight: 700, color: '#3859D0', fontFamily: 'var(--font-display)', textTransform: 'uppercase', letterSpacing: '.04em', marginBottom: 12, paddingBottom: 6, borderBottom: '2px solid rgba(56,89,208,.15)' }}>Meta Ads</div>
                    <Label>Interesses</Label>
                    <Chips opts={META_INTERESTS} sel={uf.meta_interesses} onToggle={v=>toggleArr('meta_interesses',v)} color="#3859D0" />
                    <Label>Comportamentos</Label>
                    <Chips opts={META_BEHAVIORS} sel={uf.meta_comportamentos} onToggle={v=>toggleArr('meta_comportamentos',v)} color="#3859D0" />
                    <Label>Idade</Label>
                    <div style={{ display: 'flex', gap: 10, alignItems: 'center', marginBottom: 12 }}>
                      <input type="number" placeholder="Min (25)" value={uf.meta_idade_min} onChange={e=>setUf({meta_idade_min:e.target.value})} style={{ width: 90, padding: '5px 8px', borderRadius: 6, border: '1px solid var(--border)', fontSize: 12 }} />
                      <span style={{ color: 'var(--text-dim)' }}>–</span>
                      <input type="number" placeholder="Max (65)" value={uf.meta_idade_max} onChange={e=>setUf({meta_idade_max:e.target.value})} style={{ width: 90, padding: '5px 8px', borderRadius: 6, border: '1px solid var(--border)', fontSize: 12 }} />
                    </div>
                  </div>
                )}

                {/* Email */}
                {crmCanais.includes('email') && (
                  <div style={{ marginBottom: 24 }}>
                    <div style={{ fontSize: 12, fontWeight: 700, color: '#7c3aed', fontFamily: 'var(--font-display)', textTransform: 'uppercase', letterSpacing: '.04em', marginBottom: 12, paddingBottom: 6, borderBottom: '2px solid rgba(124,58,237,.15)' }}>Email — BD Gestor</div>
                    {seg1Opts.length > 0 && (<>
                      <Label>Tipo de Indústria</Label>
                      <Chips opts={seg1Opts} sel={uf.email_profile_seg1} onToggle={v=>toggleArr('email_profile_seg1',v)} color="#7c3aed" />
                    </>)}
                    <Label>Origem do Lead</Label>
                    <Chips opts={sourcesOpts} sel={uf.email_sources_grupo} onToggle={v=>toggleArr('email_sources_grupo',v)} color="#7c3aed" />
                  </div>
                )}

                {/* WhatsApp */}
                {crmCanais.includes('whatsapp') && (
                  <div style={{ marginBottom: 8 }}>
                    <div style={{ fontSize: 12, fontWeight: 700, color: '#16a34a', fontFamily: 'var(--font-display)', textTransform: 'uppercase', letterSpacing: '.04em', marginBottom: 12, paddingBottom: 6, borderBottom: '2px solid rgba(22,163,74,.15)' }}>WhatsApp — BD Gestor</div>
                    {cargosOpts.length > 0 && (<>
                      <Label>Cargo Decisor / Influenciador</Label>
                      <Chips opts={cargosOpts} sel={uf.wa_cargo} onToggle={v=>toggleArr('wa_cargo',v)} color="#16a34a" />
                    </>)}
                    <Label>Reactivação — OPs perdidas há (meses)</Label>
                    <div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
                      <input type="number" placeholder="Min (6)" value={uf.wa_lost_janela_min} onChange={e=>setUf({wa_lost_janela_min:e.target.value})} style={{ width: 90, padding: '5px 8px', borderRadius: 6, border: '1px solid var(--border)', fontSize: 12 }} />
                      <span style={{ color: 'var(--text-dim)' }}>–</span>
                      <input type="number" placeholder="Max (24)" value={uf.wa_lost_janela_max} onChange={e=>setUf({wa_lost_janela_max:e.target.value})} style={{ width: 90, padding: '5px 8px', borderRadius: 6, border: '1px solid var(--border)', fontSize: 12 }} />
                      <span style={{ fontSize: 11, color: 'var(--text-muted)' }}>meses</span>
                    </div>
                  </div>
                )}
              </div>

              <div style={{ padding: '14px 24px', borderTop: '1px solid var(--border)', display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexShrink: 0 }}>
                <button onClick={() => setUserFilters({ meta_interesses:[], meta_comportamentos:[], meta_idade_min:'', meta_idade_max:'', email_profile_seg1:[], email_sources_grupo:[], wa_cargo:[], wa_lost_janela_min:'', wa_lost_janela_max:'' })} className="btn" style={{ fontSize: 12 }}>Limpar filtros</button>
                <div style={{ display: 'flex', gap: 8 }}>
                  <button onClick={() => setShowFilters(false)} className="btn" style={{ fontSize: 12 }}>Cancelar</button>
                  <button onClick={() => { setShowFilters(false); generate(); }} className="btn btn-ai" style={{ fontSize: 12 }}>
                    Gerar com estes filtros{nActive > 0 ? ` (${nActive})` : ''}
                  </button>
                </div>
              </div>
            </div>
          </div>
        );
      })()}

      {toast && <div style={{ position: 'fixed', bottom: 24, right: 24, background: 'var(--text)', color: '#fff', padding: '10px 18px', borderRadius: 8, fontSize: 13, zIndex: 9999 }}>{toast}</div>}
    </div>
  );
};

// ── TabOrcamento — budget split por canal gerado da estratégia · editável · aprovável ──
const TabOrcamento = ({ campanha, userEmail, onAction }) => {
  const bCol = _brandColor(campanha?.brand_slug);
  const [rows,    setRows]    = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [saving,  setSaving]  = React.useState(false);
  const [saved,   setSaved]   = React.useState({});
  const [toast,   setToast]   = React.useState('');
  const [budget,  setBudget]  = React.useState('');
  const [activeMkt, setActiveMkt] = React.useState(null);
  // Análise Meta Ads (histórico — gerado com orçamento)
  const [analyzing,    setAnalyzing]    = React.useState(false);
  const [analysis,     setAnalysis]     = React.useState(null);
  const [analysisErr,  setAnalysisErr]  = React.useState(null);
  // Edição inline do valor por conjunto (ad set)
  const [editingRow, setEditingRow] = React.useState(null); // id da row em edição
  const [editVal,    setEditVal]    = React.useState('');
  // Meta Ads Analyst Agent (SSE streaming)
  const [analystRunning, setAnalystRunning] = React.useState(false);
  const [analystStatus,  setAnalystStatus]  = React.useState('');
  const [analystResult,  setAnalystResult]  = React.useState(null);
  const [analystStream,  setAnalystStream]  = React.useState('');
  // Loading state com fases
  const [genStep,     setGenStep]     = React.useState(0);
  const [genElapsed,  setGenElapsed]  = React.useState(0);
  const genStepTimersRef  = React.useRef([]);
  const genElapsedTimerRef = React.useRef(null);

  const markets = [...new Set((campanha?.estrategia_json?.markets || []).map(m => m.country))];
  const strategyChannels = new Set((campanha?.estrategia_json?.markets || []).flatMap(m => (m.channel_mix || []).map(ch => ch.canal)));
  const hasMetaAds = strategyChannels.has('meta_ads');

  const load = () => {
    setLoading(true);
    campApiCall(`/api/marketing/campanhas/${campanha.id}/orcamento`)
      .then(d => {
        setRows(d.rows || []);
        if (d.rows?.length) setActiveMkt(d.rows[0].country);
        else setActiveMkt(markets[0] || null);
      })
      .finally(() => setLoading(false));
  };
  // Re-load rows sempre que campanha muda (mount + após refreshCampanha/aprovar)
  React.useEffect(() => { if (campanha?.id) load(); }, [campanha?.id, campanha?.updated_at]);

  // Restaurar análise e resultado do Analyst da BD sempre que campanha muda
  // (mount inicial + após refreshCampanha)
  React.useEffect(() => {
    if (!analystRunning) {
      if (campanha?.orcamento_meta_analysis_json) setAnalysis(campanha.orcamento_meta_analysis_json);
      if (campanha?.orcamento_analyst_json)       setAnalystResult(campanha.orcamento_analyst_json);
    }
  }, [campanha?.orcamento_analyst_json, campanha?.orcamento_meta_analysis_json]);

  const generate = async () => {
    setSaving(true);
    setGenStep(1);
    setGenElapsed(0);
    // Escolher steps conforme tem Meta Ads na estratégia
    const steps = hasMetaAds ? ORCAMENTO_STEPS_WITH_META : ORCAMENTO_STEPS_NO_META;
    // Timer elapsed
    genElapsedTimerRef.current = setInterval(() => setGenElapsed(s => s + 1), 1000);
    // Timers de progressão dos steps
    let cumulative = 0;
    genStepTimersRef.current = [];
    steps.forEach((s, i) => {
      cumulative += s.duration;
      genStepTimersRef.current.push(setTimeout(() => setGenStep(Math.min(s.step + 1, steps.length)), cumulative));
    });
    const cleanup = () => {
      clearInterval(genElapsedTimerRef.current);
      genStepTimersRef.current.forEach(t => clearTimeout(t));
      genStepTimersRef.current = [];
      setGenStep(0);
      setGenElapsed(0);
    };
    try {
      const d = await campApiCall(`/api/marketing/campanhas/${campanha.id}/orcamento/generate`, {
        method: 'POST',
        body: JSON.stringify({ budget_total_eur: budget ? parseFloat(budget) : null, user_email: userEmail })
      });
      setRows(d.rows || []);
      if (d.rows?.length) setActiveMkt(d.rows[0].country);
      if (d.meta_analysis) setAnalysis(d.meta_analysis);
      if (d.suggested_budget && !budget) setBudget(String(d.suggested_budget));

      // Se tem Meta Ads, encadear Analyst automaticamente (steps 6-7)
      if (hasMetaAds && d.rows?.length) {
        // Limpar timers pré-agendados (1-5) para não sobrescreverem o step 6-7 manual
        genStepTimersRef.current.forEach(t => clearTimeout(t));
        genStepTimersRef.current = [];
        setGenStep(6);
        setAnalystRunning(true);
        setAnalystResult(null);
        setAnalystStream('');
        setAnalystStatus('');
        await new Promise(resolve => {
          fetch(`/api/marketing/campanhas/${campanha.id}/orcamento/analyse`, {
            method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}'
          }).then(async resp => {
            const reader = resp.body.getReader();
            const dec = new TextDecoder();
            let buf = '';
            while (true) {
              const { done, value } = await reader.read();
              if (done) break;
              buf += dec.decode(value, { stream: true });
              const parts = buf.split('\n\n');
              buf = parts.pop();
              for (const part of parts) {
                if (!part.startsWith('data: ')) continue;
                try {
                  const msg = JSON.parse(part.slice(6));
                  if (msg.type === 'status' && msg.step === 3) setGenStep(7);
                  if (msg.type === 'chunk')  setAnalystStream(s => s + (msg.text || ''));
                  if (msg.type === 'done')   { setAnalystResult(msg.result); setAnalystStatus(''); }
                  if (msg.type === 'error')  setAnalystStatus('Erro: ' + msg.message);
                } catch {}
              }
            }
          }).catch(e => setAnalystStatus('Erro: ' + e.message))
            .finally(() => { setAnalystRunning(false); resolve(); });
        });
      }

      cleanup();
      setToast('Orçamento gerado');
      // Forçar refresh do parent para que campanha.orcamento_analyst_json fique actualizado
      if (onAction) onAction('refreshCampanha');
    } catch(e) { cleanup(); setToast('Erro: ' + e.message); }
    setSaving(false);
    setTimeout(() => setToast(''), 2800);
  };
  React.useEffect(() => () => {
    clearInterval(genElapsedTimerRef.current);
    genStepTimersRef.current.forEach(t => clearTimeout(t));
  }, []);

  const analyzeMetaHistory = async () => {
    setAnalyzing(true);
    setAnalysisErr(null);
    try {
      const d = await campApiCall(`/api/marketing/campanhas/${campanha.id}/orcamento/analyze-meta-history`, { method: 'POST' });
      if (d.error) throw new Error(d.error);
      setAnalysis(d);
    } catch(e) { setAnalysisErr(e.message || 'Erro desconhecido'); }
    setAnalyzing(false);
  };

  const applyMetaSuggestion = () => {
    if (!analysis?.suggestion) return;
    setBudget(String(analysis.suggestion.budget_total_meta_ads));
    setAnalysis(null);
    setToast('Budget aplicado — clica "Regenerar" para actualizar linhas');
    setTimeout(() => setToast(''), 3000);
  };

  const updateRow = async (row, field, val) => {
    const updated = { ...row, [field]: val };
    // Sync pct ↔ valor: proteger divisão por zero e NaN
    const nVal = parseFloat(val);
    const nBudget = parseFloat(budget);
    const totalEur = rows.filter(r => r.country === row.country).reduce((s,r) => s + (parseFloat(r.valor_eur)||0), 0);
    if (field === 'valor_eur' && totalEur > 0 && !isNaN(nVal)) updated.pct_total = Math.round((nVal/totalEur)*100);
    if (field === 'pct_total' && nBudget > 0 && !isNaN(nVal)) updated.valor_eur = Math.round((nVal/100)*nBudget*100)/100;
    setRows(rs => rs.map(r => r.id === row.id ? updated : r));
    setSaved(s => ({...s, [row.id]: false}));
    try {
      await campApiCall(`/api/marketing/campanhas/${campanha.id}/orcamento/${row.id}`, {
        method: 'PUT',
        body: JSON.stringify({ valor_eur: updated.valor_eur, pct_total: updated.pct_total, user_email: userEmail })
      });
      setSaved(s => ({...s, [row.id]: true}));
      setTimeout(() => setSaved(s => ({...s, [row.id]: false})), 2000);
    } catch(e) { setToast('Erro ao guardar'); setTimeout(() => setToast(''), 2000); }
  };

  const saveEditRow = async (row) => {
    const newValorEur = parseFloat(editVal);
    if (isNaN(newValorEur) || newValorEur <= 0) { setEditingRow(null); return; }
    setRows(rs => rs.map(r => r.id === row.id ? { ...r, valor_eur: newValorEur } : r));
    setEditingRow(null);
    try {
      await campApiCall(`/api/marketing/campanhas/${campanha.id}/orcamento/${row.id}`, {
        method: 'PUT', body: JSON.stringify({ valor_eur: newValorEur, user_email: userEmail })
      });
      setToast('Budget actualizado');
    } catch { setToast('Erro ao guardar'); }
    setTimeout(() => setToast(''), 2000);
  };

  const approve = () => {
    if (onAction) onAction('aprovarOrcamento');
  };

  const runAnalyst = () => {
    setAnalystRunning(true);
    setAnalystResult(null);
    setAnalystStream('');
    setAnalystStatus('A iniciar análise...');
    const es = new EventSource(`/api/marketing/campanhas/${campanha.id}/orcamento/analyse`);
    // EventSource não suporta POST — usar fetch com streaming manual
    es.close();
    setAnalystRunning(true);
    fetch(`/api/marketing/campanhas/${campanha.id}/orcamento/analyse`, {
      method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}'
    }).then(async resp => {
      const reader = resp.body.getReader();
      const dec = new TextDecoder();
      let buf = '';
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buf += dec.decode(value, { stream: true });
        const parts = buf.split('\n\n');
        buf = parts.pop();
        for (const part of parts) {
          if (!part.startsWith('data: ')) continue;
          try {
            const msg = JSON.parse(part.slice(6));
            if (msg.type === 'status')  setAnalystStatus(msg.message || '');
            if (msg.type === 'chunk')   setAnalystStream(s => s + (msg.text || ''));
            if (msg.type === 'done')    { setAnalystResult(msg.result); setAnalystStatus(''); }
            if (msg.type === 'error')   { setAnalystStatus('Erro: ' + msg.message); }
          } catch {}
        }
      }
    }).catch(e => setAnalystStatus('Erro: ' + e.message))
      .finally(() => {
        setAnalystRunning(false);
        // Actualizar parent para que campanha.orcamento_analyst_json fique correcto
        if (onAction) onAction('refreshCampanha');
      });
  };

  const mktRows = rows.filter(r => r.country === activeMkt);
  const totalPct = mktRows.reduce((s,r) => s + (parseFloat(r.pct_total)||0), 0);
  const totalEur = mktRows.reduce((s,r) => s + (parseFloat(r.valor_eur)||0), 0);
  const pctOk = Math.round(totalPct) === 100;
  // Aprovação: lê do campanha.status (fiável, actualizado pelo parent via load())
  // Fallback para rows.every caso status ainda não tenha transitado
  const STATUSES_AFTER_ORCAMENTO = ['segmentacao_pendente','segmentacao_gerada','segmentacao_aprovada',
    'planeamento_pendente','planeamento_gerado','planeamento_aprovado',
    'funil_pendente','funil_gerado','funil_aprovado','pending_executive','em_aprovacao','aprovado'];
  const allApproved = STATUSES_AFTER_ORCAMENTO.includes(campanha?.status)
    || (rows.length > 0 && rows.every(r => r.approved_at));
  const budgetTotal = rows.reduce((s,r) => s + (parseFloat(r.valor_eur)||0), 0);
  const nCanais = [...new Set(rows.map(r => r.canal))].length;
  const nMercados = [...new Set(rows.map(r => r.country))].length;

  const thS = { fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', letterSpacing: '0.06em', textTransform: 'uppercase', color: 'var(--text-dim)', padding: '8px 14px', background: 'var(--bg-sunken)', borderBottom: '1px solid var(--border)' };
  const tdS = { fontSize: 13, padding: '10px 14px', borderBottom: '1px solid var(--border)', verticalAlign: 'middle' };
  const inputS = { fontSize: 13, padding: '5px 8px', borderRadius: 6, border: '1px solid var(--border)', background: 'var(--bg)', color: 'var(--text)', width: '90px', textAlign: 'right', fontFamily: 'var(--font-mono)' };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
      {/* Status strip + actions — padrão consistente */}
      {rows.length > 0 && (
        <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: allApproved ? 'var(--green-100,#E1F7E6)' : 'var(--dd-blue-100,#EBEFF9)',
            color:      allApproved ? 'var(--green-700,#1F8A52)' : 'var(--dd-primary-600,#3859D0)' }}>
            {allApproved ? 'Aprovado' : 'Gerado — aguarda aprovação'}
          </span>
          <span style={{ fontSize: 11, color: 'var(--fg-3, var(--text-muted))' }}>
            {allApproved
              ? `€${Math.round(budgetTotal).toLocaleString('pt-PT')} · ${nCanais} canal${nCanais !== 1 ? 'ais' : ''} · ${nMercados} mercado${nMercados !== 1 ? 's' : ''}`
              : `${nCanais} canal${nCanais !== 1 ? 'ais' : ''} · ${nMercados} mercado${nMercados !== 1 ? 's' : ''}`}
          </span>
          <div style={{ marginLeft: 'auto', display: 'flex', gap: 6, alignItems: 'center' }}>
            <button className="btn" onClick={generate} disabled={saving} style={{ height: 28, padding: '0 12px', fontSize: 12 }}>
              {saving ? 'A gerar...' : 'Regenerar'}
            </button>
            {!allApproved && (
              <button className="btn btn-ai" onClick={approve} style={{ height: 28, padding: '0 14px', fontSize: 12 }}>
                Aprovar Orçamento
              </button>
            )}
          </div>
        </div>
      )}

      {/* Modal Análise Meta Ads */}

      {saving && (
        <PhaseLoadingState
          steps={hasMetaAds ? ORCAMENTO_STEPS_WITH_META : ORCAMENTO_STEPS_NO_META}
          activeStep={genStep}
          elapsedSec={genElapsed}
          brandColor="#0891b2"
        />
      )}
      {loading && !saving && <div style={{ padding: 32, color: 'var(--text-muted)', fontSize: 13 }}>A carregar...</div>}

      {!loading && !saving && rows.length === 0 && (
        <PhaseEmptyState
          label="Orçamento · fase 3"
          title="Ainda sem orçamento definido"
          description={hasMetaAds
            ? "Investimento em paid media da campanha (Meta Ads, LinkedIn Ads, Google Ads). Distribui o budget pelos canais Ads da estratégia usando pesos KB por mercado. A geração inclui automaticamente análise de campanhas Meta anteriores da marca para calibrar o valor. Aprovar avança para Target. (Email · WhatsApp · Website não têm investimento em ads — não aparecem aqui.)"
            : "Investimento em paid media da campanha (Meta Ads, LinkedIn Ads, Google Ads). Distribui o budget pelos canais Ads da estratégia usando pesos KB por mercado. O budget total pode ser calibrado manualmente. Aprovar avança para Target. (Email · WhatsApp · Website não têm investimento em ads — não aparecem aqui.)"}
          ctaLabel="Gerar Orçamento →"
          onCta={generate}
          loading={saving}
          disabled={!campanha?.estrategia_json}
          disabledReason={!campanha?.estrategia_json ? 'A estratégia precisa de ser gerada e aprovada primeiro.' : null}
        />
      )}

      {!loading && !saving && rows.length > 0 && (() => {
        // Estrutura: agrupar rows por canal (com todos os mercados juntos)
        const rowsByCanal = {};
        for (const r of rows) {
          if (!rowsByCanal[r.canal]) rowsByCanal[r.canal] = [];
          rowsByCanal[r.canal].push(r);
        }
        const orderedCanais = Object.keys(rowsByCanal);
        const usps = campanha?.briefing?.usps || [];
        const nUsps = usps.length || (campanha?.proposta_json?.messaging_angles?.length) || 4;
        const timelineStart = campanha?.briefing?.timeline_start;
        const timelineDays = (() => {
          const s = timelineStart;
          const e = campanha?.briefing?.timeline_end;
          if (!s || !e) return 30;
          return Math.max(1, Math.round((new Date(e) - new Date(s)) / 864e5));
        })();

        // Naming convention Meta Ads para ad sets: [PT] Q3.08.26 ProductName · Objetivo
        const objectiveLabel = { lead_gen: 'Lead Gen', conversion: 'Conversão', awareness: 'Awareness', retention: 'Engagement' };
        const objShort = objectiveLabel[campanha?.briefing?.objective] || campanha?.briefing?.objective || 'Conversão';
        const productShort = (campanha?.briefing?.commercial_name || campanha?.titulo || '').split(/[\s·–]/)[0];
        const buildAdSetName = (country) => {
          const d = timelineStart ? new Date(timelineStart) : new Date();
          const q = Math.ceil((d.getMonth() + 1) / 3);
          const mm = String(d.getMonth() + 1).padStart(2, '0');
          const yy = String(d.getFullYear()).slice(-2);
          return `[${country}] Q${q}.${mm}.${yy} ${productShort} · ${objShort}`;
        };
        // Análise Meta Ads guardada em `analysis`
        const meta = analysis || {};
        const sug = meta.suggestion || {};
        const hs = meta.history_summary || {};
        const bt = meta.briefing_targets || {};
        const budgetTotalCampanha = rows.reduce((s, r) => s + (parseFloat(r.valor_eur) || 0), 0);
        const dailyMedio = budgetTotalCampanha / timelineDays;

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

            {/* Hierarquia Meta Ads: Canal → Campanha → Conjunto por País → Anúncios por USP */}
            {orderedCanais.map(canal => {
              const canalRows = rowsByCanal[canal];
              const canalTotalEur = canalRows.reduce((s, r) => s + (parseFloat(r.valor_eur) || 0), 0);
              const campaignMarkets = canalRows.map(r => r.country).join('/');
              const qmmyy = (() => {
                const d = timelineStart ? new Date(timelineStart) : new Date();
                const q  = Math.ceil((d.getMonth()+1)/3);
                const mm = String(d.getMonth()+1).padStart(2,'0');
                const yy = String(d.getFullYear()).slice(-2);
                return `Q${q}.${mm}.${yy}`;
              })();
              const campaignName = `[${campaignMarkets}] ${qmmyy} ${productShort} · ${objShort}`;

              return (
                <div key={canal} style={{ background: 'var(--bg-card, #fff)', border: '1px solid var(--border)', borderRadius: 10, overflow: 'hidden' }}>

                  {/* Canal header */}
                  <div style={{ padding: '10px 16px', borderBottom: '1px solid var(--border)', display: 'flex', alignItems: 'center', gap: 10 }}>
                    <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--text)', fontFamily: 'var(--font-display)', textTransform: 'uppercase', letterSpacing: '.04em' }}>{CANAL_LABEL[canal] || canal}</div>
                    <div style={{ fontSize: 11, color: 'var(--text-muted)', marginLeft: 'auto', fontFamily: 'var(--font-mono)' }}>
                      Total: <span style={{ color: 'var(--text)', fontWeight: 700 }}>€{Math.round(canalTotalEur).toLocaleString('pt-PT')}</span>
                    </div>
                  </div>

                  {/* Campanha row — nível 1 */}
                  <div style={{ padding: '8px 16px', borderBottom: '1px solid var(--border)', background: 'var(--bg-sunken)', display: 'flex', alignItems: 'center', gap: 8 }}>
                    <div style={{ width: 3, height: 14, background: 'var(--ai-500, #3859D0)', borderRadius: 2, flexShrink: 0 }} />
                    <div style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', color: 'var(--text-dim)', textTransform: 'uppercase', letterSpacing: '.06em', marginRight: 6 }}>Campanha</div>
                    <div style={{ fontSize: 11, fontFamily: 'var(--font-mono)', color: 'var(--text)', fontWeight: 600 }}>{campaignName}</div>
                    <div style={{ marginLeft: 'auto', fontSize: 11, fontFamily: 'var(--font-mono)', color: 'var(--text-muted)' }}>€{Math.round(canalTotalEur).toLocaleString('pt-PT')} total</div>
                  </div>

                  {/* Conjuntos de Anúncios — nível 2, um por país */}
                  {canalRows.map(r => {
                    const mktTotal = parseFloat(r.valor_eur) || 0;
                    const mktDaily = Math.round(mktTotal / timelineDays);
                    const adSetName = `[${r.country}] ${qmmyy} ${productShort} · ${objShort}`;
                    return (
                      <div key={r.country}>
                        {/* Ad Set row */}
                        <div style={{ padding: '8px 16px 8px 28px', borderBottom: '1px solid var(--border)', display: 'flex', alignItems: 'center', gap: 8, background: 'var(--bg-card, #fff)' }}>
                          <div style={{ width: 2, height: 12, background: 'var(--border)', borderRadius: 1, flexShrink: 0 }} />
                          <div style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', color: 'var(--text-dim)', textTransform: 'uppercase', letterSpacing: '.06em', marginRight: 6 }}>Conjunto</div>
                          <div style={{ fontSize: 11, fontFamily: 'var(--font-mono)', color: 'var(--text)', fontWeight: 600 }}>{adSetName}</div>
                          <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 6 }}>
                            {editingRow === r.id ? (
                              <>
                                <span style={{ fontSize: 11, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>€</span>
                                <input
                                  autoFocus
                                  type="number"
                                  value={editVal}
                                  onChange={e => setEditVal(e.target.value)}
                                  onBlur={() => saveEditRow(r)}
                                  onKeyDown={e => { if (e.key === 'Enter') saveEditRow(r); if (e.key === 'Escape') setEditingRow(null); }}
                                  style={{ width: 80, fontFamily: 'var(--font-mono)', fontSize: 12, fontWeight: 700, padding: '2px 6px', borderRadius: 4, border: '1px solid var(--ai-500)', textAlign: 'right' }}
                                />
                                <span style={{ fontSize: 11, color: 'var(--text-muted)' }}>total</span>
                              </>
                            ) : (
                              <>
                                <span style={{ fontSize: 12, fontFamily: 'var(--font-mono)', color: 'var(--text)', fontWeight: 700 }}>€{mktDaily}/dia</span>
                                <button onClick={() => { setEditingRow(r.id); setEditVal(String(mktTotal)); }}
                                  style={{ fontSize: 10, padding: '2px 7px', borderRadius: 4, border: '1px solid var(--border)', background: 'var(--bg-sunken)', color: 'var(--text-dim)', cursor: 'pointer' }}>
                                  Editar
                                </button>
                              </>
                            )}
                          </div>
                        </div>

                        {/* Anúncios — nível 3, um por USP */}
                        {Array.from({ length: nUsps }).map((_, i) => {
                          const uspText = usps[i] || '';
                          const adName = `[${r.country}] ${qmmyy} - AD-${String(i+1).padStart(2,'0')} ${productShort}`;
                          return (
                            <div key={i} style={{ padding: '6px 16px 6px 44px', borderBottom: i < nUsps - 1 ? '1px solid var(--border)' : 'none', display: 'flex', alignItems: 'center', gap: 8 }}>
                              <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', color: 'var(--text)', fontWeight: 600, flexShrink: 0 }}>{adName}</div>
                              {uspText && (
                                <div style={{ fontSize: 10, color: 'var(--text-muted)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>· {uspText}</div>
                              )}
                            </div>
                          );
                        })}
                      </div>
                    );
                  })}
                </div>
              );
            })}

            {/* Sumário Executivo */}
            <div style={{ background: 'var(--bg-card, #fff)', border: '1px solid var(--border)', borderRadius: 10, padding: '14px 18px', display: 'flex', gap: 24, flexWrap: 'wrap' }}>
              <div>
                <div style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--text-dim)' }}>Budget Total</div>
                <div style={{ fontSize: 20, fontWeight: 800, color: 'var(--text)', fontFamily: 'var(--font-display)' }}>€{Math.round(budgetTotalCampanha).toLocaleString('pt-PT')}</div>
              </div>
              <div>
                <div style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--text-dim)' }}>Timeline</div>
                <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--text)' }}>{timelineDays} dias</div>
              </div>
              <div>
                <div style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--text-dim)' }}>€/dia médio</div>
                <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--text)' }}>€{Math.round(dailyMedio)}</div>
              </div>
              <div>
                <div style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--text-dim)' }}>Mercados</div>
                <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--text)' }}>{markets.join(' + ')}</div>
              </div>
              {hs.campaigns_analyzed != null && (
                <div>
                  <div style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--text-dim)' }}>Análise Meta</div>
                  <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--text)' }}>{hs.campaigns_analyzed} camp. · CPL €{hs.avg_cpl || '—'} · CTR {hs.avg_ctr || '—'}%</div>
                </div>
              )}
            </div>

            {/* Recomendação do Agente */}
            {sug.recomendacao_agente && (
              <div style={{ background: 'linear-gradient(135deg, rgba(56,89,208,.06), rgba(8,145,178,.06))', border: '1px solid rgba(56,89,208,.2)', borderRadius: 10, padding: '14px 18px' }}>
                <div style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--ai-500, #3859D0)', marginBottom: 8 }}>Recomendação do Agente</div>
                <div style={{ fontSize: 13, color: 'var(--text)', lineHeight: 1.6, whiteSpace: 'pre-wrap' }}>{sug.recomendacao_agente}</div>
              </div>
            )}

            {/* Cenário Scale */}
            {sug.cenario_scale && (
              <div style={{ background: 'var(--bg-card, #fff)', border: '1px solid rgba(234,179,8,.3)', borderLeft: '3px solid #d97706', borderRadius: 10, padding: '14px 18px' }}>
                <div style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', letterSpacing: '.08em', textTransform: 'uppercase', color: '#92400e', marginBottom: 10 }}>Cenário Scale (opcional)</div>
                <div style={{ fontSize: 12, color: 'var(--text)', lineHeight: 1.7 }}>
                  <div><strong>Arranque (dias {sug.cenario_scale.fase_arranque.days}):</strong> €{sug.cenario_scale.fase_arranque.daily_por_mercado}/dia × {meta.n_markets} mercados = <span style={{ fontFamily: 'var(--font-mono)' }}>€{sug.cenario_scale.fase_arranque.total}</span></div>
                  <div><strong>Scale (dias {sug.cenario_scale.fase_scale.days}):</strong> €{sug.cenario_scale.fase_scale.daily_por_mercado}/dia × {meta.n_markets} mercados = <span style={{ fontFamily: 'var(--font-mono)' }}>€{sug.cenario_scale.fase_scale.total}</span></div>
                  <div style={{ marginTop: 6, paddingTop: 6, borderTop: '1px solid var(--border)', fontWeight: 700 }}>Total com scale: <span style={{ fontFamily: 'var(--font-mono)' }}>€{sug.cenario_scale.total_com_scale}</span> (vs €{Math.round(budgetTotalCampanha)} sem scale)</div>
                  <div style={{ marginTop: 6, fontSize: 11, color: '#92400e', fontStyle: 'italic' }}>{sug.cenario_scale.trigger}</div>
                </div>
              </div>
            )}

            {/* Justificação + Fórmula 3 âncoras + Referências */}
            {(sug.justificacao || sug.budget_formula || (meta.reference_campaigns && meta.reference_campaigns.length > 0)) && (
              <div style={{ background: 'var(--bg-card, #fff)', border: '1px solid var(--border)', borderRadius: 10, padding: '14px 18px' }}>
                <div style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--text-dim)', marginBottom: 10 }}>Justificação do Budget</div>

                {/* Fórmula 3 âncoras */}
                {sug.budget_formula && (
                  <div style={{ background: 'var(--bg-sunken)', borderRadius: 8, padding: '10px 12px', marginBottom: 10 }}>
                    <div style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', textTransform: 'uppercase', color: 'var(--text-dim)', marginBottom: 8, letterSpacing: '.06em' }}>Fórmula 3 Âncoras · Confiança {sug.budget_formula.confidence?.toUpperCase()}</div>
                    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 8, marginBottom: 8 }}>
                      {[
                        { label: 'Briefing', valor: sug.budget_formula.ancora_briefing?.valor, peso: sug.budget_formula.ancora_briefing?.peso, color: '#3859D0' },
                        { label: 'Histórico ponderado', valor: sug.budget_formula.ancora_historico?.valor, peso: sug.budget_formula.ancora_historico?.peso, color: '#0891b2' },
                        { label: 'KB B2B PT/ES', valor: sug.budget_formula.ancora_kb?.valor, peso: sug.budget_formula.ancora_kb?.peso, color: '#059669' },
                      ].map(a => (
                        <div key={a.label} style={{ background: '#fff', borderRadius: 6, padding: '8px 10px', borderTop: `3px solid ${a.color}` }}>
                          <div style={{ fontSize: 9, fontWeight: 700, color: a.color, textTransform: 'uppercase', letterSpacing: '.06em', marginBottom: 2 }}>{a.label}</div>
                          <div style={{ fontSize: 14, fontWeight: 800, fontFamily: 'var(--font-mono)', color: 'var(--text)' }}>€{a.valor}</div>
                          <div style={{ fontSize: 10, color: 'var(--text-muted)' }}>peso {a.peso}%</div>
                        </div>
                      ))}
                    </div>
                    <div style={{ display: 'flex', gap: 16, fontSize: 11, color: 'var(--text-muted)', flexWrap: 'wrap' }}>
                      <span>CPL blended: <strong style={{ color: 'var(--text)', fontFamily: 'var(--font-mono)' }}>€{sug.budget_formula.cpl_blended}</strong></span>
                      <span>Margem segurança: <strong style={{ color: 'var(--text)', fontFamily: 'var(--font-mono)' }}>+{sug.budget_formula.margem}%</strong></span>
                      {sug.budget_formula.fator_ctr !== 1.0 && (
                        <span>Factor CTR: <strong style={{ color: 'var(--text)', fontFamily: 'var(--font-mono)' }}>{sug.budget_formula.fator_ctr}×</strong></span>
                      )}
                    </div>
                  </div>
                )}

                {sug.justificacao && (
                  <div style={{ fontSize: 11, color: 'var(--text-muted)', lineHeight: 1.6, marginBottom: 8, paddingLeft: 10, borderLeft: '3px solid var(--ai-500)' }}>{sug.justificacao}</div>
                )}
                {meta.reference_campaigns?.length > 0 && (
                  <>
                    <div style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--text-dim)', marginTop: 12, marginBottom: 6 }}>Campanhas de referência</div>
                    <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 11 }}>
                      <thead><tr style={{ borderBottom: '1px solid var(--border)' }}>
                        <th style={{ textAlign: 'left', padding: '5px 8px', color: 'var(--text-dim)' }}>Nome</th>
                        <th style={{ textAlign: 'right', padding: '5px 8px', color: 'var(--text-dim)' }}>Spend</th>
                        <th style={{ textAlign: 'right', padding: '5px 8px', color: 'var(--text-dim)' }}>Leads</th>
                        <th style={{ textAlign: 'right', padding: '5px 8px', color: 'var(--text-dim)' }}>CPL</th>
                        <th style={{ textAlign: 'right', padding: '5px 8px', color: 'var(--text-dim)' }}>CTR</th>
                      </tr></thead>
                      <tbody>
                        {meta.reference_campaigns.map((rc, i) => (
                          <tr key={i} style={{ borderBottom: '1px solid var(--border)' }}>
                            <td style={{ padding: '5px 8px', color: 'var(--text)' }}>{rc.name?.slice(0, 45)}</td>
                            <td style={{ padding: '5px 8px', textAlign: 'right', fontFamily: 'var(--font-mono)' }}>€{rc.spend}</td>
                            <td style={{ padding: '5px 8px', textAlign: 'right', fontFamily: 'var(--font-mono)' }}>{rc.leads || '—'}</td>
                            <td style={{ padding: '5px 8px', textAlign: 'right', fontFamily: 'var(--font-mono)', fontWeight: 600 }}>{rc.cpl ? `€${rc.cpl}` : '—'}</td>
                            <td style={{ padding: '5px 8px', textAlign: 'right', fontFamily: 'var(--font-mono)' }}>{rc.ctr ? `${rc.ctr}%` : '—'}</td>
                          </tr>
                        ))}
                      </tbody>
                    </table>
                  </>
                )}
              </div>
            )}
          </div>
        );
      })()}

      {/* Meta Ads Analyst Agent */}
      {!loading && !saving && rows.length > 0 && hasMetaAds && (
        <div style={{ background: 'var(--bg-card, #fff)', border: '1px solid var(--border)', borderRadius: 10, overflow: 'hidden' }}>
          <div style={{ padding: '12px 16px', borderBottom: '1px solid var(--border)', display: 'flex', alignItems: 'center', gap: 10 }}>
            <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--text)', fontFamily: 'var(--font-display)', textTransform: 'uppercase', letterSpacing: '.04em' }}>Meta Ads Analyst</div>
            <div style={{ fontSize: 11, color: 'var(--text-muted)' }}>Diagnóstico · Recomendações · Previsão</div>
            {analystResult && !analystRunning && (
              <button className="btn" style={{ marginLeft: 'auto', fontSize: 11 }} onClick={runAnalyst}>Reanalisar</button>
            )}
          </div>

          {analystRunning && (
            <div style={{ padding: '16px 20px' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
                <div style={{ width: 8, height: 8, borderRadius: '50%', background: 'var(--ai-500)', animation: 'pulse 1.2s infinite' }} />
                <span style={{ fontSize: 12, color: 'var(--text-muted)' }}>{analystStatus || 'A analisar...'}</span>
              </div>
              {analystStream && (
                <div style={{ fontSize: 12, color: 'var(--text)', lineHeight: 1.7, fontFamily: 'var(--font-mono)', whiteSpace: 'pre-wrap', opacity: .8 }}>{analystStream}</div>
              )}
            </div>
          )}

          {analystResult && !analystRunning && (() => {
            const r = analystResult;
            const sections = [
              { key: 'diagnostico',   label: 'Diagnóstico',    border: '#3859D0' },
              { key: 'orcamento',     label: 'Orçamento',      border: '#0891b2' },
              { key: 'recomendacoes', label: 'Recomendações',  border: '#059669' },
              { key: 'previsao',      label: 'Previsão',       border: '#d97706' },
            ];
            return (
              <div style={{ padding: '16px 20px', display: 'flex', flexDirection: 'column', gap: 14 }}>
                {sections.map(s => r[s.key] ? (
                  <div key={s.key} style={{ paddingLeft: 12, borderLeft: `3px solid ${s.border}` }}>
                    <div style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', textTransform: 'uppercase', letterSpacing: '.06em', color: s.border, marginBottom: 4 }}>{s.label}</div>
                    <div style={{ fontSize: 12, color: 'var(--text)', lineHeight: 1.7, whiteSpace: 'pre-wrap' }}>{r[s.key]}</div>
                  </div>
                ) : null)}
                {r.meta_history && (
                  <div style={{ borderTop: '1px solid var(--border)', paddingTop: 12, marginTop: 4 }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: r.meta_history.campaigns?.length > 0 ? 8 : 0 }}>
                      <span style={{ fontSize: 10, fontWeight: 700, fontFamily: 'var(--font-mono)', textTransform: 'uppercase', letterSpacing: '.06em', color: 'var(--text-dim)' }}>
                        Campanhas analisadas ({r.meta_history.campaigns_analyzed})
                      </span>
                      {r.meta_history.no_product_history && (
                        <span style={{ fontSize: 10, color: '#92400e', background: '#FEF3C7', padding: '1px 6px', borderRadius: 3 }}>sem histórico do produto — dados da conta</span>
                      )}
                      <span style={{ marginLeft: 'auto', fontSize: 10, fontFamily: 'var(--font-mono)', color: 'var(--text-muted)' }}>
                        CPL €{r.meta_history.avg_cpl || '—'} · CTR {r.meta_history.avg_ctr || '—'}% · CPM €{r.meta_history.avg_cpm || '—'}
                      </span>
                    </div>
                    {r.meta_history.campaigns?.length > 0 && (
                      <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 10 }}>
                        <thead><tr style={{ borderBottom: '1px solid var(--border)' }}>
                          <th style={{ textAlign: 'left', padding: '4px 6px', color: 'var(--text-dim)', fontWeight: 600 }}>Campanha</th>
                          <th style={{ textAlign: 'right', padding: '4px 6px', color: 'var(--text-dim)', fontWeight: 600 }}>Spend</th>
                          <th style={{ textAlign: 'right', padding: '4px 6px', color: 'var(--text-dim)', fontWeight: 600 }}>CPL</th>
                          <th style={{ textAlign: 'right', padding: '4px 6px', color: 'var(--text-dim)', fontWeight: 600 }}>CTR</th>
                        </tr></thead>
                        <tbody>
                          {r.meta_history.campaigns.map((cp, i) => (
                            <tr key={i} style={{ borderBottom: '1px solid var(--border)' }}>
                              <td style={{ padding: '4px 6px', color: 'var(--text-muted)' }}>{(cp.name || '').slice(0, 50)}</td>
                              <td style={{ padding: '4px 6px', textAlign: 'right', fontFamily: 'var(--font-mono)' }}>€{cp.spend}</td>
                              <td style={{ padding: '4px 6px', textAlign: 'right', fontFamily: 'var(--font-mono)', fontWeight: 600 }}>{cp.cpl ? `€${cp.cpl}` : '—'}</td>
                              <td style={{ padding: '4px 6px', textAlign: 'right', fontFamily: 'var(--font-mono)' }}>{cp.ctr ? `${cp.ctr}%` : '—'}</td>
                            </tr>
                          ))}
                        </tbody>
                      </table>
                    )}
                  </div>
                )}
              </div>
            );
          })()}

        </div>
      )}

      {toast && <div style={{ position: 'fixed', bottom: 24, right: 24, background: 'var(--text)', color: '#fff', padding: '10px 18px', borderRadius: 8, fontSize: 13, zIndex: 9999 }}>{toast}</div>}
    </div>
  );
};

// ── TabFunilMulticanal — funil vertical 5 layers · design system portal ────────
const _LAYER_META = [
  { id: 'l1', num: 'LAYER 1', title: 'TRAFFIC SOURCES',        color: '#0F4C75', bg: 'rgba(15,76,117,.05)'   },
  { id: 'l2', num: 'LAYER 2', title: 'CONVERSION HUB',         color: '#3859D0', bg: 'rgba(56,89,208,.05)'   },
  { id: 'l3', num: 'LAYER 3', title: 'DIGI AI · QUALIFICAÇÃO', color: '#065F46', bg: 'rgba(6,95,70,.05)'     },
  { id: 'l4', num: 'LAYER 4', title: 'HANDOFF COMERCIAL',      color: '#92400E', bg: 'rgba(146,64,14,.05)'   },
  { id: 'l5', num: 'LAYER 5', title: 'NURTURING WA',           color: '#4C1D95', bg: 'rgba(76,29,149,.05)'   },
];

const _CH_ICONS = {
  meta_ads:           <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="2" width="9" height="9" rx="1"/><rect x="13" y="2" width="9" height="9" rx="1"/><rect x="2" y="13" width="9" height="9" rx="1"/><rect x="13" y="13" width="9" height="9" rx="1"/></svg>,
  linkedin_ads:       <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M6 9v9"/><circle cx="6" cy="5.5" r="1"/><path d="M10 18v-5a2 2 0 014 0v5"/><path d="M18 18v-4"/></svg>,
  email:              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m2 7 10 7 10-7"/></svg>,
  whatsapp:           <svg width="16" height="16" viewBox="0 0 24 24" fill="white"><path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413z"/><path d="M12 0C5.376 0 0 5.376 0 12c0 2.11.554 4.085 1.518 5.793L0 24l6.37-1.487A11.955 11.955 0 0 0 12 24C18.624 24 24 18.624 24 12S18.624 0 12 0z"/></svg>,
  google_ads_search:  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>,
  google_ads_display: <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>,
  muppi_led:          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="7" width="20" height="15" rx="2"/><path d="M16 3h-2l-2 4H8l-2-4H4"/></svg>,
  website:            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10A15.3 15.3 0 0112 2z"/></svg>,
};

const _NURTURE_STAGES = [
  { num: 'Stage 10', name: 'Entrada no Funil',  action: 'Confirmar perfil · identificar dor dominante · propor próximo passo' },
  { num: 'Stage 20', name: 'Lead Qualificada',  action: 'Aprofundar dor · dado concreto do cluster · propor demo' },
  { num: 'Stage 50', name: 'Pré-Demo',          action: 'Confirmar presença · pedir amostras para testar ao vivo' },
  { num: 'Stage 65', name: 'Follow-up 48h',     action: 'Reforçar o que viu · tratar objecção · propor próximo passo' },
  { num: 'Stage 80', name: 'Sem Resposta +5d',  action: 'Reactivar com nova perspectiva · financiamento como alternativa' },
  { num: 'Stage 90', name: 'Proposta Enviada',  action: 'Fechar · tratar objecção de preço · confirmar condições finais' },
];

const TabFunilMulticanal = ({ campanha, onAction }) => {
  const bCol = _brandColor(campanha?.brand_slug);

  const [activeMkt, setActiveMkt] = React.useState(() => (campanha?.estrategia_json?.markets || [])[0]?.country || null);
  const [sending,   setSending]   = React.useState(false);
  const [ctx,       setCtx]       = React.useState(null);  // funil-context (fonte única)
  const [loading,   setLoading]   = React.useState(false);

  const status     = campanha?.status || '';
  const isPending  = ['pending_executive','em_aprovacao'].includes(status);
  const isApproved = ['em_producao','publicado','concluida'].includes(status);
  const canSendToApproval = ['funil_pendente','planeamento_aprovado'].includes(status) || (!isPending && !isApproved);
  const statusLabel = isApproved ? 'Campanha aprovada' : isPending ? 'Enviado para aprovação executiva' : 'Pronto para revisão';
  const statusBg    = isApproved ? 'var(--green-100,#E1F7E6)' : isPending ? '#FEF3C7' : 'var(--dd-blue-100,#EBEFF9)';
  const statusFg    = isApproved ? 'var(--green-700,#1F8A52)' : isPending ? '#92400E' : 'var(--dd-primary-600,#3859D0)';

  React.useEffect(() => {
    if (!campanha?.id) return;
    setLoading(true);
    campApiCall('/api/marketing/campanhas/' + campanha.id + '/funil-context')
      .then(d => { setCtx(d); if (d?.estrategia?.markets?.length) setActiveMkt(d.estrategia.markets[0].country); })
      .catch(() => null)
      .finally(() => setLoading(false));
  }, [campanha?.id]);

  // Dados derivados do ctx (fonte única) com fallback para campanha prop
  const brief    = ctx?.briefing   || campanha?.briefing || {};
  const markets  = ctx?.estrategia?.markets || campanha?.estrategia_json?.markets || [];
  const commPlan = ctx?.conceito?.comm_plan || campanha?.proposta_json?.comm_plan || [];
  const sdrAI    = ctx?.sdr_ai || null;
  const nurtStages = ctx?.nurturing_stages?.length ? ctx.nurturing_stages : _NURTURE_STAGES.map(s => ({ stage: s.num, nome: s.name, accao: s.action }));
  const estrategia = ctx?.estrategia || campanha?.estrategia_json;

  if (!estrategia || !markets.length) return (
    <PhaseEmptyState
      label="Funil Multicanal · fase 6"
      title="Ainda sem funil para visualizar"
      description="Síntese visual da campanha em 5 layers: Traffic Sources → Conversion Hub → Digi AI Qualificação → Handoff Comercial → Nurturing WA. Gera a estratégia primeiro para ter dados a mostrar."
      disabled
      disabledReason="A estratégia precisa de ser gerada primeiro."
    />
  );

  const mkt      = markets.find(m => m.country === activeMkt) || markets[0];
  const chFit    = mkt?.channel_mix || [];
  const personas = mkt?.personas_priorizadas || [];
  const msgU     = mkt?.mensagem_unificada || {};
  const kpis     = mkt?.kpis || [];
  const tom      = mkt?.tom || '';

  // Parse commercial offer (can be string or object in briefing)
  const offerRaw = brief.commercial_offer;
  const offer    = offerRaw
    ? (typeof offerRaw === 'string' ? (() => { try { return JSON.parse(offerRaw); } catch { return { descricao: offerRaw }; } })() : offerRaw)
    : {};
  const offerLabel = offer.tipo === 'digirent'   ? `DigiRent €${offer.valor_mensal || '—'}/mês`
                   : offer.tipo === 'printplan'   ? `PrintPlan €${offer.custo_por_m2 || '—'}/m²`
                   : offer.tipo === 'financiamento'? `Financiamento ${offer.prazo || ''}`
                   : offer.descricao || 'Oferta comercial';

  // Layer 1 = canais de tráfego pagos/email — blog/website é Conversion Hub (Layer 2)
  const LAYER1_CANAIS = new Set(['meta_ads','linkedin_ads','google_ads_search','google_ads_display','email','whatsapp','muppi_led']);
  const allChannels = [];
  const seen = new Set();
  markets.forEach(m => {
    (m.channel_mix || []).forEach(ch => {
      if (LAYER1_CANAIS.has(ch.canal) && !seen.has(ch.canal)) {
        seen.add(ch.canal); allChannels.push({ ...ch, country: m.country });
      }
    });
  });

  // Comm plan helpers
  const emailRows    = commPlan.filter(r => r.content_type === 'email_html' || r.canal === 'email').slice(0, 3);
  const blogRows     = commPlan.filter(r => r.content_type === 'blog_post'  || r.canal === 'website').slice(0, 2);
  const metaRows     = commPlan.filter(r => r.canal === 'meta_ads').slice(0, 2);

  // Segmentation CRM segments
  const crmSegs = ctx?.target?.crm?.length
    ? ctx.target.crm
    : [];
  const metaSeg = ctx?.target?.ads?.length ? ctx.target.ads : [];

  // Pain points (first 3)
  const pains = (Array.isArray(brief.pain_points) ? brief.pain_points : []).slice(0, 3);

  // USPs (first 3)
  const usps = (Array.isArray(brief.usps) ? brief.usps : []).slice(0, 3);

  // Persona split: primary (peso >= 60) / secondary
  const primaryPersonas   = personas.filter(p => (p.peso || 0) >= 60 || p.prioridade === 'primary');
  const secondaryPersonas = personas.filter(p => (p.peso || 0) < 60  && p.prioridade !== 'primary');

  // Language variants
  const langVariants = [...new Set(markets.map(m => m.language_variant || m.country || ''))].filter(Boolean);

  // ── Shared style helpers ──
  const layerHeader = (col) => ({
    background: col, padding: '10px 24px', display: 'flex', alignItems: 'center', gap: 10,
    fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: '#fff',
    fontFamily: 'var(--font-display)',
  });
  const layerBody = (bg) => ({ padding: '18px 20px 20px', background: bg });
  const connector = {
    display: 'flex', alignItems: 'center', justifyContent: 'center',
    padding: '4px 0', background: 'transparent', position: 'relative',
  };
  const lNum = () => ({ background: 'rgba(255,255,255,.22)', padding: '2px 8px', borderRadius: 99, fontSize: 10, letterSpacing: '0.08em' });
  const card = (borderColor, extra) => ({
    background: 'var(--bg-card, #fff)', borderRadius: 10, border: '1px solid var(--border)',
    borderTop: `3px solid ${borderColor}`, padding: '14px 14px 12px',
    boxShadow: '0 1px 3px rgba(17,41,84,.06)', ...extra,
  });
  const metaLabel = (col) => ({ fontSize: 9, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: col, fontFamily: 'var(--font-mono)', marginBottom: 4 });
  const bigNum    = { fontSize: 20, fontWeight: 800, fontFamily: 'var(--font-display)', color: 'var(--navy,#112954)', lineHeight: 1 };
  const smallTag  = (col, bg) => ({ display: 'inline-block', fontSize: 9, fontWeight: 700, letterSpacing: '0.05em', textTransform: 'uppercase', padding: '2px 7px', borderRadius: 99, background: bg || `${col}14`, color: col, border: `1px solid ${col}30` });
  const dot = (col) => ({ width: 6, height: 6, borderRadius: '50%', background: col, flexShrink: 0, marginTop: 5 });

  const Connector = () => (
    <div style={connector}>
      <div style={{ position: 'absolute', left: '50%', top: 0, bottom: 0, width: 2, background: 'var(--border)', transform: 'translateX(-50%)' }} />
      <div style={{ position: 'relative', zIndex: 1, width: 28, height: 28, background: 'var(--navy,#112954)', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
      </div>
    </div>
  );

  const ChIcon = ({ canal, size = 28, bg }) => (
    <div style={{ width: size, height: size, borderRadius: 7, background: bg || _LAYER_META[0].color, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
      {_CH_ICONS[canal] || <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2"><circle cx="12" cy="12" r="10"/></svg>}
    </div>
  );

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

      {/* ── Status strip ── */}
      <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)', marginBottom: 14 }}>
        <span style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', padding: '3px 8px', borderRadius: 'var(--radius-xs,4px)', background: statusBg, color: statusFg }}>
          {statusLabel}
        </span>
        <span style={{ fontSize: 11, color: 'var(--fg-3,var(--text-muted))' }}>
          {markets.length} mercado{markets.length !== 1 ? 's' : ''} · {allChannels.length} canais · 5 layers
          {loading && ' · a carregar dados...'}
        </span>
        {isPending && (
          <div style={{ marginLeft: 'auto' }}>
            <span style={{ fontSize: 10, fontWeight: 700, padding: '3px 10px', borderRadius: 99, background: '#FEF3C7', color: '#92400E', fontFamily: 'var(--font-mono)' }}>Aguarda aprovação executiva</span>
          </div>
        )}
      </div>

      {/* ════════════════════════════════════════
          LAYER 1 — TRAFFIC SOURCES
      ════════════════════════════════════════ */}
      <div>
        <div style={layerHeader(_LAYER_META[0].color)}>
          <span style={lNum()}>{_LAYER_META[0].num}</span>
          {_LAYER_META[0].title}
          <span style={{ marginLeft: 'auto', fontSize: 10, fontWeight: 500, opacity: .7 }}>{allChannels.length} canais activos</span>
        </div>
        <div style={layerBody(_LAYER_META[0].bg)}>
          <div style={{ display: 'grid', gridTemplateColumns: `repeat(${Math.min(allChannels.length || 3, 3)}, 1fr)`, gap: 12 }}>
            {allChannels.map((ch, i) => {
              const L1col = _LAYER_META[0].color;
              // Audience from segmentacao for this channel
              const audSeg  = metaSeg.find(s => s.canal === ch.canal || s.canal === 'meta_ads');
              const audSize = audSeg?.audiencia_estimada || audSeg?.audience_size;
              // Comm plan rows for this channel
              const planRows = ch.canal === 'email'    ? emailRows
                             : ch.canal === 'website' ? blogRows
                             : ch.canal === 'meta_ads' ? metaRows
                             : (ctx?.planeamento?.rows || []).filter(r => r.canal === ch.canal).slice(0, 2);
              return (
                <div key={i} style={card(L1col)}>
                  <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10, marginBottom: 10 }}>
                    <ChIcon canal={ch.canal} size={32} bg={L1col} />
                    <div style={{ minWidth: 0 }}>
                      <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--navy,#112954)', lineHeight: 1.2, fontFamily: 'var(--font-display)' }}>
                        {CANAL_LABEL[ch.canal] || ch.canal}
                      </div>
                      <div style={{ fontSize: 10, color: 'var(--text-muted)', marginTop: 2 }}>{ch.country || mkt?.country || ''}</div>
                    </div>
                    {ch.pct != null && (
                      <div style={{ marginLeft: 'auto', flexShrink: 0, ...smallTag(L1col) }}>{ch.pct}%</div>
                    )}
                  </div>

                  {/* Type / audience */}
                  {ch.canal === 'meta_ads' && (
                    <div style={{ marginBottom: 8 }}>
                      <div style={metaLabel(L1col)}>Formato</div>
                      <div style={{ fontSize: 11, color: 'var(--text)', fontWeight: 600 }}>Lead Gen Form</div>
                      {audSize && (
                        <div style={{ marginTop: 4 }}>
                          <div style={metaLabel(L1col)}>Audiencia estimada</div>
                          <div style={{ ...bigNum, fontSize: 18 }}>{audSize}</div>
                        </div>
                      )}
                    </div>
                  )}

                  {/* Planned dates from comm plan */}
                  {planRows.length > 0 && (
                    <div>
                      <div style={metaLabel(L1col)}>Datas planeadas</div>
                      {planRows.map((r, j) => (
                        <div key={j} style={{ fontSize: 10, color: '#475569', display: 'flex', gap: 6, alignItems: 'flex-start', marginBottom: 2 }}>
                          <div style={dot(L1col)} />
                          <span style={{ fontFamily: 'var(--font-mono)', fontWeight: 600 }}>{r.planned_date || r.data || '—'}</span>
                          {r.titulo && <span style={{ color: 'var(--text-muted)' }}>{r.titulo.slice(0, 40)}</span>}
                        </div>
                      ))}
                    </div>
                  )}
                </div>
              );
            })}
          </div>
        </div>
      </div>

      <Connector />

      {/* ════════════════════════════════════════
          LAYER 2 — CONVERSION HUB
      ════════════════════════════════════════ */}
      <div>
        <div style={layerHeader(_LAYER_META[1].color)}>
          <span style={lNum()}>{_LAYER_META[1].num}</span>
          {_LAYER_META[1].title}
        </div>
        <div style={layerBody(_LAYER_META[1].bg)}>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 16 }}>

            {/* Card 1 — Blog Post · Content Hub */}
            <div style={card(_LAYER_META[1].color)}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
                <div style={{ width: 36, height: 36, borderRadius: 8, background: _LAYER_META[1].color, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
                </div>
                <div>
                  <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--navy,#112954)', fontFamily: 'var(--font-display)' }}>Blog Post</div>
                  <div style={{ fontSize: 10, color: 'var(--text-muted)' }}>Content Hub · entrada via Email / Orgânico</div>
                </div>
              </div>
              {blogRows.length > 0 ? (
                <div style={{ marginBottom: 12 }}>
                  <div style={metaLabel(_LAYER_META[1].color)}>Conteúdos planeados</div>
                  {blogRows.map((r, j) => (
                    <div key={j} style={{ display: 'flex', gap: 6, alignItems: 'flex-start', marginBottom: 4 }}>
                      <div style={dot(_LAYER_META[1].color)} />
                      <div>
                        <div style={{ fontSize: 11, color: 'var(--text)', fontWeight: 600, lineHeight: 1.3 }}>{r.titulo || r.title || `Artigo ${j+1}`}</div>
                        {r.planned_date && <div style={{ fontSize: 9, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>{r.planned_date}</div>}
                      </div>
                    </div>
                  ))}
                </div>
              ) : (
                <div style={{ marginBottom: 12 }}>
                  <div style={metaLabel(_LAYER_META[1].color)}>Função</div>
                  <div style={{ fontSize: 11, color: 'var(--text)', lineHeight: 1.5 }}>
                    Artigo de conversão com argumentos técnicos e ROI. Email linka para o blog post que fecha com CTA WhatsApp.
                  </div>
                </div>
              )}
              {msgU.decision && (
                <div>
                  <div style={metaLabel(_LAYER_META[1].color)}>Mensagem de conversão</div>
                  <div style={{ fontSize: 11, color: 'var(--text)', lineHeight: 1.5, fontStyle: 'italic' }}>"{msgU.decision}"</div>
                </div>
              )}
            </div>

            {/* Card 2 — Lista CRM · Qualificação Digi AI */}
            <div style={card(_LAYER_META[1].color)}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
                <div style={{ width: 36, height: 36, borderRadius: 8, background: _LAYER_META[1].color, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
                </div>
                <div>
                  <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--navy,#112954)', fontFamily: 'var(--font-display)' }}>Lista CRM Marketing</div>
                  <div style={{ fontSize: 10, color: 'var(--text-muted)' }}>Leads Meta Ads · Lead Gen Form</div>
                </div>
              </div>
              <div style={{ marginBottom: 12 }}>
                <div style={metaLabel(_LAYER_META[1].color)}>Origem</div>
                <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 4 }}>
                  {allChannels.filter(c => c.canal === 'meta_ads' || c.canal === 'linkedin_ads').map((c, i) => (
                    <span key={i} style={smallTag(_LAYER_META[1].color)}>{CANAL_LABEL[c.canal] || c.canal}</span>
                  ))}
                  {allChannels.filter(c => c.canal === 'meta_ads' || c.canal === 'linkedin_ads').length === 0 && (
                    <span style={smallTag(_LAYER_META[1].color)}>Meta Ads</span>
                  )}
                </div>
              </div>
              {crmSegs.length > 0 ? (
                <div style={{ marginBottom: 12 }}>
                  <div style={metaLabel(_LAYER_META[1].color)}>Segmentos CRM configurados</div>
                  {crmSegs.slice(0, 3).map((seg, i) => (
                    <div key={i} style={{ display: 'flex', gap: 6, alignItems: 'flex-start', marginBottom: 4 }}>
                      <div style={dot(_LAYER_META[1].color)} />
                      <div style={{ fontSize: 11, color: 'var(--text)', lineHeight: 1.3 }}>{seg.nome || seg.name || `Segmento ${i+1}`}</div>
                    </div>
                  ))}
                </div>
              ) : (
                <div style={{ marginBottom: 12 }}>
                  <div style={metaLabel(_LAYER_META[1].color)}>Fluxo</div>
                  <div style={{ fontSize: 11, color: 'var(--text)', lineHeight: 1.5 }}>
                    Lead preenche form → entra na lista CRM Marketing → qualificação automática via Digi AI SDR.
                  </div>
                </div>
              )}
              {tom && (
                <div>
                  <div style={metaLabel(_LAYER_META[1].color)}>Tom</div>
                  <div style={{ fontSize: 11, color: 'var(--text)', fontWeight: 600 }}>{tom}</div>
                </div>
              )}
            </div>
          </div>
        </div>
      </div>

      <Connector />

      {/* ════════════════════════════════════════
          LAYER 3 — DIGI AI · QUALIFICACAO
      ════════════════════════════════════════ */}
      <div>
        <div style={layerHeader(_LAYER_META[2].color)}>
          <span style={lNum()}>{_LAYER_META[2].num}</span>
          {_LAYER_META[2].title}
          <span style={{ marginLeft: 'auto', fontSize: 10, fontWeight: 500, opacity: .7 }}>
            {langVariants.join(' + ')} · Stage 10 → 20
          </span>
        </div>
        <div style={layerBody(_LAYER_META[2].bg)}>

          {/* SDR AI · processo completo de qualificação */}
          <div style={card(_LAYER_META[2].color)}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 14 }}>
              <div style={{ width: 28, height: 28, background: _LAYER_META[2].color, borderRadius: 7, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
              </div>
              <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--navy,#112954)', fontFamily: 'var(--font-display)' }}>Digi AI · Processo de Qualificação SDR</div>
              {langVariants.length > 1 && langVariants.map(l => <span key={l} style={{ marginLeft: 4, ...smallTag(_LAYER_META[2].color) }}>{l}</span>)}
              {sdrAI?.score_alvo && <span style={{ marginLeft: 'auto', ...smallTag(_LAYER_META[2].color) }}>{sdrAI.score_alvo}</span>}
            </div>

            {sdrAI ? (
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 14 }}>

                {/* Col 1 — Abertura + Perguntas */}
                <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                  {sdrAI.abertura_sugerida && (
                    <div>
                      <div style={metaLabel(_LAYER_META[2].color)}>1 · Abertura WA (Msg inicial)</div>
                      <div style={{ fontSize: 11, color: 'var(--text)', lineHeight: 1.5, fontStyle: 'italic', background: 'rgba(6,95,70,.05)', padding: '8px 10px', borderRadius: 6, borderLeft: `3px solid ${_LAYER_META[2].color}` }}>"{sdrAI.abertura_sugerida}"</div>
                    </div>
                  )}
                  {sdrAI.perguntas_chave?.length > 0 && (
                    <div>
                      <div style={metaLabel(_LAYER_META[2].color)}>2 · Perguntas de qualificação</div>
                      {sdrAI.perguntas_chave.map((q, i) => (
                        <div key={i} style={{ display: 'flex', gap: 7, marginBottom: 5, alignItems: 'flex-start' }}>
                          <div style={{ background: _LAYER_META[2].color, color: '#fff', borderRadius: 4, padding: '1px 5px', fontSize: 9, fontWeight: 700, fontFamily: 'var(--font-mono)', flexShrink: 0, marginTop: 2 }}>{i+1}</div>
                          <span style={{ fontSize: 11, color: 'var(--text)', lineHeight: 1.4 }}>{q}</span>
                        </div>
                      ))}
                    </div>
                  )}
                </div>

                {/* Col 2 — Argumento produto + Argumento confiança */}
                <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                  {sdrAI.argumento_produto && (
                    <div>
                      <div style={metaLabel(_LAYER_META[2].color)}>3 · Argumento ROI (Ten#1 Lógico)</div>
                      <div style={{ fontSize: 11, color: 'var(--text)', lineHeight: 1.5 }}>{sdrAI.argumento_produto}</div>
                    </div>
                  )}
                  {sdrAI.argumento_confianca && (
                    <div>
                      <div style={metaLabel(_LAYER_META[2].color)}>4 · Argumento confiança (Ten#2)</div>
                      <div style={{ fontSize: 11, color: 'var(--text)', lineHeight: 1.5 }}>{sdrAI.argumento_confianca}</div>
                    </div>
                  )}
                </div>

                {/* Col 3 — Objeção + Critério handoff */}
                <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                  {sdrAI.objecao_principal && (
                    <div>
                      <div style={metaLabel(_LAYER_META[2].color)}>5 · Objeção principal + resposta</div>
                      <div style={{ fontSize: 11, color: 'var(--text)', lineHeight: 1.5 }}>{sdrAI.objecao_principal}</div>
                    </div>
                  )}
                  {sdrAI.criterio_handoff && (
                    <div style={{ background: `${_LAYER_META[2].color}0D`, borderRadius: 8, padding: '8px 10px', borderLeft: `3px solid ${_LAYER_META[2].color}` }}>
                      <div style={metaLabel(_LAYER_META[2].color)}>6 · Critério handoff → Stage 65</div>
                      <div style={{ fontSize: 11, color: 'var(--text)', lineHeight: 1.5, fontWeight: 600 }}>{sdrAI.criterio_handoff}</div>
                    </div>
                  )}
                </div>
              </div>
            ) : (() => {
              const painStr = pains.length > 0 ? (typeof pains[0] === 'string' ? pains[0] : pains[0].descricao || '') : '—';
              const uspStr  = usps.length  > 0 ? (typeof usps[0]  === 'string' ? usps[0]  : usps[0].usp || '') : '—';
              const prod    = brief.commercial_name || campanha?.titulo || 'produto';
              const decisor = brief.decision_maker || 'decisor';
              return (
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 14 }}>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                  <div>
                    <div style={metaLabel(_LAYER_META[2].color)}>0 · Enriquecimento automático</div>
                    <div style={{ fontSize: 10, color: 'var(--text-muted)', lineHeight: 1.5 }}>Lead entra via {allChannels.map(c => CANAL_LABEL[c.canal] || c.canal).join(' / ')} → match Gestor por email/telefone → carrega histórico OP + SAT + Digi Brain (Three Tens anteriores + Pain Profile).</div>
                  </div>
                  <div>
                    <div style={metaLabel(_LAYER_META[2].color)}>1 · Abertura Straight Line (4 seg)</div>
                    <div style={{ fontSize: 10, color: 'var(--text)', lineHeight: 1.5, fontStyle: 'italic', background: 'rgba(6,95,70,.05)', padding: '6px 8px', borderRadius: 6, borderLeft: `3px solid ${_LAYER_META[2].color}` }}>
                      "Olá [nome], vi que demonstrou interesse em {prod}. Trabalha com impressão/personalização têxtil?"
                    </div>
                    <div style={{ fontSize: 9, color: 'var(--text-dim)', marginTop: 3, fontFamily: 'var(--font-mono)' }}>Tom: Absolute Certainty · Pace, Pace, Lead</div>
                  </div>
                </div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                  <div>
                    <div style={metaLabel(_LAYER_META[2].color)}>2 · Intelligence Gathering SPIN</div>
                    {[
                      `S — Situação: que equipamento usa actualmente?`,
                      `P — Problema: qual a maior dificuldade? ("${painStr.slice(0,60)}${painStr.length>60?'...':''}")`,
                      `I — Implicação: o que isso custa em tempo/desperdício/clientes?`,
                      `N — Necessidade: o que mudaria se esse problema fosse resolvido?`,
                    ].map((q, i) => (
                      <div key={i} style={{ display: 'flex', gap: 6, marginBottom: 4 }}>
                        <div style={{ width: 5, height: 5, borderRadius: '50%', background: _LAYER_META[2].color, flexShrink: 0, marginTop: 5 }} />
                        <span style={{ fontSize: 10, color: 'var(--text)', lineHeight: 1.4 }}>{q}</span>
                      </div>
                    ))}
                  </div>
                  <div>
                    <div style={metaLabel(_LAYER_META[2].color)}>3 · Ten#1 Produto — ROI lógico → emocional</div>
                    <div style={{ fontSize: 10, color: 'var(--text)', lineHeight: 1.5 }}>Argumento lógico: "{uspStr.slice(0,80)}". Future Pacing: imagina produzir X transferências/dia sem paragens.</div>
                  </div>
                </div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                  <div>
                    <div style={metaLabel(_LAYER_META[2].color)}>4 · Ten#2 Digi + Ten#3 Digidelta</div>
                    <div style={{ fontSize: 10, color: 'var(--text)', lineHeight: 1.5 }}>Confiança no agente: conhecimento profundo, cuidado genuíno, honestidade. Confiança na marca: Digidelta com anos de actividade, rede de suporte, casos de sucesso.</div>
                  </div>
                  <div>
                    <div style={metaLabel(_LAYER_META[2].color)}>5 · Close + Looping (máx 3 loops)</div>
                    <div style={{ fontSize: 10, color: 'var(--text)', lineHeight: 1.5 }}>Objecção → Loop (reintroduzir dor + novo argumento). Money-aside close: "Deixando o investimento de lado, a solução faz sentido para o teu negócio?"</div>
                  </div>
                  <div style={{ background: `${_LAYER_META[2].color}0D`, borderRadius: 8, padding: '8px 10px', borderLeft: `3px solid ${_LAYER_META[2].color}` }}>
                    <div style={metaLabel(_LAYER_META[2].color)}>6 · Trigger → Stage 65</div>
                    <div style={{ fontSize: 10, color: 'var(--text)', fontWeight: 600, lineHeight: 1.5 }}>Ten#1 ≥ 8 · Ten#2 ≥ 7 · Ten#3 ≥ 7 → demo/visita agendada → Digi notifica {decisor} por email com perfil completo.</div>
                  </div>
                </div>
              </div>
              );
            })()}
          </div>
        </div>
      </div>

      <Connector />

      {/* ════════════════════════════════════════
          LAYER 4 — HANDOFF COMERCIAL
      ════════════════════════════════════════ */}
      <div>
        <div style={layerHeader(_LAYER_META[3].color)}>
          <span style={lNum()}>{_LAYER_META[3].num}</span>
          {_LAYER_META[3].title}
        </div>
        <div style={layerBody(_LAYER_META[3].bg)}>
          {/* Trigger banner */}
          <div style={{ background: '#FEF3C7', borderRadius: 8, padding: '8px 14px', marginBottom: 14, display: 'flex', alignItems: 'center', gap: 8 }}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#92400E" strokeWidth="2"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
            <span style={{ fontSize: 11, color: '#92400E', fontWeight: 600 }}>
              Trigger: Stage 65 → Digi AI notifica Carina por email com perfil completo do lead → Carina abre OP no Gestor
            </span>
          </div>

          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, marginBottom: 12 }}>
            {/* Perfil enviado a Carina — definido no SP */}
            <div style={card(_LAYER_META[3].color)}>
              <div style={metaLabel(_LAYER_META[3].color)}>Perfil do lead · email automático para Carina</div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginTop: 6 }}>
                {[
                  { lbl: 'Produto de interesse', val: brief.commercial_name || campanha?.titulo || '—' },
                  { lbl: 'Decisor identificado', val: brief.decision_maker || '—' },
                  { lbl: 'Dor dominante confirmada', val: pains.length > 0 ? (typeof pains[0] === 'string' ? pains[0] : pains[0].descricao || '—') : '—' },
                  { lbl: 'Oferta a apresentar', val: offerLabel !== 'Oferta comercial' ? offerLabel : (offer.details || offer.descricao || '—') },
                  { lbl: 'Score Three Tens', val: sdrAI?.score_alvo || 'Ten#1 ≥ 8 · Ten#2 ≥ 7 · Ten#3 ≥ 7' },
                  { lbl: 'Mercados', val: markets.map(m => m.country).join(' + ') || '—' },
                ].map((row, i) => (
                  <div key={i} style={{ display: 'flex', gap: 8, alignItems: 'flex-start', paddingBottom: 5, borderBottom: i < 5 ? '1px solid var(--border-light,#f1f5f9)' : 'none' }}>
                    <div style={{ fontSize: 9, fontWeight: 700, color: _LAYER_META[3].color, fontFamily: 'var(--font-mono)', textTransform: 'uppercase', letterSpacing: '.04em', flexShrink: 0, minWidth: 130 }}>{row.lbl}</div>
                    <div style={{ fontSize: 11, color: 'var(--text)', lineHeight: 1.4 }}>{row.val}</div>
                  </div>
                ))}
              </div>
            </div>

            {/* Top USPs · argumentos de venda */}
            <div style={card(_LAYER_META[3].color)}>
              <div style={metaLabel(_LAYER_META[3].color)}>Argumentos de venda · definidos no briefing</div>
              {usps.length === 0
                ? <div style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 6 }}>Sem USPs definidos no briefing</div>
                : usps.map((u, i) => (
                  <div key={i} style={{ display: 'flex', gap: 8, marginTop: 8, alignItems: 'flex-start' }}>
                    <div style={{ ...bigNum, fontSize: 16, color: _LAYER_META[3].color, flexShrink: 0, lineHeight: 1.1 }}>{i+1}</div>
                    <div style={{ fontSize: 11, color: 'var(--text)', lineHeight: 1.5 }}>{typeof u === 'string' ? u : u.usp || u.descricao || JSON.stringify(u)}</div>
                  </div>
                ))
              }
            </div>
          </div>

          {/* Próximos passos comerciais */}
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 10 }}>
            {[
              { badge: 'Demo Showroom', title: `${brief.commercial_name || 'Produto'} · ${markets.map(m=>m.country).join(' + ')}`,
                color: '#92400E', bg: '#FEF3C7',
                rows: ['Carina abre OP Stage 65 no Gestor', 'Equipa comercial agenda visita ao showroom Lisboa', 'Demo ao vivo + amostras para testar'] },
              { badge: 'Proposta ROI', title: brief.purchase_trigger || 'Intenção de compra declarada',
                color: '#991B1B', bg: '#FEE2E2',
                rows: [
                  `Volume actual + máquina actual + urgência + budget`,
                  `Proposta personalizada com ROI: ${usps.length > 0 ? (typeof usps[0] === 'string' ? usps[0].slice(0,50) + '...' : '—') : '—'}`,
                  'Carina valida condições e prazo com o decisor',
                ] },
              { badge: offerLabel !== 'Oferta comercial' ? (offer.type || offer.tipo || 'Oferta') : 'Oferta Financiamento',
                title: offerLabel !== 'Oferta comercial' ? offerLabel : (offer.details || 'Condições especiais campanha'),
                color: '#065F46', bg: '#CCFBF1',
                rows: [
                  offer.details || offer.descricao || 'Condições financeiras adaptadas ao perfil',
                  offer.negotiable ? 'Condições negociáveis mediante volume' : 'Garantia standard incluída',
                  'Equipa comercial valida e fecha condições',
                ] },
            ].map((c, i) => (
              <div key={i} style={{ background: 'var(--bg-card,#fff)', borderRadius: 10, border: '1px solid var(--border)', borderTop: `3px solid ${c.color}`, padding: '12px 12px 10px' }}>
                <span style={{ ...smallTag(c.color, c.bg), marginBottom: 8, display: 'inline-block' }}>{c.badge}</span>
                <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--navy,#112954)', marginBottom: 8, fontFamily: 'var(--font-display)', lineHeight: 1.3 }}>{c.title}</div>
                {c.rows.map((r, j) => (
                  <div key={j} style={{ fontSize: 10, color: '#475569', display: 'flex', gap: 5, lineHeight: 1.4, marginBottom: 3 }}>
                    <span style={{ color: c.color, flexShrink: 0, fontWeight: 700 }}>→</span>{r}
                  </div>
                ))}
              </div>
            ))}
          </div>
        </div>
      </div>

      <Connector />

      {/* ════════════════════════════════════════
          LAYER 5 — NURTURING WA
      ════════════════════════════════════════ */}
      <div style={{ borderRadius: '0 0 0 0', overflow: 'hidden' }}>
        <div style={layerHeader(_LAYER_META[4].color)}>
          <span style={lNum()}>{_LAYER_META[4].num}</span>
          {_LAYER_META[4].title}
          <span style={{ marginLeft: 'auto', fontSize: 10, fontWeight: 500, opacity: .7 }}>Target CRM + Stages</span>
        </div>
        <div style={layerBody(_LAYER_META[4].bg)}>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: 14, marginBottom: 14 }}>

            {/* WA sequences per objectivo */}
            <div style={card(_LAYER_META[4].color)}>
              <div style={metaLabel(_LAYER_META[4].color)}>Sequencia WA por objectivo</div>
              {[
                { obj: 'Reactivacao', seq: 'Nova solucao + ROI comparativo vs solucao anterior', color: '#4C1D95' },
                { obj: 'Aceleracao (AG Decisao)', seq: 'Urgencia stock + oferta PrintPlan + deadline', color: '#7C3AED' },
                { obj: 'Prospect inactivo', seq: 'Case study similar + convite open house + demo gratuita', color: '#A855F7' },
              ].map((item, i) => (
                <div key={i} style={{ marginBottom: 10 }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 3 }}>
                    <div style={dot(item.color)} />
                    <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--navy,#112954)' }}>{item.obj}</div>
                  </div>
                  <div style={{ fontSize: 10, color: 'var(--text-muted)', paddingLeft: 14, lineHeight: 1.5 }}>{item.seq}</div>
                </div>
              ))}
            </div>
          </div>

          {/* Stage progression */}
          <div style={card(_LAYER_META[4].color)}>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}>
              <div style={metaLabel(_LAYER_META[4].color)}>Progressao de stages · WA Nurturing via Digi AI</div>
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(6,1fr)', gap: 8 }}>
              {_NURTURE_STAGES.map((stage, i) => (
                <div key={i} style={{ background: 'var(--bg-sunken)', borderRadius: 8, padding: '8px 10px', borderLeft: `3px solid ${_LAYER_META[4].color}`, position: 'relative' }}>
                  {i < _NURTURE_STAGES.length - 1 && (
                    <div style={{ position: 'absolute', right: -14, top: '50%', transform: 'translateY(-50%)', zIndex: 1, color: _LAYER_META[4].color, fontSize: 10, fontWeight: 700 }}>›</div>
                  )}
                  <div style={{ fontSize: 9, fontWeight: 700, color: _LAYER_META[4].color, letterSpacing: '0.06em', textTransform: 'uppercase', fontFamily: 'var(--font-mono)' }}>{stage.num}</div>
                  <div style={{ fontSize: 10, fontWeight: 700, color: 'var(--navy,#112954)', margin: '3px 0', fontFamily: 'var(--font-display)', lineHeight: 1.2 }}>{stage.name}</div>
                  <div style={{ fontSize: 9, color: 'var(--text-muted)', lineHeight: 1.4 }}>{stage.action}</div>
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>

      {/* Footer */}
      <div style={{ background: 'var(--navy,#112954)', padding: '10px 24px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderRadius: '0 0 10px 10px' }}>
        <div style={{ fontSize: 11, fontWeight: 700, color: 'rgba(255,255,255,.5)', letterSpacing: '0.06em', fontFamily: 'var(--font-mono)' }}>
          {brief.brand_name || ''}{brief.brand_name && campanha?.titulo ? ' · ' : ''}{campanha?.titulo || ''}
        </div>
        <div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
          {kpis.length > 0 && (
            <span style={{ fontSize: 10, color: 'rgba(255,255,255,.4)', fontFamily: 'var(--font-mono)' }}>
              KPIs: {kpis.slice(0,2).map(k => typeof k === 'string' ? k : k.kpi || '').join(' · ')}
            </span>
          )}
          <div style={{ fontSize: 10, color: 'rgba(255,255,255,.35)', fontFamily: 'var(--font-mono)' }}>
            {new Date().toLocaleDateString('pt-PT', { month: 'long', year: 'numeric' })}
          </div>
        </div>
      </div>

    </div>
  );
};


// ── TabAnunciosFull — hierarquia Campaign→AdSet→Ad para canais pagos ───────────
const PAID_CHANNELS = new Set(['meta_ads','linkedin_ads','google_ads_search','google_ads_display','muppi_led']);
const PAID_LABELS   = { meta_ads:'Meta Ads', linkedin_ads:'LinkedIn Ads', google_ads_search:'Google Search', google_ads_display:'Google Display', muppi_led:'LED/Muppi' };

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: 12, fontWeight: 600, color: 'var(--text,#1d2e38)' }}>{item.titulo || `Ad ${i+1}`}</div>
                        <div style={{ fontSize: 10, color: 'var(--text-dim,#94a3b8)', marginTop: 1 }}>{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>
  );
};

// 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).

// Steps do generate-copy (SSE) — alinhados com endpoint marketing-dev-server.js:6642+
const COPY_STEPS = [
  { step: 1, label: 'Ler conceito',          duration: 3000 },
  { step: 2, label: 'Analisar plano',        duration: 3000 },
  { step: 3, label: 'Escrever orgânico',     duration: 15000 },
  { step: 4, label: 'Escrever performance',  duration: 10000 },
  { step: 5, label: 'Guardar copy',          duration: 3000 },
];

// 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',
  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);
  // 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   = 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 fmtEl = (s) => s < 60 ? `${s}s` : `${Math.floor(s/60)}m${String(s%60).padStart(2,'0')}s`;
    const bChannels = campanha?.briefing?.channels || [];
    const PERF_SET  = new Set(['meta_ads','linkedin_ads','google_ads_search','google_ads_display','muppi_led']);
    const hasMeta_  = bChannels.includes('meta_ads');
    const hasWA_    = bChannels.includes('whatsapp');
    const hasEmail_ = bChannels.includes('email');
    const nUsps_    = (campanha?.briefing?.usps || []).length;
    const nDores_   = (campanha?.briefing?.pain_points || []).length;
    const nObj_     = (campanha?.briefing?.objections || []).length;
    const ISO = { portugal:'PT', espanha:'ES', españa:'ES', france:'FR', germany:'DE', 'united kingdom':'UK', netherlands:'NL' };
    const mkts_ = (campanha?.briefing?.geo_markets || []).map(m => ISO[String(m).toLowerCase().trim()] || String(m).slice(0,2).toUpperCase()).filter(Boolean);
    const mktsStr = mkts_.length ? mkts_.join('+') : 'PT';
    const chStr_  = bChannels.filter(ch => !PERF_SET.has(ch)).map(ch => CANAL_LABELS[ch] || ch).join(' · ') || 'canais';

    const SECTIONS = [
      { label: 'Estratégia Criativa',     detail: 'Big Idea · Posicionamento · Narrativa · Tom',                                              doneAfter: 3 },
      { label: 'Ângulos por USP',         detail: `${nUsps_ || '—'} ângulos · 1 por USP · cada um integra as 3 fases do funil`,               doneAfter: 4 },
      { label: 'Abordagem por Canal',     detail: bChannels.length ? bChannels.map(ch => CANAL_LABELS[ch]||ch).join(' · ') : 'por canal do briefing', doneAfter: 5 },
      hasEmail_ ? { label: 'Email',       detail: `Sequência: Kick Off + Reminders + Last Call · objectivo por email`,                        doneAfter: 5 } : null,
      hasMeta_  ? { label: 'Meta Ads',    detail: `Objectivo · abordagem criativa · formatos · nº anúncios previstos`,                        doneAfter: 5 } : null,
      hasWA_    ? { label: 'WhatsApp',    detail: `Tipo (outbound/inbound) · Digi AI · tom conversacional`,                                    doneAfter: 5 } : null,
      { label: 'Cronograma Orgânico',     detail: `comm_plan por semana — passa depois para Planeamento`,                                     doneAfter: 6 },
      { label: 'Variantes por Mercado',   detail: `${mktsStr} · reinterpretação idiomática (não tradução) · tom cultural local`,             doneAfter: 8 },
    ].filter(Boolean);

    const ConceitoDot = ({ done, active }) => {
      if (done) return (
        <div style={{ width: 16, height: 16, borderRadius: '50%', background: 'rgba(21,128,61,.12)', border: '1.5px solid rgba(21,128,61,.3)', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <svg width="8" height="8" viewBox="0 0 10 10" fill="none"><polyline points="1.5,5 4,7.5 8.5,2.5" stroke="#15803d" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>
        </div>
      );
      if (active) return (
        <div style={{ width: 16, height: 16, borderRadius: '50%', border: '2px solid #5B43C5', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', animation: 'espin 1.4s linear infinite' }}>
          <div style={{ width: 6, height: 6, borderRadius: '50%', background: '#5B43C5' }} />
        </div>
      );
      return <div style={{ width: 16, height: 16, borderRadius: '50%', border: '1.5px solid #e2e8f0', flexShrink: 0 }} />;
    };

    return (
      <div style={{ background: 'var(--bg-elev,#fff)', border: '1px solid var(--border,#e2e8f0)', borderRadius: 10, padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 16 }}>
        <style>{`
          @keyframes cpulse { 0%,100%{opacity:1} 50%{opacity:.25} }
          @keyframes espin  { to { transform: rotate(360deg); } }
        `}</style>
        {/* Aviso */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 12px', background: '#fffbeb', border: '1px solid #fde68a', borderRadius: 6 }}>
          <span style={{ fontSize: 13 }}>⚠</span>
          <span style={{ fontSize: 12, color: '#92400e', fontWeight: 500 }}>Não feches esta janela nem saias deste ecrã — a geração está em curso.</span>
        </div>
        {/* Barra de progresso + timer */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <div style={{ flex: 1, height: 5, borderRadius: 99, background: 'var(--bg-app,#f5f6f8)', overflow: 'hidden' }}>
            <div style={{ height: '100%', borderRadius: 99, background: 'linear-gradient(90deg,#5B43C5,#7c6de0)', width: `${overallPct}%`, transition: 'width 0.15s linear' }} />
          </div>
          <span style={{ fontSize: 11, fontWeight: 700, color: '#5B43C5', fontFamily: 'monospace', flexShrink: 0, minWidth: 32 }}>{overallPct}%</span>
          <span style={{ fontSize: 11, fontWeight: 600, color: '#94a3b8', fontFamily: 'monospace', flexShrink: 0, minWidth: 36 }}>{fmtEl(conceitoElapsed)}</span>
        </div>
        {/* Tabela de secções */}
        <table style={{ width: '100%', borderCollapse: 'collapse', tableLayout: 'fixed' }}>
          <colgroup><col style={{ width: 28 }} /><col style={{ width: 220 }} /><col /></colgroup>
          <thead>
            <tr>
              <th style={{ padding: '4px 8px 4px 0', fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#94a3b8', fontFamily: 'monospace', textAlign: 'left' }} />
              <th style={{ padding: '4px 8px', fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#94a3b8', fontFamily: 'monospace', textAlign: 'left' }}>SECÇÃO</th>
              <th style={{ padding: '4px 8px', fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#94a3b8', fontFamily: 'monospace', textAlign: 'left' }}>DETALHE</th>
            </tr>
          </thead>
          <tbody>
            {SECTIONS.map((sec, idx) => {
              const done   = conceitoStep > sec.doneAfter;
              const active = !done && conceitoStep >= (idx === 0 ? 3 : SECTIONS[idx-1]?.doneAfter || 3);
              return (
                <tr key={idx} style={{ borderBottom: '1px solid #f1f5f9', background: active ? 'rgba(91,67,197,.02)' : 'transparent' }}>
                  <td style={{ padding: '8px 8px 8px 0', verticalAlign: 'middle' }}><ConceitoDot done={done} active={active} /></td>
                  <td style={{ padding: '8px', verticalAlign: 'middle' }}>
                    <span style={{ fontSize: 13, fontWeight: active ? 700 : done ? 500 : 400, color: done ? '#374151' : active ? 'var(--text,#1d2e38)' : '#94a3b8', fontFamily: 'var(--font-body,Inter,sans-serif)' }}>{sec.label}</span>
                  </td>
                  <td style={{ padding: '8px', verticalAlign: 'middle' }}>
                    <span style={{ fontSize: 12, color: active ? '#64748b' : done ? '#64748b' : '#cbd5e1', fontFamily: 'var(--font-body,Inter,sans-serif)' }}>{sec.detail}</span>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    );
  }

  if (!hasConceito) return (
    <PhaseEmptyState
      label="Conceito · fase 2"
      title="Ainda sem conceito criativo"
      description="Direcção criativa gerada a partir do briefing aprovado (USPs, dores, motivadores, tom, key_message, restrições) e da estratégia. Produz big idea unificada e ângulos criativos por USP — cada ângulo cobre as 3 fases do funil (awareness · consideration · decision) na mesma peça, seguindo a filosofia B2B Digidelta. Serve depois de base para a Produção de Conteúdos (várias peças por USP × canais aprovados)."
      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_manually_edited_at && ` · editado ${new Date(campanha.conceito_manually_edited_at).toLocaleDateString('pt-PT')}`}
        </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)' }}>
            Big idea, posicionamento e narrativa são cross-market. Tom, key message, ângulos, hooks, copy e plano adaptam-se ao mercado seleccionado.
          </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, hooks, estrutura de anúncios pagos e plano de comunicação orgânico.</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>
          </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={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--text-dim, #94a3b8)', fontFamily: 'var(--font-mono, monospace)', paddingBottom: 14, borderBottom: '1px solid var(--border, #e2e8f0)', marginBottom: 18 }}>Fundação Criativa</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>
      )}

      {/* ── 4. Abordagem por Canal — direcção criativa por canal do briefing ── */}
      {(() => {
        const abordagem = proposta.abordagem_por_canal || null;
        // 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="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>
                    )}

                    {/* 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>
  );
};

// ── TabCopyFull (grid de canais) ───────────────────────────────────────────────
// ── TabIdiomas ─────────────────────────────────────────────────────────────────
const LANG_LABELS_UI = { pt: 'Português', es: 'Español', en: 'English', fr: 'Français', de: 'Deutsch', it: 'Italiano' };

const TabIdiomas = ({ campanha, idiomas, onAction, generating, idiomasStep = 0, idiomasMsg = '', idiomasTotal = 0, idiomasLangs = [] }) => {
  const [activeLang, setActiveLang] = React.useState(null);
  const [extraLang,  setExtraLang]  = React.useState('');
  const langs = [...new Set(idiomas.map(i => i.lingua))].filter(Boolean);

  React.useEffect(() => {
    if (langs.length > 0 && !activeLang) setActiveLang(langs[0]);
  }, [langs.length]);

  const hasIdiomas = idiomas.length > 0;
  const isGenerating = generating.idiomas;

  const overallPct = idiomasTotal > 0 ? Math.min(Math.round((idiomasStep / idiomasTotal) * 100), 99) : 0;

  // step 3+ = tradução por língua (índice = step - 3)
  const getLangStatus = (idx) => {
    const langStep = 3 + idx;
    if (idiomasStep > langStep) return 'done';
    if (idiomasStep === langStep) return 'active';
    return 'pending';
  };

  if (isGenerating) {
    const allCopy     = campanha?.copy || [];
    const orgCopy     = allCopy.filter(c => !c.copy_type || c.copy_type === 'organico');
    const perfCopy    = allCopy.filter(c => c.copy_type === 'performance');

    const getItemText = (cp) => {
      const t = cp.caption_organica || cp.headline || cp.item_title || '';
      return t.length > 80 ? t.slice(0, 80) + '…' : t;
    };

    const IDot = ({ done, active }) => {
      if (done) return (
        <div style={{ width: 16, height: 16, borderRadius: '50%', background: 'rgba(21,128,61,.12)', border: '1.5px solid rgba(21,128,61,.3)', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <svg width="8" height="8" viewBox="0 0 10 10" fill="none"><polyline points="1.5,5 4,7.5 8.5,2.5" stroke="#15803d" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>
        </div>
      );
      if (active) return (
        <div style={{ width: 16, height: 16, borderRadius: '50%', border: '2px solid #8b5cf6', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <div style={{ width: 7, height: 7, borderRadius: '50%', background: '#8b5cf6', animation: 'cpulse 1s infinite' }} />
        </div>
      );
      return <div style={{ width: 16, height: 16, borderRadius: '50%', border: '1.5px solid #e2e8f0', flexShrink: 0 }} />;
    };

    const ItemsTable = ({ items, isOrg, langDone, langActive }) => {
      const TYPE_LABEL = { post:'Post', reel:'Reel', story:'Story', carrossel:'Carrossel', email:'Email', blog_post:'Blog', video:'Vídeo', rsa:'RSA', mensagem:'WA' };
      return (
        <div style={{ marginTop: 6 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 6 }}>
            <div style={{ width: 3, height: 12, borderRadius: 2, background: isOrg ? '#059669' : '#0ea5e9', flexShrink: 0 }} />
            <span style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: isOrg ? '#059669' : '#0ea5e9', fontFamily: 'var(--font-mono,monospace)' }}>
              {isOrg ? 'Orgânico' : 'Performance'} · {items.length}
            </span>
          </div>
          <table style={{ width: '100%', borderCollapse: 'collapse', tableLayout: 'fixed' }}>
            <colgroup>
              <col style={{ width: 24 }} />
              <col style={{ width: 160 }} />
              <col style={{ width: 70 }} />
              <col />
            </colgroup>
            <thead>
              <tr style={{ background: 'var(--bg-app,#f5f6f8)', borderBottom: '1px solid var(--border,#e2e8f0)' }}>
                <th style={{ padding: '4px 4px' }} />
                <th style={{ padding: '4px 6px', textAlign: 'left', fontSize: 8, fontWeight: 700, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase' }}>Canal</th>
                <th style={{ padding: '4px 6px', textAlign: 'left', fontSize: 8, fontWeight: 700, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase' }}>Tipo</th>
                <th style={{ padding: '4px 6px', textAlign: 'left', fontSize: 8, fontWeight: 700, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase' }}>Conteúdo</th>
              </tr>
            </thead>
            <tbody>
              {items.map((cp, i) => {
                const text = getItemText(cp);
                const tc   = langDone ? '#374151' : langActive ? 'var(--text,#1d2e38)' : '#94a3b8';
                return (
                  <tr key={cp.id || i} style={{ borderBottom: '1px solid var(--border-light,#f8fafc)' }}>
                    <td style={{ padding: '5px 4px', verticalAlign: 'middle' }}><IDot done={langDone} active={langActive} /></td>
                    <td style={{ padding: '5px 6px', verticalAlign: 'middle' }}>
                      <span style={{ fontSize: 10, fontWeight: 700, padding: '1px 5px', borderRadius: 4, background: langDone ? 'rgba(21,128,61,.08)' : 'var(--bg-app,#f5f6f8)', color: langDone ? '#15803d' : '#64748b', fontFamily: 'monospace', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', display: 'block', maxWidth: 148 }}>
                        {CANAL_LABEL[cp.canal] || cp.canal}
                      </span>
                    </td>
                    <td style={{ padding: '5px 6px', verticalAlign: 'middle' }}>
                      <span style={{ fontSize: 9, color: '#64748b', fontFamily: 'monospace', textTransform: 'uppercase' }}>
                        {TYPE_LABEL[cp.content_type] || cp.content_type || '—'}
                      </span>
                    </td>
                    <td style={{ padding: '5px 6px', verticalAlign: 'middle' }}>
                      <span style={{ fontSize: 11, color: tc, fontWeight: langActive ? 600 : 400, fontFamily: 'var(--font-body,Inter,sans-serif)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'block' }}>{text || '—'}</span>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      );
    };

    return (
      <div style={{ background: 'var(--bg-elev,#fff)', border: '1px solid var(--border,#e2e8f0)', borderRadius: 10, padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 16 }}>
        <style>{`@keyframes cpulse { 0%,100%{opacity:1} 50%{opacity:.25} }`}</style>

        {/* Barra de progresso compacta */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <div style={{ flex: 1, height: 4, borderRadius: 99, background: 'var(--bg-app,#f5f6f8)', overflow: 'hidden' }}>
            <div style={{ height: '100%', borderRadius: 99, background: '#8b5cf6', width: `${overallPct}%`, transition: 'width 0.15s linear' }} />
          </div>
          <span style={{ fontSize: 11, fontWeight: 700, color: '#8b5cf6', fontFamily: 'monospace', flexShrink: 0 }}>{overallPct}%</span>
          <span style={{ fontSize: 11, color: '#94a3b8', fontFamily: 'var(--font-body,Inter,sans-serif)', flexShrink: 0 }}>{idiomasMsg || 'A traduzir…'}</span>
        </div>

        {/* Por idioma */}
        {idiomasLangs.map((lang, idx) => {
          const status    = getLangStatus(idx);
          const langDone  = status === 'done';
          const langActive = status === 'active';
          const color     = langDone ? '#15803d' : langActive ? '#8b5cf6' : '#94a3b8';
          const bgColor   = langDone ? 'rgba(21,128,61,.04)' : langActive ? 'rgba(139,92,246,.03)' : 'transparent';

          return (
            <div key={lang} style={{ border: `1px solid ${langDone ? 'rgba(21,128,61,.2)' : langActive ? 'rgba(139,92,246,.2)' : 'var(--border,#e2e8f0)'}`, borderRadius: 10, overflow: 'hidden', background: bgColor }}>
              {/* Header do idioma */}
              <div style={{ padding: '10px 14px', display: 'flex', alignItems: 'center', gap: 10 }}>
                <IDot done={langDone} active={langActive} />
                <span style={{ fontSize: 13, fontWeight: langActive ? 700 : 500, color, fontFamily: 'var(--font-display,Montserrat,sans-serif)', flex: 1 }}>
                  {LANG_LABELS_UI[lang] || lang.toUpperCase()}
                </span>
                {langDone && <span style={{ fontSize: 10, color: '#15803d', fontFamily: 'monospace' }}>{allCopy.length} traduzidos</span>}
                {langActive && <span style={{ fontSize: 10, color: '#8b5cf6', fontFamily: 'monospace' }}>a traduzir…</span>}
                {!langDone && !langActive && <span style={{ fontSize: 10, color: '#94a3b8', fontFamily: 'monospace' }}>{allCopy.length} pendentes</span>}
              </div>

              {/* Tabelas de items — só para idioma activo ou done */}
              {(langActive || langDone) && (
                <div style={{ padding: '0 14px 12px', borderTop: `1px solid ${langDone ? 'rgba(21,128,61,.15)' : 'rgba(139,92,246,.15)'}` }}>
                  {orgCopy.length > 0 && <ItemsTable items={orgCopy} isOrg={true} langDone={langDone} langActive={langActive} />}
                  {perfCopy.length > 0 && <ItemsTable items={perfCopy} isOrg={false} langDone={langDone} langActive={langActive} />}
                </div>
              )}
            </div>
          );
        })}
      </div>
    );
  }

  if (!hasIdiomas) {
    const GEO_LANG = {
      'espanha':'es','spain':'es','es':'es','españa':'es',
      'export':'en','global':'en','europa':'en','europe':'en','internacional':'en',
      'reino unido':'en','uk':'en','en':'en','inglês':'en',
      'france':'fr','fr':'fr','frança':'fr',
      'alemanha':'de','germany':'de','de':'de',
      'itália':'it','italy':'it','it':'it','italia':'it',
    };
    const geoMarkets = campanha?.briefing?.geo_markets || [];
    const geoExpanded = geoMarkets.flatMap(m => typeof m === 'string' ? m.split(/[\/,\-\+&]/).map(s => s.trim()) : [m]);
    const detectedLangs = [...new Set(
      geoExpanded.map(m => GEO_LANG[(m||'').toLowerCase().trim()]).filter(Boolean)
    )].filter(l => l !== 'pt');

    return (
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '64px 0', gap: 20 }}>
        <div style={{ width: 56, height: 56, borderRadius: 14, background: 'rgba(139,92,246,.08)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#8b5cf6" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
            <circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>
          </svg>
        </div>
        <div style={{ textAlign: 'center', maxWidth: 440 }}>
          <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--text, #1d2e38)', fontFamily: 'var(--font-display, Montserrat, sans-serif)', marginBottom: 8 }}>Traduções por gerar</div>
          {detectedLangs.length > 0 ? (
            <div style={{ fontSize: 13, color: 'var(--text-muted, #64748b)', lineHeight: 1.65, marginBottom: 8 }}>
              Com base no briefing, será gerado para:
              <div style={{ display: 'flex', gap: 6, justifyContent: 'center', marginTop: 10, flexWrap: 'wrap' }}>
                {detectedLangs.map(l => (
                  <span key={l} style={{ fontSize: 12, fontWeight: 600, padding: '4px 12px', borderRadius: 99, background: 'rgba(139,92,246,.1)', color: '#7c3aed', border: '1px solid rgba(139,92,246,.2)', fontFamily: 'Inter, sans-serif' }}>
                    {LANG_LABELS_UI[l] || l.toUpperCase()}
                  </span>
                ))}
              </div>
            </div>
          ) : (
            <div style={{ fontSize: 13, color: '#f59e0b', lineHeight: 1.65 }}>
              Sem idiomas alvo detectados no briefing (Bloco 1 — Mercados). Podes adicionar um idioma manualmente.
            </div>
          )}
        </div>
        <button onClick={() => onAction('generateIdiomas')} className="btn btn-ai" disabled={detectedLangs.length === 0} data-tutorial-step="idiomas">
          {detectedLangs.length > 0 ? 'Gerar Idiomas →' : '+ Adicionar idioma manualmente'}
        </button>
      </div>
    );
  }

  // ── Sprint 2: Tab layout with per-language content ────────────────────────────
  const [showAddLang, setShowAddLang] = React.useState(false);

  const allApproved = l => idiomas.filter(i => i.lingua === l).every(i => i.status === 'aprovado');
  const allLangs = ['pt', ...langs.filter(l => l !== 'pt')];
  React.useEffect(() => {
    if (!activeLang) setActiveLang('pt');
  }, []);

  const currentLangItems = activeLang === 'pt'
    ? (campanha?.copy || [])
    : idiomas.filter(i => i.lingua === activeLang);

  const langOrganic     = currentLangItems.filter(i => !i.copy_type || i.copy_type === 'organico');
  const langPerformance = currentLangItems.filter(i => i.copy_type === 'performance');

  const ctaForLang = (slug, l) => typeof window.ctaToText === 'function' ? window.ctaToText(slug, l) : (l === 'es' ? 'Más información' : 'Learn more');
  const offerIdi = campanha?.commercial_offer ? (typeof campanha.commercial_offer === 'string' ? (() => { try { return JSON.parse(campanha.commercial_offer); } catch { return null; } })() : campanha.commercial_offer) : null;
  const ctaSlugIdi = offerIdi?.primary_cta;

  const [expandedIdiomasRows, setExpandedIdiomasRows] = React.useState({});
  const [editingIdioma, setEditingIdioma]             = React.useState(null);
  const toggleIdiomasRow = (id) => setExpandedIdiomasRows(p => ({ ...p, [id]: !p[id] }));

  const thStIdi = { padding: '8px 12px', textAlign: 'left', fontSize: 10, fontWeight: 700, color: '#64748b', fontFamily: 'var(--font-mono,monospace)', textTransform: 'uppercase', letterSpacing: '0.05em' };

  const IdiomaRow = ({ cp, lang }) => {
    const isExpanded = expandedIdiomasRows[cp.id];
    const bodyText   = cp.body || cp.caption_organica || '';
    const ctaVal     = cp.cta || ctaForLang(ctaSlugIdi, lang);
    return (
      <React.Fragment>
        <tr style={{ borderBottom: '1px solid var(--border,#e2e8f0)', cursor: 'pointer', background: isExpanded ? 'rgba(139,92,246,.02)' : 'transparent' }} onClick={() => toggleIdiomasRow(cp.id)}>
          <td style={{ padding: '10px 12px' }}>
            <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--text,#1d2e38)' }}>{CANAL_LABEL[cp.canal] || cp.canal}</span>
            {cp.content_type && <span style={{ fontSize: 9, color: '#94a3b8', marginLeft: 4, fontFamily: 'monospace', textTransform: 'uppercase' }}>{cp.content_type}</span>}
          </td>
          <td style={{ padding: '10px 12px', maxWidth: 200 }}>
            <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--text,#1d2e38)', lineHeight: 1.3 }}>{cp.headline || '—'}</div>
          </td>
          <td style={{ padding: '10px 12px', maxWidth: 260 }}>
            <div style={{ fontSize: 12, color: 'var(--text-muted,#64748b)', lineHeight: 1.5 }}>
              {bodyText.slice(0, 100)}{bodyText.length > 100 ? '…' : ''}
            </div>
          </td>
          <td style={{ padding: '10px 12px' }}>
            <span style={{ fontSize: 11, fontWeight: 600, color: '#3859D0' }}>{ctaVal}</span>
          </td>
          <td style={{ padding: '10px 12px', minWidth: 90 }}>
            <div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
              <button onClick={e => { e.stopPropagation(); onAction('regenerateIdioma', cp.id, lang); }} style={{ background: 'none', border: '1px solid var(--border,#e2e8f0)', borderRadius: 5, padding: '3px 6px', cursor: 'pointer', fontSize: 11, color: '#64748b' }} title="Re-traduzir">⟳</button>
              <button onClick={e => { e.stopPropagation(); setEditingIdioma(cp); }} style={{ background: 'none', border: '1px solid var(--border,#e2e8f0)', borderRadius: 5, padding: '3px 6px', cursor: 'pointer', fontSize: 11, color: '#64748b' }} title="Editar tradução">✏</button>
              <span style={{ fontSize: 11, color: '#94a3b8' }}>{isExpanded ? '▴' : '▾'}</span>
            </div>
          </td>
        </tr>
        {isExpanded && (
          <tr>
            <td colSpan={5} style={{ padding: '8px 12px 16px', background: 'rgba(139,92,246,.02)' }}>
              <div style={{ paddingTop: 10, borderTop: '1px solid var(--border-light,#f1f5f9)', display: 'flex', flexDirection: 'column', gap: 10 }}>
                {cp.copy_imagem && (
                  <div>
                    <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', marginBottom: 4 }}>Texto Imagem</div>
                    <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--text,#1d2e38)' }}>{cp.copy_imagem}</div>
                  </div>
                )}
                {bodyText && (
                  <div>
                    <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', marginBottom: 4 }}>Tradução Completa</div>
                    <div style={{ fontSize: 13, color: 'var(--text,#1d2e38)', lineHeight: 1.7, whiteSpace: 'pre-wrap', background: 'var(--bg-app,#f5f6f8)', borderRadius: 8, padding: '10px 14px' }}>{bodyText}</div>
                  </div>
                )}
              </div>
            </td>
          </tr>
        )}
      </React.Fragment>
    );
  };

  const renderIdiomaSection = (items, isOrganic, lang) => {
    if (!items.length) return null;
    const accentColor = isOrganic ? '#059669' : '#0ea5e9';
    const label       = isOrganic ? 'Orgânico · Plano de Comunicação' : 'Performance · Anúncios Pagos';
    const countLabel  = isOrganic ? `${items.length} peças` : `${items.length} ads`;
    return (
      <CollapsBlock key={isOrganic ? 'org' : 'perf'} title={label} count={countLabel} defaultOpen={true} accentColor={accentColor}>
        <table style={{ width: '100%', borderCollapse: 'collapse' }}>
          <thead>
            <tr style={{ background: 'var(--bg-app,#f5f6f8)' }}>
              <th style={thStIdi}>Canal</th>
              <th style={thStIdi}>Headline</th>
              <th style={thStIdi}>Copy</th>
              <th style={thStIdi}>CTA</th>
              <th style={thStIdi}>Acções</th>
            </tr>
          </thead>
          <tbody>
            {items.map(cp => <IdiomaRow key={cp.id} cp={cp} lang={lang} />)}
          </tbody>
        </table>
      </CollapsBlock>
    );
  };

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

      {/* Tabs por idioma */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 4, borderBottom: '2px solid var(--border,#e2e8f0)', marginBottom: 4, flexWrap: 'wrap', position: 'relative' }}>
        {allLangs.map(l => (
          <button key={l} onClick={() => setActiveLang(l)} style={{
            padding: '6px 16px', border: 'none',
            borderBottom: `2px solid ${activeLang === l ? '#8b5cf6' : 'transparent'}`,
            marginBottom: -2, background: 'none', cursor: 'pointer',
            fontSize: 12, fontWeight: activeLang === l ? 700 : 500,
            color: activeLang === l ? '#7c3aed' : 'var(--text-muted,#64748b)',
            fontFamily: 'var(--font-body,Inter,sans-serif)',
            display: 'flex', alignItems: 'center', gap: 4,
          }}>
            {LANG_LABELS_UI[l] || l.toUpperCase()}
            {l === 'pt' && <span style={{ fontSize: 9, color: '#94a3b8' }}>(original)</span>}
            {l !== 'pt' && allApproved(l) && <span style={{ fontSize: 10, color: '#15803d' }}>✓</span>}
          </button>
        ))}

        {/* Adicionar idioma */}
        <div style={{ marginLeft: 'auto', position: 'relative' }}>
          <button onClick={() => setShowAddLang(!showAddLang)} style={{ background: 'none', border: '1px solid var(--border,#e2e8f0)', borderRadius: 6, padding: '5px 10px', fontSize: 11, cursor: 'pointer', color: '#64748b', marginBottom: 4 }}>
            + Adicionar idioma
          </button>
          {showAddLang && (
            <div style={{ position: 'absolute', right: 0, top: '100%', marginTop: 4, background: '#fff', border: '1px solid var(--border,#e2e8f0)', borderRadius: 8, padding: 8, zIndex: 50, boxShadow: '0 4px 12px rgba(0,0,0,.1)', minWidth: 160 }}>
              {['en','fr','de','it','es'].filter(l => !allLangs.includes(l)).map(l => (
                <button key={l} onClick={() => { onAction('generateIdiomas', l); setShowAddLang(false); }} style={{ display: 'block', width: '100%', textAlign: 'left', padding: '6px 10px', background: 'none', border: 'none', borderRadius: 5, fontSize: 12, cursor: 'pointer', color: 'var(--text,#1d2e38)' }}>
                  {LANG_LABELS_UI[l] || l.toUpperCase()}
                </button>
              ))}
              {['en','fr','de','it','es'].filter(l => !allLangs.includes(l)).length === 0 && (
                <div style={{ fontSize: 11, color: '#94a3b8', padding: '6px 10px' }}>Todos os idiomas gerados</div>
              )}
            </div>
          )}
        </div>
      </div>

      {/* Conteúdo da tab activa */}
      {activeLang && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          {renderIdiomaSection(langPerformance, false, activeLang)}
          {renderIdiomaSection(langOrganic, true, activeLang)}
          {currentLangItems.length === 0 && (
            <div style={{ textAlign: 'center', padding: '32px 0', color: 'var(--text-muted,#64748b)', fontSize: 13 }}>
              {activeLang === 'pt' ? 'Sem copy gerado ainda.' : `Sem tradução para ${LANG_LABELS_UI[activeLang] || activeLang.toUpperCase()} ainda.`}
            </div>
          )}
        </div>
      )}

      {/* Footer aprovação global */}
      {['idiomas_gerado','idiomas_pendente'].includes(campanha?.status) && (
        <div style={{ borderTop: '1px solid var(--border,#e2e8f0)', paddingTop: 16, marginTop: 4 }}>
          <button className="btn btn-ai" onClick={() => onAction('aprovarIdiomas')}>
            Aprovar Idiomas e Avançar para Prompts
          </button>
        </div>
      )}

      {/* Modal de edição de tradução */}
      {editingIdioma && (
        <EditCopyModal
          copy={editingIdioma}
          onClose={() => setEditingIdioma(null)}
          onSave={(id, data) => { onAction('saveCopy', id, data); setEditingIdioma(null); }}
        />
      )}
    </div>
  );
};

const TabCopyFull = ({ campanha, copy, onAction, generating, copyStep = 0, copyMsg = '', editMode = false }) => {
  const commPlan    = campanha?.proposta_json?.comm_plan || [];
  const hasCopy     = copy.length > 0;
  const conceptDone = campanha?.big_idea;

  // Todos os hooks ANTES de qualquer early return
  const stepStartRef      = React.useRef(null);
  const [stepPct, setStepPct]             = React.useState(0);
  const [expandedCopyRows, setExpandedCopyRows] = React.useState({});
  const [editingCopy, setEditingCopy]           = React.useState(null);
  const [collapsedCopy, setCollapsedCopy]       = React.useState({});

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

  React.useEffect(() => {
    if (!generating.copy || copyStep <= 0) return;
    const def = COPY_STEPS.find(s => s.step === copyStep);
    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);
  }, [copyStep, generating.copy]);

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

  // Chip picker por mercado (variants_by_market)
  const primaryCountry = React.useMemo(() => {
    const variants = campanha?.proposta_json?.variants_by_market || [];
    const p = Array.isArray(variants) ? variants.find(v => v.is_primary) : null;
    return p?.country || null;
  }, [campanha]);

  const markets = React.useMemo(() => {
    const set = new Set((copy || []).map(c => c.country).filter(Boolean));
    return [...set].sort((a, b) => (a === primaryCountry ? -1 : b === primaryCountry ? 1 : a.localeCompare(b)));
  }, [copy, primaryCountry]);

  const hasMarketVariants = markets.length > 0;
  const [activeMarket, setActiveMarket] = React.useState(markets[0] || null);
  React.useEffect(() => {
    if (markets.length > 0 && !markets.includes(activeMarket)) setActiveMarket(markets[0]);
  }, [markets]);

  const filteredCopy = hasMarketVariants
    ? (copy || []).filter(c => c.country === activeMarket)
    : (copy || []);

  if (generating.copy) {
    const proposta    = campanha?.proposta_json || {};
    const personas    = proposta.personas || [];
    const canaisSetup = campanha?.canais_setup ? (typeof campanha.canais_setup === 'string' ? JSON.parse(campanha.canais_setup) : campanha.canais_setup) : null;
    const PAID_CH     = ['meta_ads','linkedin_ads','google_ads_search','google_ads_display','muppi_led'];
    const adSetsPreview = [];
    PAID_CH.forEach(ch => {
      (canaisSetup?.canais?.[ch]?.audiencias || []).forEach(a => adSetsPreview.push({ ch, nome: a.nome }));
    });
    const orgDone  = copyStep > 3 ? commPlan.length : copyStep === 3 ? Math.floor((stepPct / 100) * commPlan.length) : 0;
    const perfDone = copyStep > 4 ? adSetsPreview.length : copyStep === 4 ? Math.floor((stepPct / 100) * adSetsPreview.length) : 0;

    const Dot = ({ done, active }) => {
      if (done) return (
        <div style={{ width: 16, height: 16, borderRadius: '50%', background: 'rgba(21,128,61,.12)', border: '1.5px solid rgba(21,128,61,.3)', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <svg width="8" height="8" viewBox="0 0 10 10" fill="none"><polyline points="1.5,5 4,7.5 8.5,2.5" stroke="#15803d" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>
        </div>
      );
      if (active) return (
        <div style={{ width: 16, height: 16, borderRadius: '50%', border: '2px solid rgba(56,89,208,.25)', borderTopColor: '#3859D0', flexShrink: 0, animation: 'espin 0.7s linear infinite' }} />
      );
      return <div style={{ width: 16, height: 16, borderRadius: '50%', border: '1.5px solid #e2e8f0', flexShrink: 0 }} />;
    };

    return (
      <div style={{ background: 'var(--bg-elev,#fff)', border: '1px solid var(--border,#e2e8f0)', borderRadius: 10, padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 16 }}>
        <style>{`@keyframes espin { to { transform: rotate(360deg); } }`}</style>

        {/* Barra de progresso compacta no topo */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <div style={{ flex: 1, height: 4, borderRadius: 99, background: 'var(--bg-app,#f5f6f8)', overflow: 'hidden' }}>
            <div style={{ height: '100%', borderRadius: 99, background: '#3859D0', width: `${overallPct}%`, transition: 'width 0.15s linear' }} />
          </div>
          <span style={{ fontSize: 11, fontWeight: 700, color: '#3859D0', fontFamily: 'monospace', flexShrink: 0 }}>{overallPct}%</span>
          <span style={{ fontSize: 11, color: '#94a3b8', fontFamily: 'var(--font-body,Inter,sans-serif)', flexShrink: 0 }}>{copyMsg || 'A escrever copy…'}</span>
        </div>

        {/* Secção Orgânico */}
        {commPlan.length > 0 && (
          <div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 8 }}>
              <div style={{ width: 3, height: 14, borderRadius: 2, background: '#059669', flexShrink: 0 }} />
              <span style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#059669', fontFamily: 'var(--font-mono,monospace)' }}>
                Orgânico · {orgDone}/{commPlan.length} peças
              </span>
            </div>
            <table style={{ width: '100%', borderCollapse: 'collapse', tableLayout: 'fixed' }}>
              <colgroup>
                <col style={{ width: 28 }} />
                <col style={{ width: 200 }} />
                <col style={{ width: 80 }} />
                <col />
                <col style={{ width: 160 }} />
              </colgroup>
              <thead>
                <tr style={{ background: 'var(--bg-app,#f5f6f8)', borderBottom: '1px solid var(--border,#e2e8f0)' }}>
                  <th style={{ padding: '5px 6px', width: 28 }} />
                  <th style={{ padding: '5px 8px', textAlign: 'left', fontSize: 9, fontWeight: 700, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.06em' }}>Canal</th>
                  <th style={{ padding: '5px 8px', textAlign: 'left', fontSize: 9, fontWeight: 700, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.06em' }}>Tipo</th>
                  <th style={{ padding: '5px 8px', textAlign: 'left', fontSize: 9, fontWeight: 700, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.06em' }}>Título</th>
                  <th style={{ padding: '5px 8px', textAlign: 'right', fontSize: 9, fontWeight: 700, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.06em' }}>Persona</th>
                </tr>
              </thead>
              <tbody>
                {commPlan.map((item, idx) => {
                  const isDone   = idx < orgDone;
                  const isActive = idx === orgDone && copyStep === 3;
                  const isPending = !isDone && !isActive;
                  const persona  = item.angulo_persona ? (personas.find(p => p.nome === item.angulo_persona)?.nome || item.angulo_persona).split(' ').slice(0,3).join(' ') : '';
                  const textColor = isDone ? '#374151' : isActive ? 'var(--text,#1d2e38)' : '#94a3b8';
                  return (
                    <tr key={idx} style={{ borderBottom: '1px solid var(--border-light,#f1f5f9)', opacity: isPending && idx > orgDone + 1 ? 0.4 : 1, transition: 'opacity 0.3s', background: isActive ? 'rgba(56,89,208,.02)' : 'transparent' }}>
                      <td style={{ padding: '6px 6px', verticalAlign: 'middle' }}><Dot done={isDone} active={isActive} /></td>
                      <td style={{ padding: '6px 8px', verticalAlign: 'middle' }}>
                        <span style={{ fontSize: 10, fontWeight: 700, padding: '1px 6px', borderRadius: 4, background: isDone ? 'rgba(21,128,61,.08)' : 'var(--bg-app,#f5f6f8)', color: isDone ? '#15803d' : '#64748b', fontFamily: 'monospace', whiteSpace: 'nowrap', display: 'inline-block', maxWidth: 88, overflow: 'hidden', textOverflow: 'ellipsis' }}>
                          {CANAL_LABEL[item.canal] || item.canal}
                        </span>
                      </td>
                      <td style={{ padding: '6px 8px', verticalAlign: 'middle' }}>
                        {item.content_type && <span style={{ fontSize: 9, fontWeight: 700, color: '#64748b', fontFamily: 'monospace', textTransform: 'uppercase' }}>{item.content_type}</span>}
                      </td>
                      <td style={{ padding: '6px 8px', verticalAlign: 'middle' }}>
                        <span style={{ fontSize: 12, color: textColor, fontWeight: isActive ? 600 : 400, fontFamily: 'var(--font-body,Inter,sans-serif)', lineHeight: 1.3, display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{item.titulo || ''}</span>
                      </td>
                      <td style={{ padding: '6px 8px', verticalAlign: 'middle', textAlign: 'right' }}>
                        {persona && <span style={{ fontSize: 10, color: '#94a3b8', fontFamily: 'monospace' }}>{persona}</span>}
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        )}

        {/* Secção Performance */}
        {adSetsPreview.length > 0 && (
          <div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 8 }}>
              <div style={{ width: 3, height: 14, borderRadius: 2, background: '#0ea5e9', flexShrink: 0 }} />
              <span style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#0ea5e9', fontFamily: 'var(--font-mono,monospace)' }}>
                Performance · {perfDone}/{adSetsPreview.length} ad sets
              </span>
            </div>
            <table style={{ width: '100%', borderCollapse: 'collapse', tableLayout: 'fixed' }}>
              <colgroup>
                <col style={{ width: 28 }} />
                <col style={{ width: 200 }} />
                <col />
              </colgroup>
              <thead>
                <tr style={{ background: 'var(--bg-app,#f5f6f8)', borderBottom: '1px solid var(--border,#e2e8f0)' }}>
                  <th style={{ padding: '5px 6px', width: 28 }} />
                  <th style={{ padding: '5px 8px', textAlign: 'left', fontSize: 9, fontWeight: 700, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.06em' }}>Canal</th>
                  <th style={{ padding: '5px 8px', textAlign: 'left', fontSize: 9, fontWeight: 700, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.06em' }}>Ad Set</th>
                </tr>
              </thead>
              <tbody>
                {adSetsPreview.map((a, idx) => {
                  const isDone   = idx < perfDone;
                  const isActive = idx === perfDone && copyStep === 4;
                  const textColor = isDone ? '#374151' : isActive ? 'var(--text,#1d2e38)' : '#94a3b8';
                  return (
                    <tr key={idx} style={{ borderBottom: '1px solid var(--border-light,#f1f5f9)', opacity: copyStep < 4 && !isDone ? 0.4 : 1, transition: 'opacity 0.3s', background: isActive ? 'rgba(14,165,233,.02)' : 'transparent' }}>
                      <td style={{ padding: '6px 6px', verticalAlign: 'middle' }}><Dot done={isDone} active={isActive} /></td>
                      <td style={{ padding: '6px 8px', verticalAlign: 'middle' }}>
                        <span style={{ fontSize: 10, fontWeight: 700, padding: '1px 6px', borderRadius: 4, background: isDone ? 'rgba(21,128,61,.08)' : 'var(--bg-app,#f5f6f8)', color: isDone ? '#15803d' : '#64748b', fontFamily: 'monospace', whiteSpace: 'nowrap', display: 'inline-block' }}>
                          {CANAL_LABEL[a.ch] || a.ch}
                        </span>
                      </td>
                      <td style={{ padding: '6px 8px', verticalAlign: 'middle' }}>
                        <span style={{ fontSize: 12, color: textColor, fontWeight: isActive ? 600 : 400, fontFamily: 'var(--font-body,Inter,sans-serif)' }}>{a.nome}</span>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        )}
      </div>
    );
  }

  if (!conceptDone) return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '64px 0', gap: 20 }}>
      <div style={{ width: 56, height: 56, borderRadius: 14, background: 'rgba(56,89,208,.08)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="var(--ai-500,#3859D0)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
          <rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>
        </svg>
      </div>
      <div style={{ textAlign: 'center' }}>
        <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--text,#1d2e38)', fontFamily: 'var(--font-display,Montserrat,sans-serif)', marginBottom: 6 }}>Conceito por aprovar</div>
        <div style={{ fontSize: 13, color: 'var(--text-muted,#64748b)', maxWidth: 320, lineHeight: 1.6, fontFamily: 'var(--font-body,Inter,sans-serif)' }}>Aprova o conceito da campanha antes de gerar o copy.</div>
      </div>
    </div>
  );

  // Separar copy por tipo (backward compat: NULL ou 'organico' → orgânico)
  const organicCopy     = filteredCopy.filter(c => !c.copy_type || c.copy_type === 'organico');
  const performanceCopy = filteredCopy.filter(c => c.copy_type === 'performance');

  const approved    = filteredCopy.filter(c => c.status === 'aprovado').length;
  const total       = filteredCopy.length;
  const pct         = total > 0 ? Math.round((approved / total) * 100) : 0;
  const allApproved = hasCopy && approved === total;

  // Normaliza planned_date para YYYY-MM-DD
  const normDate = (raw) => {
    if (!raw) return null;
    const s = typeof raw === 'string' ? raw : String(raw);
    return s.includes('T') ? s.split('T')[0] : s;
  };

  const formatDate = (d) => {
    if (!d) return null;
    try {
      const dt = new Date(d.includes('T') ? d : d + 'T12:00:00');
      if (isNaN(dt.getTime())) return d;
      return dt.toLocaleDateString('pt-PT', { day: 'numeric', month: 'short' });
    } catch { return d; }
  };

  // Agrupar por canal separado por tipo
  const groupByCanal = (arr) => {
    const map = {};
    arr.forEach(cp => { const k = cp.canal || 'outro'; if (!map[k]) map[k] = []; map[k].push(cp); });
    return Object.entries(map);
  };
  const organicGroups     = groupByCanal(organicCopy);
  const performanceGroups = groupByCanal(performanceCopy);

  if (!hasCopy) {
    const PERF_CH = new Set(['meta_ads','linkedin_ads','google_ads_search','google_ads_display','muppi_led']);
    const canaisSetupData = campanha?.canais_setup?.canais || {};
    // Orgânico: canais únicos no comm_plan + total peças
    const orgCanais = new Set(commPlan.map(i => i.canal)).size;
    const orgPecas  = commPlan.length;
    // Performance: canais pagos no canais_setup + total ad sets
    const perfEntries = Object.entries(canaisSetupData).filter(([k]) => PERF_CH.has(k));
    const perfCanais  = perfEntries.length;
    const perfPecas   = perfEntries.reduce((sum, [, cs]) => sum + (Array.isArray(cs.audiencias) ? cs.audiencias.length : 0), 0);

    return (
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '64px 0', gap: 20 }}>
        <div style={{ width: 56, height: 56, borderRadius: 14, background: 'rgba(56,89,208,.08)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="var(--ai-500,#3859D0)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
            <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
          </svg>
        </div>
        <div style={{ textAlign: 'center' }}>
          <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--text,#1d2e38)', fontFamily: 'var(--font-display,Montserrat,sans-serif)', marginBottom: 10 }}>Copy por gerar</div>
          <div style={{ display: 'flex', gap: 12, justifyContent: 'center', flexWrap: 'wrap' }}>
            {orgPecas > 0 && (
              <div style={{ background: 'rgba(5,150,105,.06)', border: '1px solid rgba(5,150,105,.2)', borderRadius: 10, padding: '10px 18px', textAlign: 'left' }}>
                <div style={{ fontSize: 10, fontWeight: 700, color: '#059669', fontFamily: 'var(--font-mono,monospace)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 4 }}>Orgânico</div>
                <div style={{ fontSize: 13, color: 'var(--text,#1d2e38)', fontWeight: 600 }}>{orgCanais} {orgCanais === 1 ? 'canal' : 'canais'} · {orgPecas} {orgPecas === 1 ? 'peça' : 'peças'}</div>
              </div>
            )}
            {perfCanais > 0 && (
              <div style={{ background: 'rgba(14,165,233,.06)', border: '1px solid rgba(14,165,233,.2)', borderRadius: 10, padding: '10px 18px', textAlign: 'left' }}>
                <div style={{ fontSize: 10, fontWeight: 700, color: '#0ea5e9', fontFamily: 'var(--font-mono,monospace)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 4 }}>Performance</div>
                <div style={{ fontSize: 13, color: 'var(--text,#1d2e38)', fontWeight: 600 }}>{perfCanais} {perfCanais === 1 ? 'canal' : 'canais'} · {perfPecas > 0 ? `${perfPecas} peças` : 'anúncios pagos'}</div>
              </div>
            )}
            {orgPecas === 0 && perfCanais === 0 && (
              <div style={{ fontSize: 13, color: 'var(--text-muted,#64748b)', lineHeight: 1.6 }}>Conceito aprovado — pronto para gerar o copy da campanha.</div>
            )}
          </div>
        </div>
        <button onClick={() => onAction('generateCopy')} disabled={generating.copy} className="btn btn-ai" style={{ fontSize: 13, padding: '10px 24px' }} data-tutorial-step="copy">
          Gerar Copy com Digi AI
        </button>
      </div>
    );
  }

  const toggleCopyRow = (id) => setExpandedCopyRows(p => ({ ...p, [id]: !p[id] }));

  const offer = campanha?.commercial_offer
    ? (typeof campanha.commercial_offer === 'string' ? (() => { try { return JSON.parse(campanha.commercial_offer); } catch { return null; } })() : campanha.commercial_offer)
    : null;
  const ctaSlug = offer?.primary_cta;
  const ctaDefault = typeof window.ctaToText === 'function' ? window.ctaToText(ctaSlug, 'pt') : 'Saiba mais';

  // Banner campanhas antigas
  const isOldCampaign = copy.some(c =>
    (c.body || '').toLowerCase().includes('cold') ||
    (c.body || '').toLowerCase().includes('warm') ||
    (c.body || '').toLowerCase().includes('retargeting')
  );
  const oldCampaignBanner = isOldCampaign ? (
    <div style={{ background: 'rgba(245,158,11,.06)', border: '1px solid rgba(245,158,11,.3)', borderRadius: 10, padding: '12px 16px', marginBottom: 16, display: 'flex', gap: 10, alignItems: 'flex-start' }}>
      <span style={{ fontSize: 14 }}>⚠</span>
      <div>
        <div style={{ fontSize: 12, fontWeight: 600, color: '#92400e', marginBottom: 2 }}>Campanha gerada antes da actualização do fluxo (Junho 2026)</div>
        <div style={{ fontSize: 12, color: '#78350f' }}>O copy pode referenciar Cold/Warm/Retargeting em vez da narrativa integrada Awareness→Consideration→Decision. Para regenerar com o novo modelo, volta ao Conceito e clica "Regenerar".</div>
      </div>
    </div>
  ) : null;

  const thStCopy = { padding: '8px 12px', textAlign: 'left', fontSize: 10, fontWeight: 700, color: '#64748b', fontFamily: 'var(--font-mono,monospace)', textTransform: 'uppercase', letterSpacing: '0.05em' };


  const CopyRow = ({ cp, isOrganic }) => {
    const isExpanded = expandedCopyRows[cp.id];
    const adSpecs = cp.ad_specs ? (typeof cp.ad_specs === 'string' ? (() => { try { return JSON.parse(cp.ad_specs); } catch { return null; } })() : cp.ad_specs) : null;
    const bodyText = cp.body || cp.caption_organica || '';
    return (
      <React.Fragment>
        <tr style={{ borderBottom: '1px solid var(--border,#e2e8f0)', cursor: 'pointer', background: expandedCopyRows[cp.id] ? 'rgba(56,89,208,.02)' : 'transparent' }} onClick={() => toggleCopyRow(cp.id)}>
          <td style={{ padding: '10px 12px', minWidth: 140 }}>
            <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--text,#1d2e38)' }}>
              {CANAL_LABEL[cp.canal] || cp.canal}
            </span>
            {(() => {
              const badge = cp.canal === 'whatsapp' ? 'template'
                : ['meta_ads','linkedin_ads','google_ads_search','google_ads_display'].includes(cp.canal) ? 'ad'
                : cp.content_type || null;
              return badge ? <span style={{ fontSize: 9, color: '#94a3b8', marginLeft: 4, fontFamily: 'monospace', textTransform: 'uppercase' }}>{badge}</span> : null;
            })()}
          </td>
          <td style={{ padding: '10px 12px', maxWidth: 220 }}>
            <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--text,#1d2e38)', lineHeight: 1.3 }}>
              {cp.canal === 'whatsapp'
                ? (adSpecs?.wa_template_name || cp.headline || '—')
                : (cp.headline || '—')}
            </div>
          </td>
          <td style={{ padding: '10px 12px', maxWidth: 300 }}>
            <div style={{ fontSize: 12, color: 'var(--text-muted,#64748b)', lineHeight: 1.5 }}>
              {(() => {
                const text = cp.canal === 'whatsapp'
                  ? (adSpecs?.wa_body || bodyText)
                  : bodyText;
                return text.slice(0, 120) + (text.length > 120 ? '…' : '');
              })()}
            </div>
          </td>
          <td style={{ padding: '10px 12px', minWidth: 120 }}>
            <span style={{ fontSize: 11, fontWeight: 600, color: '#3859D0' }}>{cp.cta || ctaDefault}</span>
          </td>
          <td style={{ padding: '10px 12px', minWidth: 80 }}>
            <div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
              <button onClick={e => { e.stopPropagation(); onAction('regenerateCopy', cp.id); }} style={{ background: 'none', border: '1px solid var(--border,#e2e8f0)', borderRadius: 5, padding: '3px 6px', cursor: 'pointer', fontSize: 11, color: '#64748b' }} title="Regenerar">⟳</button>
              <button onClick={e => { e.stopPropagation(); setEditingCopy(cp); }} style={{ background: 'none', border: '1px solid var(--border,#e2e8f0)', borderRadius: 5, padding: '3px 6px', cursor: 'pointer', fontSize: 11, color: '#64748b' }} title="Editar">✏</button>
              <span style={{ fontSize: 11, color: '#94a3b8' }}>{isExpanded ? '▴' : '▾'}</span>
            </div>
          </td>
        </tr>
        {isExpanded && (
          <tr>
            <td colSpan={5} style={{ padding: '0 12px 16px', background: 'rgba(56,89,208,.02)' }}>
              <div style={{ borderTop: '1px solid var(--border-light,#f1f5f9)', paddingTop: 12, marginTop: 0 }}>
                <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#3859D0', fontFamily: 'monospace', marginBottom: 10 }}>Sugestão Criativa</div>
                <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 12 }}>
                  <div>
                    <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', marginBottom: 3 }}>Headline Imagem</div>
                    <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text,#1d2e38)' }}>{cp.copy_imagem || cp.headline || '—'}</div>
                  </div>
                  <div>
                    <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', marginBottom: 3 }}>CTA Imagem</div>
                    <div style={{ fontSize: 12, color: 'var(--text,#1d2e38)' }}>{cp.cta || ctaDefault}</div>
                  </div>
                  <div>
                    <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', marginBottom: 3 }}>Descrição Visual</div>
                    <div style={{ fontSize: 12, color: 'var(--text-muted,#64748b)', lineHeight: 1.55 }}>{cp.prompt_visual || '—'}</div>
                  </div>
                </div>
                <button onClick={() => setEditingCopy({ ...cp, _mode: 'criativo' })} style={{ marginTop: 10, background: 'none', border: '1px solid rgba(56,89,208,.3)', borderRadius: 6, padding: '4px 10px', fontSize: 11, color: '#3859D0', cursor: 'pointer' }}>✏ Editar Sugestão Criativa</button>
              </div>
              {bodyText && (
                <div style={{ marginTop: 12, paddingTop: 12, borderTop: '1px solid var(--border-light,#f1f5f9)' }}>
                  <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', marginBottom: 6 }}>Copy Completo</div>
                  <div style={{ fontSize: 13, color: 'var(--text,#1d2e38)', lineHeight: 1.7, whiteSpace: 'pre-wrap', background: 'var(--bg-app,#f5f6f8)', borderRadius: 8, padding: '10px 14px' }}>
                    {bodyText}
                  </div>
                </div>
              )}
            </td>
          </tr>
        )}
      </React.Fragment>
    );
  };

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

      {oldCampaignBanner}

      {/* Chip picker por mercado */}
      {hasMarketVariants && (
        <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
          <span style={{ fontSize: 11, fontWeight: 600, color: '#64748b', letterSpacing: '.05em', textTransform: 'uppercase' }}>Mercado</span>
          {markets.map(m => {
            const active = m === activeMarket;
            const isPrimary = m === primaryCountry;
            return (
              <button key={m} onClick={() => setActiveMarket(m)}
                style={{
                  padding: '6px 12px', fontSize: 12, fontWeight: 700, borderRadius: 6,
                  border: active ? '1px solid var(--dd-primary-600,#3859D0)' : '1px solid #e2e8f0',
                  background: active ? 'rgba(56,89,208,.08)' : '#fff',
                  color: active ? 'var(--dd-primary-600,#3859D0)' : '#475569',
                  cursor: 'pointer', fontFamily: 'var(--font-mono,monospace)', letterSpacing: '.05em',
                }}>
                {m}{isPrimary ? ' · PRIMÁRIO' : ''}
              </button>
            );
          })}
          {activeMarket !== primaryCountry && (
            <span style={{ fontSize: 10, fontWeight: 700, padding: '3px 8px', borderRadius: 3,
              background: 'rgba(56,89,208,.12)', color: 'var(--dd-primary-600,#3859D0)',
              fontFamily: 'var(--font-mono,monospace)', letterSpacing: '.08em' }}>
              {activeMarket} · LOCAL
            </span>
          )}
        </div>
      )}

      {/* Secção Performance · Anúncios Pagos */}
      {performanceCopy.length > 0 && (
        <CollapsBlock title="Performance · Anúncios Pagos" count={performanceCopy.length + (performanceCopy.length === 1 ? ' canal' : ' canais')} defaultOpen={true} accentColor="#0ea5e9">
          <table style={{ width: '100%', borderCollapse: 'collapse' }}>
            <thead>
              <tr style={{ background: 'var(--bg-app,#f5f6f8)' }}>
                <th style={thStCopy}>Canal</th>
                <th style={thStCopy}>Headline</th>
                <th style={thStCopy}>Copy</th>
                <th style={thStCopy}>CTA</th>
                <th style={thStCopy}>Acções</th>
              </tr>
            </thead>
            <tbody>
              {performanceCopy.map(cp => <CopyRow key={cp.id} cp={cp} isOrganic={false} />)}
            </tbody>
          </table>
        </CollapsBlock>
      )}

      {/* Secção Orgânico · Plano de Comunicação */}
      {organicCopy.length > 0 && (
        <CollapsBlock title="Orgânico · Plano de Comunicação" count={organicCopy.length + ' peças'} defaultOpen={true} accentColor="#059669">
          <table style={{ width: '100%', borderCollapse: 'collapse' }}>
            <thead>
              <tr style={{ background: 'var(--bg-app,#f5f6f8)' }}>
                <th style={thStCopy}>Canal</th>
                <th style={thStCopy}>Headline</th>
                <th style={thStCopy}>Copy</th>
                <th style={thStCopy}>CTA</th>
                <th style={thStCopy}>Acções</th>
              </tr>
            </thead>
            <tbody>
              {organicCopy.map(cp => <CopyRow key={cp.id} cp={cp} isOrganic={true} />)}
            </tbody>
          </table>
        </CollapsBlock>
      )}

      {/* Footer aprovação global */}
      {hasCopy && ['copy_gerado','copy_pendente'].includes(campanha?.status) && (
        <div style={{ borderTop: '1px solid var(--border,#e2e8f0)', paddingTop: 16, marginTop: 4 }}>
          <button className="btn btn-ai" onClick={() => onAction('aprovarCopy')}>
            Aprovar Copy e Avançar para Prompts
          </button>
        </div>
      )}

      {/* Modal de edição de copy */}
      {editingCopy && (
        <EditCopyModal
          copy={editingCopy}
          onClose={() => setEditingCopy(null)}
          onSave={(id, data) => { onAction('saveCopy', id, data); setEditingCopy(null); }}
        />
      )}
    </div>
  );
};

const CANAL_COLOR_MAP = {
  meta_ads:'#1877F2', instagram:'#E1306C', facebook:'#1877F2',
  linkedin_ads:'#0A66C2', linkedin:'#0A66C2',
  email:'#6366f1', whatsapp:'#25D366',
  google_ads_search:'#4285F4', google_ads:'#4285F4', google_ads_display:'#34A853',
  website:'#0ea5e9', site:'#0ea5e9',
  muppi_led:'#8b5cf6', led:'#8b5cf6',
  youtube:'#FF0000', tiktok:'#1d1d1f',
  email_interno:'#6366f1', whatsapp_interno:'#25D366', portal_notificacao:'#0ea5e9',
  'email_interno+whatsapp_interno+portal_notificacao': '#6366f1',
};

const CanalSvgIcon = ({ canal, size = 15 }) => {
  const col = CANAL_COLOR_MAP[canal] || '#64748b';
  const sp = { width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: col, strokeWidth: '1.8', strokeLinecap: 'round', strokeLinejoin: 'round' };
  if (['meta_ads','instagram','facebook'].includes(canal)) return <svg {...sp}><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>;
  if (['linkedin_ads','linkedin'].includes(canal)) return <svg {...sp}><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"/><rect x="2" y="9" width="4" height="12"/><circle cx="4" cy="4" r="2"/></svg>;
  if (['email','email_interno'].includes(canal)) return <svg {...sp}><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>;
  if (['whatsapp','whatsapp_interno'].includes(canal)) return <svg {...sp}><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>;
  if (canal === 'portal_notificacao') return <svg {...sp}><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>;
  if (canal === 'email_interno+whatsapp_interno+portal_notificacao') return <svg {...sp}><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>;
  if (['google_ads_search','google_ads','google_ads_display'].includes(canal)) return <svg {...sp}><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>;
  if (['website','site'].includes(canal)) return <svg {...sp}><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>;
  if (['muppi_led','led'].includes(canal)) return <svg {...sp}><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>;
  if (canal === 'youtube') return <svg {...sp}><path d="M22.54 6.42a2.78 2.78 0 0 0-1.95-1.96C18.88 4 12 4 12 4s-6.88 0-8.59.46a2.78 2.78 0 0 0-1.95 1.96A29 29 0 0 0 1 12a29 29 0 0 0 .46 5.58A2.78 2.78 0 0 0 3.41 19.54C5.12 20 12 20 12 20s6.88 0 8.59-.46a2.78 2.78 0 0 0 1.95-1.96A29 29 0 0 0 23 12a29 29 0 0 0-.46-5.58z"/><polygon points="9.75 15.02 15.5 12 9.75 8.98 9.75 15.02"/></svg>;
  // default: file/doc
  return <svg {...sp}><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>;
};

const CopyCardFull = ({ cp, onApprove, onFlag, onSave, editMode }) => {
  // Canais compostos (e.g. "email_interno+whatsapp_interno") — usar o primeiro segmento
  const primaryCanal = cp.canal ? cp.canal.split('+')[0].trim() : '';
  const canalLabel   = CANAL_LABEL[cp.canal] || CANAL_LABEL[primaryCanal] || (primaryCanal ? primaryCanal.replace(/_/g,' ') : '—');
  const canalCol     = CANAL_COLOR_MAP[cp.canal] || CANAL_COLOR_MAP[primaryCanal] || '#64748b';
  const isApproved = cp.status === 'aprovado';
  const isFlagged  = cp.status === 'correcao';
  const adSpecs    = cp.ad_specs ? (typeof cp.ad_specs === 'string' ? JSON.parse(cp.ad_specs) : cp.ad_specs) : null;

  const [editHeadline, setEditHeadline] = React.useState(cp.headline || '');
  const [editBody,     setEditBody]     = React.useState(cp.body || '');
  const [editCta,      setEditCta]      = React.useState(cp.cta || '');
  const [editHashtags, setEditHashtags] = React.useState(cp.hashtags || '');
  const [saving,       setSaving]       = React.useState(false);

  React.useEffect(() => {
    setEditHeadline(cp.headline || '');
    setEditBody(cp.body || '');
    setEditCta(cp.cta || '');
    setEditHashtags(cp.hashtags || '');
  }, [cp.id]);

  const handleSave = async () => {
    setSaving(true);
    await onSave({ headline: editHeadline, body: editBody, cta: editCta, hashtags: editHashtags });
    setSaving(false);
  };

  const editFieldStyle = { width: '100%', fontSize: 12.5, color: '#1d2e38', background: '#f8fafc', border: '1px solid #e2e8f0', borderRadius: 6, padding: '6px 10px', fontFamily: 'Inter, sans-serif', lineHeight: 1.5, resize: 'vertical', outline: 'none', boxSizing: 'border-box' };
  return (
    <div style={{
      background: '#fff',
      border: `1px solid ${isApproved ? 'rgba(34,197,94,.4)' : isFlagged ? 'rgba(251,146,60,.4)' : '#e2e8f0'}`,
      borderLeft: `3px solid ${isApproved ? '#22c55e' : isFlagged ? '#fb923c' : canalCol}`,
      borderRadius: '0 10px 10px 0',
      display: 'flex', flexDirection: 'column',
      boxShadow: '0 1px 3px rgba(0,0,0,.04)',
    }}>
      {/* Card header */}
      <div style={{
        padding: '12px 16px 10px', borderBottom: '1px solid #f1f5f9',
        background: isApproved ? 'rgba(34,197,94,.04)' : isFlagged ? 'rgba(251,146,60,.04)' : '#fafbfc',
        borderRadius: '0 10px 0 0', display: 'flex', flexDirection: 'column', gap: 6,
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <div style={{ width: 26, height: 26, borderRadius: 6, background: `${canalCol}14`, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
            <CanalSvgIcon canal={primaryCanal || cp.canal} size={14} />
          </div>
          <span style={{ fontSize: 13, fontWeight: 700, color: '#1d2e38', flex: 1, fontFamily: 'Montserrat, sans-serif' }}>{canalLabel}</span>
          {cp.content_type && (
            <span style={{ fontSize: 10, color: '#64748b', background: '#f1f5f9', padding: '2px 7px', borderRadius: 5, fontFamily: 'monospace' }}>{cp.content_type}</span>
          )}
          {cp.formato && !cp.content_type && (
            <span style={{ fontSize: 10, color: '#94a3b8', fontFamily: 'monospace' }}>{cp.formato}</span>
          )}
          {/* Status inline */}
          <span style={{
            fontSize: 10, fontWeight: 600, padding: '2px 8px', borderRadius: 99, fontFamily: 'monospace',
            color: isApproved ? '#16a34a' : isFlagged ? '#ea580c' : '#64748b',
            background: isApproved ? 'rgba(34,197,94,.1)' : isFlagged ? 'rgba(251,146,60,.12)' : '#f1f5f9',
          }}>
            {isApproved ? '✓ Aprovado' : isFlagged ? '⚑ Correcção' : 'Pendente'}
          </span>
        </div>
        {/* Context row from comm_plan */}
        {(cp.planned_date || cp.item_title) && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            {cp.planned_date && (
              <span style={{ fontSize: 10, color: '#94a3b8', fontFamily: 'monospace', background: '#f1f5f9', padding: '1px 6px', borderRadius: 4 }}>{cp.planned_date}</span>
            )}
            {cp.item_title && (
              <span style={{ fontSize: 11, color: '#64748b', flex: 1, fontStyle: 'italic' }}>{cp.item_title}</span>
            )}
          </div>
        )}
      </div>

      {/* Card body */}
      <div style={{ padding: '14px 16px', display: 'flex', flexDirection: 'column', gap: 12, flex: 1 }}>
        {editMode ? (
          <>
            <div>
              <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#94a3b8', fontFamily: 'monospace', marginBottom: 4 }}>Headline</div>
              <input value={editHeadline} onChange={e => setEditHeadline(e.target.value)} style={editFieldStyle} />
            </div>
            {cp.body !== undefined && (
              <div>
                <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#94a3b8', fontFamily: 'monospace', marginBottom: 4 }}>Body</div>
                <textarea value={editBody} onChange={e => setEditBody(e.target.value)} rows={4} style={editFieldStyle} />
              </div>
            )}
            <div>
              <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#94a3b8', fontFamily: 'monospace', marginBottom: 4 }}>CTA</div>
              <input value={editCta} onChange={e => setEditCta(e.target.value)} style={editFieldStyle} />
            </div>
            {cp.hashtags !== undefined && (
              <div>
                <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#94a3b8', fontFamily: 'monospace', marginBottom: 4 }}>Hashtags</div>
                <input value={editHashtags} onChange={e => setEditHashtags(e.target.value)} style={editFieldStyle} />
              </div>
            )}
            <div style={{ display: 'flex', gap: 8, paddingTop: 8, borderTop: '1px solid #f1f5f9' }}>
              <button onClick={handleSave} disabled={saving} className="btn btn-ai" style={{ fontSize: 11 }}>
                {saving ? 'A guardar…' : '✓ Guardar'}
              </button>
            </div>
          </>
        ) : (
          <>
            {cp.canal === 'whatsapp' && adSpecs?.wa_template_name ? (
              /* ── WhatsApp: campos específicos Meta Template ── */
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
                  <span style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: '#25D366', fontFamily: 'monospace', background: 'rgba(37,211,102,.08)', padding: '3px 8px', borderRadius: 4 }}>
                    WhatsApp · Template Meta
                  </span>
                  <span style={{ fontSize: 9, fontFamily: 'monospace', color: '#64748b', background: '#f1f5f9', padding: '3px 8px', borderRadius: 4 }}>
                    {adSpecs.wa_template_name}
                  </span>
                  <span style={{ fontSize: 9, fontFamily: 'monospace', color: '#64748b', background: '#f1f5f9', padding: '3px 8px', borderRadius: 4 }}>
                    {adSpecs.wa_categoria}
                  </span>
                </div>
                <CopyFieldLight label="Body (Template)" value={adSpecs.wa_body || cp.body} multiline />
                {adSpecs.wa_footer && <CopyFieldLight label="Footer" value={adSpecs.wa_footer} />}
                {Array.isArray(adSpecs.wa_buttons) && adSpecs.wa_buttons.length > 0 && (
                  <CopyFieldLight label="Botões" value={adSpecs.wa_buttons.map(b => `${b.type}: ${b.text}${b.url ? ` → ${b.url}` : ''}`).join(' | ')} mono />
                )}
                {adSpecs.wa_exemplo_vars && Object.keys(adSpecs.wa_exemplo_vars).length > 0 && (
                  <CopyFieldLight label="Variáveis" value={Object.entries(adSpecs.wa_exemplo_vars).map(([k,v]) => `{{${k}}}=${v}`).join(', ')} mono />
                )}
              </div>
            ) : (
              <>
                {/* ── Copy da Imagem ── */}
                {cp.copy_imagem && (
                  <div style={{ background: 'rgba(56,89,208,.05)', border: '1px solid rgba(56,89,208,.15)', borderRadius: 8, padding: '10px 12px', marginBottom: 4 }}>
                    <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: '#3859D0', fontFamily: 'monospace', marginBottom: 6 }}>Copy Imagem · texto sobre o visual</div>
                    <div style={{ fontSize: 14, fontWeight: 700, color: '#1d2e38', fontFamily: 'Montserrat, sans-serif', lineHeight: 1.3 }}>{cp.copy_imagem}</div>
                  </div>
                )}
                {/* ── Copy Publicação ── */}
                {(cp.headline || cp.body || cp.cta) && (
                  <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: '#94a3b8', fontFamily: 'monospace', marginTop: cp.copy_imagem ? 4 : 0 }}>Copy Publicação · post / email / anúncio</div>
                )}
                {cp.headline && <CopyFieldLight label="Headline" value={cp.headline} />}
                {cp.body     && <CopyFieldLight label="Body"     value={cp.body}     multiline />}
                {cp.cta      && <CopyFieldLight label="CTA"      value={cp.cta} />}
                {cp.hashtags && <CopyFieldLight label="Hashtags" value={cp.hashtags} mono />}
                {adSpecs     && <AdSpecsBlock   specs={adSpecs}  canal={cp.canal}    light />}
              </>
            )}
            {!isApproved && (
              <div style={{ display: 'flex', gap: 8, marginTop: 4, paddingTop: 10, borderTop: '1px solid #f1f5f9' }}>
                <button onClick={onApprove} className="btn btn-ai" style={{ fontSize: 11 }}>✓ Aprovar</button>
                <button onClick={onFlag}    className="btn" style={{ fontSize: 11, color: '#fb923c', borderColor: 'rgba(251,146,60,.3)' }}>⚑ Corrigir</button>
              </div>
            )}
            {isApproved && (
              <div style={{ fontSize: 11, color: '#16a34a', fontWeight: 600, paddingTop: 8, borderTop: '1px solid #f1f5f9' }}>✓ Copy aprovado</div>
            )}
          </>
        )}
      </div>
    </div>
  );
};

const TIPO_CONFIG = {
  background:    { label: 'Background',   plat: 'FLUX.2 [pro]',          color: '#3859D0', bg: 'rgba(56,89,208,.07)'  },
  video_produto: { label: 'Vídeo Produto', plat: 'Kling 3.0',             color: '#8b5cf6', bg: 'rgba(139,92,246,.07)' },
  video_operador:{ label: 'Vídeo Operador',plat: 'Higgsfield',            color: '#0ea5e9', bg: 'rgba(14,165,233,.07)' },
  composicao:    { label: 'Composição',    plat: 'Bannerbear / Puppeteer', color: '#059669', bg: 'rgba(5,150,105,.07)'  },
  imagem:        { label: 'Imagem',        plat: 'FLUX',                  color: '#3859D0', bg: 'rgba(56,89,208,.07)'  },
  video:         { label: 'Vídeo',         plat: 'Kling',                 color: '#8b5cf6', bg: 'rgba(139,92,246,.07)' },
};

const STEP_CONFIG = {
  background:       { step: '①', label: 'Fundo',             sub: 'Imagem sem texto',             color: '#3859D0', plat: 'FLUX.2 [pro]'    },
  imagem:           { step: '①', label: 'Fundo',             sub: 'Imagem sem texto',             color: '#3859D0', plat: 'FLUX'            },
  video_produto:    { step: '①', label: 'Vídeo de fundo',    sub: 'Equipamento em acção',         color: '#8b5cf6', plat: 'Kling 3.0'       },
  video_operador:   { step: '①', label: 'Vídeo de fundo',    sub: 'Operador a trabalhar',         color: '#0ea5e9', plat: 'Higgsfield'      },
  video:            { step: '①', label: 'Vídeo de fundo',    sub: 'Equipamento em acção',         color: '#8b5cf6', plat: 'Kling'           },
  composicao:       { step: '②', label: 'Texto na imagem',   sub: 'Copy + layout → imagem final', color: '#059669', plat: 'Bannerbear'      },
  composicao_video: { step: '②', label: 'Texto no vídeo',    sub: 'Copy + timing → vídeo final',  color: '#059669', plat: 'Puppeteer+ffmpeg' },
  composicao_email: { step: '①', label: 'Template Email',    sub: 'Estrutura + copy do email',    color: '#f59e0b', plat: 'HTML Template'   },
  composicao_rsa:   { step: '①', label: 'Criativo Google',   sub: 'Headlines + descriptions',     color: '#ef4444', plat: 'Google Ads'      },
};

const PromptMiniCard = ({ pr, onApprove, onFlag }) => {
  const [expanded, setExpanded] = React.useState(false);
  const [copied,   setCopied]   = React.useState(false);
  const scfg       = STEP_CONFIG[pr.tipo] || STEP_CONFIG.background;
  const isApproved = pr.status === 'aprovado';
  const isFlagged  = pr.status === 'correcao';

  let composicaoSpec = null;
  if (pr.tipo === 'composicao' && pr.prompt_texto) {
    try { composicaoSpec = JSON.parse(pr.prompt_texto); } catch {}
  }
  const promptShort = pr.prompt_texto && pr.prompt_texto.length > 130
    ? pr.prompt_texto.slice(0, 130) + '…'
    : pr.prompt_texto;

  const handleCopy = () => {
    navigator.clipboard?.writeText(pr.prompt_texto || '').then(() => {
      setCopied(true); setTimeout(() => setCopied(false), 1600);
    });
  };

  return (
    <div style={{
      background: '#fff', borderRadius: 10, overflow: 'hidden',
      border: `1px solid ${isApproved ? 'rgba(34,197,94,.35)' : isFlagged ? 'rgba(251,146,60,.35)' : '#e2e8f0'}`,
      display: 'flex', flexDirection: 'column',
    }}>
      {/* Thumbnail — só fundo/imagem (Nano Banana refs ou legacy single) */}
      {(() => {
        if (pr.tipo !== 'background' && pr.tipo !== 'imagem') return null;
        const brandRefs = (() => {
          if (!pr.brand_asset_refs) return null;
          if (typeof pr.brand_asset_refs === 'string') {
            try { return JSON.parse(pr.brand_asset_refs); } catch { return null; }
          }
          return pr.brand_asset_refs;
        })();
        const urls = brandRefs?.image_urls || [];
        if (urls.length > 0) {
          return (
            <div style={{ height: 80, background: '#f8fafc', position: 'relative', flexShrink: 0, display: 'grid', gridTemplateColumns: `repeat(${Math.min(urls.length, 3)}, 1fr)`, gap: 2, padding: 2 }}>
              {urls.slice(0, 3).map((url, idx) => (
                <img key={idx} src={url} alt={`ref ${idx + 1}`} style={{ width: '100%', height: '100%', objectFit: 'cover', borderRadius: 3 }} onError={e => { e.target.style.opacity = '0.3'; }} />
              ))}
              <span style={{ position: 'absolute', bottom: 5, right: 7, fontSize: 8, fontWeight: 700, color: '#fff', background: 'rgba(0,0,0,.55)', padding: '1px 5px', borderRadius: 3, fontFamily: 'monospace' }}>
                {urls.length} REFS
              </span>
            </div>
          );
        }
        if (pr.imagem_referencia) {
          return (
            <div style={{ height: 80, background: '#f8fafc', position: 'relative', flexShrink: 0 }}>
              <img src={pr.imagem_referencia} alt="ref" style={{ width: '100%', height: '100%', objectFit: 'contain', padding: 6 }} />
              <span style={{ position: 'absolute', bottom: 5, right: 7, fontSize: 8, fontWeight: 700, color: '#fff', background: 'rgba(0,0,0,.45)', padding: '1px 5px', borderRadius: 3, fontFamily: 'monospace' }}>REF</span>
            </div>
          );
        }
        return null;
      })()}

      {/* Header */}
      <div style={{ padding: '9px 12px 7px', borderBottom: '1px solid #f1f5f9', background: isApproved ? 'rgba(34,197,94,.04)' : scfg.color + '0d', display: 'flex', alignItems: 'center', gap: 8 }}>
        <span style={{ fontSize: 18, fontWeight: 700, color: scfg.color, lineHeight: 1 }}>{scfg.step}</span>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 12, fontWeight: 700, color: '#1d2e38', fontFamily: 'Montserrat, sans-serif' }}>{scfg.label}</div>
          <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'monospace' }}>{pr.plataforma || scfg.plat}{pr.ratio ? ' · ' + pr.ratio : ''}</div>
        </div>
        {isApproved && <span style={{ fontSize: 10, color: '#16a34a', fontWeight: 700 }}>✓</span>}
        {isFlagged  && <span style={{ fontSize: 10, color: '#ea580c', fontWeight: 700 }}>⚑</span>}
      </div>

      {/* Conteúdo */}
      <div style={{ padding: '10px 12px', display: 'flex', flexDirection: 'column', gap: 8, flex: 1 }}>
        {composicaoSpec ? (
          <>
            {composicaoSpec.copy_overlay && (
              <div style={{ background: 'rgba(56,89,208,.06)', borderRadius: 7, padding: '8px 10px' }}>
                <div style={{ fontSize: 13, fontWeight: 700, color: '#1d2e38', fontFamily: 'Montserrat, sans-serif', lineHeight: 1.3 }}>{composicaoSpec.copy_overlay}</div>
                {composicaoSpec.cta_overlay && <div style={{ fontSize: 11, color: '#3859D0', fontWeight: 600, marginTop: 3 }}>{composicaoSpec.cta_overlay} →</div>}
              </div>
            )}
            <div style={{ display: 'flex', gap: 5, flexWrap: 'wrap' }}>
              {['overlay','headline_zone','logo_zone','accent_color'].map(k => composicaoSpec[k] && (
                <span key={k} style={{ fontSize: 9, color: '#64748b', background: '#f1f5f9', padding: '1px 6px', borderRadius: 4, fontFamily: 'monospace' }}>{k}: {String(composicaoSpec[k])}</span>
              ))}
            </div>
          </>
        ) : (
          <>
            <div style={{ fontSize: 11.5, color: '#475569', lineHeight: 1.65, fontFamily: 'Inter, sans-serif' }}>
              {expanded ? pr.prompt_texto : promptShort}
            </div>
            {pr.prompt_texto && pr.prompt_texto.length > 130 && (
              <button onClick={() => setExpanded(e => !e)} style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer', fontSize: 10, color: '#3859D0', fontFamily: 'monospace', textAlign: 'left' }}>
                {expanded ? '▲ Colapsar' : '▼ Ver prompt completo'}
              </button>
            )}
          </>
        )}

        {/* Acções */}
        <div style={{ display: 'flex', gap: 6, paddingTop: 6, borderTop: '1px solid #f1f5f9', marginTop: 'auto' }}>
          {!isApproved ? (
            <>
              <button onClick={onApprove} className="btn btn-ai" style={{ fontSize: 10, height: 24, padding: '0 10px' }}>✓ Aprovar</button>
              <button onClick={onFlag}    className="btn" style={{ fontSize: 10, height: 24, padding: '0 8px', color: '#fb923c', borderColor: 'rgba(251,146,60,.3)' }}>⚑</button>
            </>
          ) : (
            <span style={{ fontSize: 10, color: '#16a34a', fontWeight: 600 }}>✓ Aprovado</span>
          )}
          {pr.tipo !== 'composicao' && (
            <button onClick={handleCopy} className="btn" style={{ fontSize: 10, height: 24, padding: '0 8px', color: copied ? '#16a34a' : '#64748b', marginLeft: 'auto' }}>
              {copied ? '✓' : '⎘'}
            </button>
          )}
        </div>
      </div>
    </div>
  );
};

// ── TabPromptsFull ────────────────────────────────────────────────────────────
const EditPromptModal = ({ prompt: p, onClose, onSave }) => {
  const GOOGLE_FONTS = ['Montserrat','Inter','Poppins','Raleway','Open Sans','Roboto','Lato','Nunito','Work Sans','Barlow','Exo 2','Bebas Neue','Oswald','Playfair Display','Source Sans Pro'];
  const ovRaw = p?.overlay_config ? (typeof p.overlay_config === 'string' ? (() => { try { return JSON.parse(p.overlay_config); } catch { return {}; } })() : p.overlay_config) : {};
  const [form, setForm] = React.useState({
    prompt_visual:   p?.prompt_visual || p?.prompt_texto || '',
    prompt_video:    p?.prompt_video || '',
    headline_imagem: p?.headline_imagem || '',
    cta_imagem:      p?.cta_imagem || '',
    font_family:     ovRaw.font_family || 'Montserrat',
    font_weight:     ovRaw.font_weight || '700',
    text_color:      ovRaw.text_color || '#FFFFFF',
    copy_overlay:    ovRaw.copy_overlay || '',
    cta_overlay:     ovRaw.cta_overlay || '',
    overlay:         ovRaw.overlay || 'dark_gradient',
    overlay_opacity: ovRaw.overlay_opacity ? String(ovRaw.overlay_opacity) : '0.55',
    accent_color:    ovRaw.accent_color || '',
    headline_zone:   ovRaw.headline_zone || ovRaw.text_position || 'bottom',
  });
  const [showAdvanced, setShowAdvanced] = React.useState(false);

  const lbl = (text) => (
    <label style={{ fontSize: 11, fontWeight: 600, color: '#64748b', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.05em', display: 'block', marginBottom: 5 }}>{text}</label>
  );
  const inp = (key, placeholder) => (
    <input value={form[key]} onChange={e => setForm(f => ({...f, [key]: e.target.value}))} placeholder={placeholder || ''} style={{ width: '100%', padding: '8px 12px', border: '1px solid var(--border,#e2e8f0)', borderRadius: 8, fontSize: 13, outline: 'none', boxSizing: 'border-box' }} />
  );

  const handleSave = () => {
    const ovOut = { copy_overlay: form.copy_overlay, cta_overlay: form.cta_overlay, overlay: form.overlay, overlay_opacity: parseFloat(form.overlay_opacity)||0.55, accent_color: form.accent_color, headline_zone: form.headline_zone, cta_zone: ovRaw.cta_zone || 'bottom', logo_zone: ovRaw.logo_zone || 'top-left', font_family: form.font_family, font_weight: form.font_weight, text_color: form.text_color, background: 'transparent', ...( ovRaw.animation ? { animation: ovRaw.animation } : {} ), ...( ovRaw.text_timing_start ? { text_timing_start: ovRaw.text_timing_start, text_timing_end: ovRaw.text_timing_end } : {} ), ...( ovRaw.notes ? { notes: ovRaw.notes } : {} ) };
    onSave(p.id, { prompt_visual: form.prompt_visual, prompt_video: form.prompt_video, headline_imagem: form.headline_imagem, cta_imagem: form.cta_imagem, overlay_config: JSON.stringify(ovOut) });
  };

  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, width: '100%', maxWidth: 600, maxHeight: '90vh', overflow: 'auto', boxShadow: '0 20px 60px rgba(0,0,0,.2)' }}>
        <div style={{ padding: '20px 24px', borderBottom: '1px solid var(--border,#e2e8f0)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <div style={{ fontSize: 14, fontWeight: 700, fontFamily: 'var(--font-display,Montserrat,sans-serif)' }}>Editar Prompts — {CANAL_LABEL[p?.canal] || p?.canal}</div>
          <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 18, color: '#94a3b8' }}>×</button>
        </div>
        <div style={{ padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 16 }}>

          {/* Secção 1 — Texto/Headline + Tipografia */}
          <div style={{ background: 'rgba(124,58,237,.04)', border: '1px solid rgba(124,58,237,.15)', borderRadius: 8, padding: '12px 14px', display: 'flex', flexDirection: 'column', gap: 10 }}>
            <div style={{ fontSize: 9, fontWeight: 700, color: '#7c3aed', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.07em' }}>1 · Texto / Headline</div>
            <div>{lbl('Headline da Imagem')}{inp('headline_imagem')}</div>
            <div>{lbl('CTA da Imagem')}{inp('cta_imagem')}</div>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 100px 80px', gap: 10 }}>
              <div>
                {lbl('Tipo de Letra (Google Fonts)')}
                <select value={form.font_family} onChange={e => setForm(f => ({...f, font_family: e.target.value}))} style={{ width: '100%', padding: '8px 12px', border: '1px solid var(--border,#e2e8f0)', borderRadius: 8, fontSize: 13, outline: 'none', background: '#fff', fontFamily: form.font_family }}>
                  {GOOGLE_FONTS.map(fnt => <option key={fnt} value={fnt} style={{ fontFamily: fnt }}>{fnt}</option>)}
                </select>
              </div>
              <div>
                {lbl('Peso')}
                <select value={form.font_weight} onChange={e => setForm(f => ({...f, font_weight: e.target.value}))} style={{ width: '100%', padding: '8px 12px', border: '1px solid var(--border,#e2e8f0)', borderRadius: 8, fontSize: 13, outline: 'none', background: '#fff' }}>
                  <option value="400">Regular</option>
                  <option value="600">Semi-Bold</option>
                  <option value="700">Bold</option>
                  <option value="800">Extra-Bold</option>
                  <option value="900">Black</option>
                </select>
              </div>
              <div>
                {lbl('Cor texto')}
                <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
                  <input type="color" value={form.text_color} onChange={e => setForm(f => ({...f, text_color: e.target.value}))} style={{ width: 40, height: 36, padding: 2, border: '1px solid var(--border,#e2e8f0)', borderRadius: 6, cursor: 'pointer', background: '#fff' }} />
                  <input value={form.text_color} onChange={e => setForm(f => ({...f, text_color: e.target.value}))} style={{ flex: 1, padding: '8px 6px', border: '1px solid var(--border,#e2e8f0)', borderRadius: 8, fontSize: 12, outline: 'none', fontFamily: 'monospace' }} />
                </div>
              </div>
            </div>
            {/* Preview da tipografia */}
            <div style={{ background: '#1d2e38', borderRadius: 8, padding: '10px 14px', display: 'flex', alignItems: 'center', gap: 8 }}>
              <span style={{ fontSize: 9, color: '#64748b', fontFamily: 'monospace', textTransform: 'uppercase', flexShrink: 0 }}>Preview</span>
              <span style={{ fontSize: 18, fontWeight: parseInt(form.font_weight), color: form.text_color, fontFamily: `'${form.font_family}', sans-serif` }}>
                {form.headline_imagem || 'Headline da imagem'}
              </span>
            </div>
          </div>

          {/* Secção 2 — Fundo */}
          <div style={{ background: 'rgba(56,89,208,.04)', border: '1px solid rgba(56,89,208,.15)', borderRadius: 8, padding: '12px 14px', display: 'flex', flexDirection: 'column', gap: 10 }}>
            <div style={{ fontSize: 9, fontWeight: 700, color: '#3859D0', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.07em' }}>2 · Prompt Fundo (Flux / Kling)</div>
            <div>
              {lbl('Prompt Fundo (Flux)')}
              <textarea value={form.prompt_visual} onChange={e => setForm(f => ({...f, prompt_visual: e.target.value}))} rows={3} style={{ width: '100%', padding: '8px 12px', border: '1px solid var(--border,#e2e8f0)', borderRadius: 8, fontSize: 13, fontFamily: 'var(--font-body,Inter,sans-serif)', outline: 'none', resize: 'vertical', boxSizing: 'border-box' }} />
            </div>
            <div>
              {lbl('Prompt Vídeo (Kling)')}
              <textarea value={form.prompt_video} onChange={e => setForm(f => ({...f, prompt_video: e.target.value}))} rows={2} style={{ width: '100%', padding: '8px 12px', border: '1px solid var(--border,#e2e8f0)', borderRadius: 8, fontSize: 13, fontFamily: 'var(--font-body,Inter,sans-serif)', outline: 'none', resize: 'vertical', boxSizing: 'border-box' }} />
            </div>
          </div>

          {/* Secção 3 — Composição */}
          <div style={{ background: 'rgba(5,150,105,.04)', border: '1px solid rgba(5,150,105,.15)', borderRadius: 8, padding: '12px 14px', display: 'flex', flexDirection: 'column', gap: 10 }}>
            <div style={{ fontSize: 9, fontWeight: 700, color: '#059669', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.07em' }}>3 · Composição Final (Bannerbear / Puppeteer)</div>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
              <div>{lbl('Copy Overlay')}{inp('copy_overlay')}</div>
              <div>{lbl('CTA Overlay')}{inp('cta_overlay')}</div>
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 10 }}>
              <div>
                {lbl('Overlay')}
                <select value={form.overlay} onChange={e => setForm(f => ({...f, overlay: e.target.value}))} style={{ width: '100%', padding: '8px 12px', border: '1px solid var(--border,#e2e8f0)', borderRadius: 8, fontSize: 13, outline: 'none', background: '#fff' }}>
                  <option value="dark_gradient">Dark gradient</option>
                  <option value="light_gradient">Light gradient</option>
                  <option value="none">Sem overlay</option>
                </select>
              </div>
              <div>{lbl('Opacidade (0-1)')}{inp('overlay_opacity', '0.55')}</div>
              <div>{lbl('Accent color')}{inp('accent_color', '#hex')}</div>
            </div>
            <div>
              {lbl('Posição do texto')}
              <select value={form.headline_zone} onChange={e => setForm(f => ({...f, headline_zone: e.target.value}))} style={{ width: '100%', padding: '8px 12px', border: '1px solid var(--border,#e2e8f0)', borderRadius: 8, fontSize: 13, outline: 'none', background: '#fff' }}>
                <option value="bottom">Bottom</option>
                <option value="center">Centro</option>
                <option value="top">Top</option>
                <option value="bottom_third">Bottom third</option>
              </select>
            </div>
          </div>

        </div>
        <div style={{ padding: '16px 24px', borderTop: '1px solid var(--border,#e2e8f0)', display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
          <button onClick={onClose} className="btn">Cancelar</button>
          <button onClick={handleSave} className="btn btn-ai">Guardar Prompts</button>
        </div>
      </div>
    </div>
  );
};

const EditCopyModal = ({ copy: cp, onClose, onSave }) => {
  const isCriativo = cp?._mode === 'criativo';
  const [form, setForm] = React.useState(isCriativo
    ? { copy_imagem: cp?.copy_imagem || cp?.headline || '', cta_imagem: cp?.cta || '', prompt_visual: cp?.prompt_visual || '' }
    : { headline: cp?.headline || '', body: cp?.body || cp?.caption_organica || '', cta: cp?.cta || '', hashtags: cp?.hashtags || '', prompt_visual: cp?.prompt_visual || '' }
  );

  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, width: '100%', maxWidth: 540, maxHeight: '90vh', overflow: 'auto', boxShadow: '0 20px 60px rgba(0,0,0,.2)' }}>
        <div style={{ padding: '20px 24px', borderBottom: '1px solid var(--border,#e2e8f0)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <div style={{ fontSize: 14, fontWeight: 700, fontFamily: 'var(--font-display,Montserrat,sans-serif)' }}>
            {isCriativo ? 'Editar Sugestão Criativa' : 'Editar Copy'} — {CANAL_LABEL[cp?.canal] || cp?.canal}
          </div>
          <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 18, color: '#94a3b8' }}>×</button>
        </div>
        <div style={{ padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 14 }}>
          {isCriativo ? (
            <>
              {[{ key: 'copy_imagem', label: 'Headline Imagem' }, { key: 'cta_imagem', label: 'CTA Imagem' }].map(f => (
                <div key={f.key}>
                  <label style={{ fontSize: 11, fontWeight: 600, color: '#64748b', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.05em', display: 'block', marginBottom: 5 }}>{f.label}</label>
                  <input value={form[f.key]} onChange={e => setForm(fv => ({...fv, [f.key]: e.target.value}))} style={{ width: '100%', padding: '8px 12px', border: '1px solid var(--border,#e2e8f0)', borderRadius: 8, fontSize: 13, outline: 'none', boxSizing: 'border-box' }} />
                </div>
              ))}
              <div>
                <label style={{ fontSize: 11, fontWeight: 600, color: '#64748b', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.05em', display: 'block', marginBottom: 5 }}>Descrição Visual</label>
                <textarea value={form.prompt_visual} onChange={e => setForm(fv => ({...fv, prompt_visual: e.target.value}))} rows={3} style={{ width: '100%', padding: '8px 12px', border: '1px solid var(--border,#e2e8f0)', borderRadius: 8, fontSize: 13, fontFamily: 'var(--font-body,Inter,sans-serif)', outline: 'none', resize: 'vertical', boxSizing: 'border-box' }} />
              </div>
            </>
          ) : (
            <>
              {[{ key: 'headline', label: 'Headline', type: 'input' }, { key: 'body', label: 'Copy / Caption', type: 'textarea' }, { key: 'cta', label: 'CTA', type: 'input' }, { key: 'hashtags', label: 'Hashtags', type: 'input' }, { key: 'prompt_visual', label: 'Descrição Visual', type: 'textarea' }].map(f => (
                <div key={f.key}>
                  <label style={{ fontSize: 11, fontWeight: 600, color: '#64748b', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.05em', display: 'block', marginBottom: 5 }}>{f.label}</label>
                  {f.type === 'textarea'
                    ? <textarea value={form[f.key]} onChange={e => setForm(fv => ({...fv, [f.key]: e.target.value}))} rows={f.key === 'prompt_visual' ? 3 : 4} style={{ width: '100%', padding: '8px 12px', border: '1px solid var(--border,#e2e8f0)', borderRadius: 8, fontSize: 13, fontFamily: 'var(--font-body,Inter,sans-serif)', outline: 'none', resize: 'vertical', boxSizing: 'border-box' }} />
                    : <input value={form[f.key]} onChange={e => setForm(fv => ({...fv, [f.key]: e.target.value}))} style={{ width: '100%', padding: '8px 12px', border: '1px solid var(--border,#e2e8f0)', borderRadius: 8, fontSize: 13, outline: 'none', boxSizing: 'border-box' }} />
                  }
                </div>
              ))}
            </>
          )}
        </div>
        <div style={{ padding: '16px 24px', borderTop: '1px solid var(--border,#e2e8f0)', display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
          <button onClick={onClose} className="btn">Cancelar</button>
          <button onClick={() => onSave(cp.id, form)} className="btn btn-ai">Guardar</button>
        </div>
      </div>
    </div>
  );
};

const TabPromptsFull = ({ campanha, prompts, onAction, generating }) => {
  const copyDone = (campanha?.copy || []).some(c => c.status === 'aprovado') || campanha?.big_idea;
  const [expandedPromptRows, setExpandedPromptRows] = React.useState({});
  const [editingPrompt, setEditingPrompt] = React.useState(null);
  const togglePromptRow = (id) => setExpandedPromptRows(p => ({ ...p, [id]: !p[id] }));

  // Canais dinâmicos — todos os copy sem filtro de canal
  const allCopy    = campanha?.copy || [];
  const orgVisual  = allCopy.filter(c => !c.copy_type || c.copy_type === 'organico');
  const perfVisual = allCopy.filter(c => c.copy_type === 'performance');
  const MODELO_LABEL = {
    flux2_pro: 'Flux2 Pro', flux: 'Flux2 Pro',
    kling3: 'Kling3', kling: 'Kling3',
    bannerbear: 'Bannerbear',
    'fal-ai/nano-banana-pro/edit': 'Nano Banana Pro 2',
    'nano-banana-pro': 'Nano Banana Pro 2',
  };

  // Timer de progresso — exactamente igual ao Copy/Canais/Conceito
  const promptStepRef = React.useRef(null);
  const [promptPct, setPromptPct] = React.useState(0);
  React.useEffect(() => {
    if (!generating.prompts) { setPromptPct(0); return; }
    promptStepRef.current = Date.now();
    setPromptPct(0);
  }, [generating.prompts]);
  React.useEffect(() => {
    if (!generating.prompts) return;
    const iv = setInterval(() => {
      const elapsed = Date.now() - (promptStepRef.current || Date.now());
      setPromptPct(Math.min((elapsed / 20000) * 90, 90));
    }, 80);
    return () => clearInterval(iv);
  }, [generating.prompts]);
  const overallPct = Math.round(promptPct);

  if (generating.prompts) {
    const TYPE_LABEL = { post:'Post', reel:'Reel', story:'Story', carrossel:'Carrossel', email:'Email', blog_post:'Blog', video:'Vídeo', rsa:'RSA', mensagem:'WA' };

    // Progressão por item — exactamente igual ao Copy (orgDone/perfDone baseado em overallPct)
    const perfDone = Math.floor((overallPct / 90) * perfVisual.length);
    const orgDone  = Math.max(0, Math.floor((overallPct / 90) * (perfVisual.length + orgVisual.length)) - perfVisual.length);

    // Dot — ✓/●/○ igual ao Copy
    const PDot = ({ done, active }) => {
      if (done) return (
        <div style={{ width: 16, height: 16, borderRadius: '50%', background: 'rgba(21,128,61,.12)', border: '1.5px solid rgba(21,128,61,.3)', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <svg width="8" height="8" viewBox="0 0 10 10" fill="none"><polyline points="1.5,5 4,7.5 8.5,2.5" stroke="#15803d" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>
        </div>
      );
      if (active) return (
        <div style={{ width: 16, height: 16, borderRadius: '50%', border: '2px solid #3859D0', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <div style={{ width: 7, height: 7, borderRadius: '50%', background: '#3859D0', animation: 'cpulse 1s infinite' }} />
        </div>
      );
      return <div style={{ width: 16, height: 16, borderRadius: '50%', border: '1.5px solid #e2e8f0', flexShrink: 0 }} />;
    };

    const PromptLoadTable = ({ items, isOrg, doneCount }) => (
      <div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 8 }}>
          <div style={{ width: 3, height: 14, borderRadius: 2, background: isOrg ? '#059669' : '#0ea5e9', flexShrink: 0 }} />
          <span style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: isOrg ? '#059669' : '#0ea5e9', fontFamily: 'var(--font-mono,monospace)' }}>
            {isOrg ? 'Orgânico' : 'Performance'} · {doneCount}/{items.length}
          </span>
        </div>
        <table style={{ width: '100%', borderCollapse: 'collapse', tableLayout: 'fixed' }}>
          <colgroup><col style={{ width: 24 }}/><col style={{ width: 200 }}/><col style={{ width: 70 }}/><col/></colgroup>
          <thead>
            <tr style={{ background: 'var(--bg-app,#f5f6f8)', borderBottom: '1px solid var(--border,#e2e8f0)' }}>
              <th style={{ padding: '5px 4px' }}/>
              <th style={{ padding: '5px 8px', textAlign: 'left', fontSize: 9, fontWeight: 700, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase' }}>Canal</th>
              <th style={{ padding: '5px 8px', textAlign: 'left', fontSize: 9, fontWeight: 700, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase' }}>Tipo</th>
              <th style={{ padding: '5px 8px', textAlign: 'left', fontSize: 9, fontWeight: 700, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase' }}>Conteúdo</th>
            </tr>
          </thead>
          <tbody>
            {items.map((cp, idx) => {
              const isDone   = idx < doneCount;
              const isActive = idx === doneCount;
              const text = (cp.copy_imagem || cp.headline || cp.item_title || '').slice(0, 80);
              const tc = isDone ? '#374151' : isActive ? 'var(--text,#1d2e38)' : '#94a3b8';
              return (
                <tr key={cp.id || idx} style={{ borderBottom: '1px solid var(--border-light,#f1f5f9)', opacity: !isDone && !isActive && idx > doneCount + 1 ? 0.45 : 1, transition: 'opacity 0.3s' }}>
                  <td style={{ padding: '6px 4px', verticalAlign: 'middle' }}><PDot done={isDone} active={isActive} /></td>
                  <td style={{ padding: '6px 8px', verticalAlign: 'middle' }}>
                    <span style={{ fontSize: 10, fontWeight: 700, padding: '1px 6px', borderRadius: 4, background: isDone ? 'rgba(21,128,61,.08)' : 'var(--bg-app,#f5f6f8)', color: isDone ? '#15803d' : '#64748b', fontFamily: 'monospace', whiteSpace: 'nowrap', display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: 188 }}>
                      {CANAL_LABEL[cp.canal] || cp.canal}
                    </span>
                  </td>
                  <td style={{ padding: '6px 8px', verticalAlign: 'middle' }}>
                    <span style={{ fontSize: 9, color: '#64748b', fontFamily: 'monospace', textTransform: 'uppercase' }}>{TYPE_LABEL[cp.content_type] || cp.content_type || '—'}</span>
                  </td>
                  <td style={{ padding: '6px 8px', verticalAlign: 'middle' }}>
                    <span style={{ fontSize: 12, color: tc, fontWeight: isActive ? 600 : 400, fontFamily: 'var(--font-body,Inter,sans-serif)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'block' }}>{text || '—'}</span>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    );

    return (
      <div style={{ background: 'var(--bg-elev,#fff)', border: '1px solid var(--border,#e2e8f0)', borderRadius: 10, padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 16 }}>
        <style>{`@keyframes cpulse { 0%,100%{opacity:1} 50%{opacity:.25} }`}</style>
        {/* Barra de progresso — exactamente igual ao Copy/Canais/Conceito */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <div style={{ flex: 1, height: 4, borderRadius: 99, background: 'var(--bg-app,#f5f6f8)', overflow: 'hidden' }}>
            <div style={{ height: '100%', borderRadius: 99, background: '#3859D0', width: `${overallPct}%`, transition: 'width 0.1s linear' }} />
          </div>
          <span style={{ fontSize: 11, fontWeight: 700, color: '#3859D0', fontFamily: 'var(--font-mono,monospace)', flexShrink: 0 }}>{overallPct}%</span>
          <span style={{ fontSize: 11, color: '#94a3b8', fontFamily: 'var(--font-body,Inter,sans-serif)', flexShrink: 0 }}>Prompts visuais · ~20s</span>
        </div>
        {/* Performance primeiro, Orgânico depois — igual às outras fases */}
        {perfVisual.length > 0 && <PromptLoadTable items={perfVisual} isOrg={false} doneCount={perfDone} />}
        {orgVisual.length > 0  && <PromptLoadTable items={orgVisual}  isOrg={true}  doneCount={orgDone}  />}
      </div>
    );
  }

  if (!copyDone) return (
    <div style={{ padding: '32px 0', textAlign: 'center', color: 'var(--text-muted,#64748b)', fontSize: 13 }}>
      Aprova pelo menos um canal de copy antes de gerar prompts visuais.
    </div>
  );

  const total = prompts.length;
  const organicPrompts = prompts.filter(p => p.copy_type === 'organico' || !p.copy_type);
  const perfPrompts    = prompts.filter(p => p.copy_type === 'performance');

  const thStPr = { padding: '8px 12px', textAlign: 'left', fontSize: 10, fontWeight: 700, color: '#64748b', fontFamily: 'var(--font-mono,monospace)', textTransform: 'uppercase', letterSpacing: '0.05em' };

  const PromptRow = ({ p }) => {
    const isExpanded = expandedPromptRows[p.id];
    const [showArtDir, setShowArtDir] = React.useState(false);
    const tipoConteudo = p.tipo_conteudo || (p.prompt_video ? 'video' : (p.tipo && ['video_produto','video_operador','video'].includes(p.tipo) ? 'video' : 'imagem'));
    const modelKey = p.model_used || p.modelo || (tipoConteudo === 'video' ? 'kling3' : 'flux2_pro');
    const modelo = MODELO_LABEL[modelKey] || (modelKey?.includes('nano-banana') ? 'Nano Banana Pro 2' : 'Flux2 Pro');
    const conceitoVisual = p.conceito_visual || p.estilo || (p.headline_imagem ? `${p.headline_imagem} · "${p.cta_imagem || ''}"` : (p.prompt_texto ? p.prompt_texto.slice(0, 80) + (p.prompt_texto.length > 80 ? '…' : '') : '—'));
    const promptVisualText = p.prompt_visual || p.prompt_texto || '';

    // Parse brand_asset_refs (JSONB) — contains image_urls from new Nano Banana pipeline
    const brandRefs = (() => {
      if (!p.brand_asset_refs) return null;
      if (typeof p.brand_asset_refs === 'string') {
        try { return JSON.parse(p.brand_asset_refs); } catch { return null; }
      }
      return p.brand_asset_refs;
    })();
    const refImageUrls = brandRefs?.image_urls || [];
    const refStrategy  = brandRefs?.strategy || null;
    const hasNewRefs   = refImageUrls.length > 0;
    const hasLegacyRef = !!p.imagem_referencia;

    return (
      <React.Fragment>
        <tr style={{ borderBottom: '1px solid var(--border,#e2e8f0)', cursor: 'pointer', background: isExpanded ? 'rgba(56,89,208,.02)' : 'transparent' }} onClick={() => togglePromptRow(p.id)}>
          <td style={{ padding: '10px 12px' }}>
            <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--text,#1d2e38)' }}>{CANAL_LABEL[p.canal] || p.canal}</div>
          </td>
          <td style={{ padding: '10px 12px' }}>
            <div style={{ display: 'flex', gap: 4, alignItems: 'center', flexWrap: 'wrap' }}>
              <span style={{
                fontSize: 10, padding: '2px 8px', borderRadius: 99, fontWeight: 600,
                background: tipoConteudo === 'video' ? 'rgba(239,68,68,.08)' : 'rgba(56,89,208,.08)',
                color: tipoConteudo === 'video' ? '#dc2626' : '#3859D0',
                border: `1px solid ${tipoConteudo === 'video' ? 'rgba(239,68,68,.2)' : 'rgba(56,89,208,.2)'}`,
                fontFamily: 'monospace',
              }}>
                {tipoConteudo === 'video' ? 'Vídeo' : 'Imagem'}
              </span>
              <span style={{ fontSize: 10, color: '#64748b', fontFamily: 'monospace' }}>· {modelo}</span>
            </div>
          </td>
          <td style={{ padding: '10px 12px', maxWidth: 280 }}>
            <div style={{ fontSize: 12, color: 'var(--text,#1d2e38)', lineHeight: 1.4 }}>
              {conceitoVisual.slice(0, 100)}{conceitoVisual.length > 100 ? '…' : ''}
            </div>
          </td>
          <td style={{ padding: '10px 12px' }}>
            <div style={{ display: 'flex', gap: 4 }}>
              <button onClick={e => { e.stopPropagation(); onAction('regeneratePrompt', p.id); }} style={{ background: 'none', border: '1px solid var(--border,#e2e8f0)', borderRadius: 5, padding: '3px 6px', cursor: 'pointer', fontSize: 11, color: '#64748b' }} title="Regenerar">⟳</button>
              <button onClick={e => { e.stopPropagation(); setEditingPrompt(p); }} style={{ background: 'none', border: '1px solid var(--border,#e2e8f0)', borderRadius: 5, padding: '3px 6px', cursor: 'pointer', fontSize: 11, color: '#64748b' }} title="Editar">✏</button>
              <span style={{ fontSize: 11, color: '#94a3b8', padding: '3px 4px' }}>{isExpanded ? '▴' : '▾'}</span>
            </div>
          </td>
        </tr>
        {isExpanded && (
          <tr>
            <td colSpan={4} style={{ padding: '0', background: 'rgba(56,89,208,.02)' }}>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 0 }}>

                {/* ── SECÇÃO 1: Texto / Headline — 1fr | 1fr ── */}
                {(() => {
                  const ov = p.overlay_config ? (typeof p.overlay_config === 'string' ? (() => { try { return JSON.parse(p.overlay_config); } catch { return {}; } })() : p.overlay_config) : {};
                  const overlayTool = tipoConteudo === 'video' ? 'Puppeteer' : 'Bannerbear';
                  if (!p.headline_imagem && !p.cta_imagem) return null;
                  const secColor = '#7c3aed';
                  return (
                    <div style={{ padding: '14px 16px', borderBottom: '1px solid var(--border-light,#f1f5f9)' }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
                        <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: secColor, fontFamily: 'var(--font-mono,monospace)' }}>1 · Texto / Headline da Imagem</div>
                        <span style={{ fontSize: 9, padding: '2px 7px', borderRadius: 99, background: `rgba(124,58,237,.1)`, color: secColor, fontFamily: 'var(--font-mono,monospace)', fontWeight: 700 }}>{overlayTool}</span>
                      </div>
                      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
                        {/* Coluna esq — conteúdo */}
                        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                          <div>
                            <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'var(--font-mono,monospace)', textTransform: 'uppercase', marginBottom: 4 }}>Headline</div>
                            <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--text,#1d2e38)', lineHeight: 1.35, fontFamily: 'var(--font-display,Montserrat,sans-serif)' }}>{p.headline_imagem || '—'}</div>
                          </div>
                          <div>
                            <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'var(--font-mono,monospace)', textTransform: 'uppercase', marginBottom: 4 }}>CTA</div>
                            <div style={{ fontSize: 13, color: 'var(--text,#1d2e38)', fontFamily: 'var(--font-body,Inter,sans-serif)' }}>{p.cta_imagem || '—'}</div>
                          </div>
                          <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginTop: 8, flexWrap: 'wrap' }}>
                            <div style={{ width: 18, height: 18, borderRadius: 4, background: ov.text_color || '#FFFFFF', border: '1px solid rgba(0,0,0,.15)' }} title={`Cor: ${ov.text_color || '#FFFFFF'}`} />
                            <span style={{ fontSize: 11, color: '#64748b', fontFamily: 'var(--font-mono,monospace)' }}>{ov.font_family || 'Montserrat'} {ov.font_weight || '700'}</span>
                            <span style={{ fontSize: 11, color: '#64748b', fontFamily: 'var(--font-mono,monospace)' }}>{ov.text_color || '#FFFFFF'}</span>
                            <span style={{ fontSize: 9, padding: '1px 6px', borderRadius: 3, background: 'rgba(5,150,105,.08)', color: '#059669', fontFamily: 'monospace' }}>{ov.background || 'transparent'}</span>
                          </div>
                        </div>
                        {/* Coluna dir — "prompt" Bannerbear como JSON */}
                        <div>
                          <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'var(--font-mono,monospace)', textTransform: 'uppercase', marginBottom: 4 }}>Prompt {overlayTool}</div>
                          <div style={{ fontSize: 11, color: 'var(--text,#1d2e38)', lineHeight: 1.7, background: 'var(--bg-app,#f5f6f8)', padding: '10px 14px', borderRadius: 8, fontFamily: 'var(--font-mono,monospace)', whiteSpace: 'pre-wrap', overflowX: 'auto' }}>
                            {JSON.stringify(ov, null, 2)}
                          </div>
                          {ov.notes && <div style={{ marginTop: 6, fontSize: 11, color: '#64748b', fontStyle: 'italic', lineHeight: 1.5 }}>{ov.notes}</div>}
                        </div>
                      </div>
                    </div>
                  );
                })()}

                {/* ── SECÇÃO 2: Fundo (Flux/Kling) — 1fr | 1fr ── */}
                {(promptVisualText || p.prompt_video) && (() => {
                  const secColor = tipoConteudo === 'video' ? '#dc2626' : '#3859D0';
                  const secBg    = tipoConteudo === 'video' ? 'rgba(220,38,38,.1)' : 'rgba(56,89,208,.1)';
                  return (
                    <div style={{ padding: '14px 16px' }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
                        <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: secColor, fontFamily: 'var(--font-mono,monospace)' }}>
                          2 · {tipoConteudo === 'video' ? 'Prompt Vídeo' : 'Prompt Fundo'}
                        </div>
                        <span style={{ fontSize: 9, padding: '2px 7px', borderRadius: 99, background: secBg, color: secColor, fontFamily: 'var(--font-mono,monospace)', fontWeight: 700 }}>{modelo}</span>
                      </div>
                      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
                        {/* Coluna esq — imagens de referência (Nano Banana: benchmark + product refs) */}
                        <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                          {hasNewRefs ? (
                            <>
                              <div style={{ display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
                                <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'var(--font-mono,monospace)', textTransform: 'uppercase' }}>
                                  Refs ({refImageUrls.length}) · {modelo} input
                                </div>
                                {refStrategy && (
                                  <span style={{ fontSize: 8, padding: '1px 6px', borderRadius: 3, background: 'rgba(56,89,208,.08)', color: '#3859D0', fontFamily: 'monospace', fontWeight: 700, textTransform: 'uppercase' }}>
                                    {refStrategy}
                                  </span>
                                )}
                              </div>
                              <div style={{ display: 'grid', gridTemplateColumns: refImageUrls.length > 1 ? 'repeat(2, 1fr)' : '1fr', gap: 6 }}>
                                {refImageUrls.map((url, idx) => (
                                  <a key={idx} href={url} target="_blank" rel="noreferrer" onClick={e => e.stopPropagation()} style={{ display: 'block', position: 'relative' }}>
                                    <img src={url} alt={`ref ${idx + 1}`} style={{ width: '100%', borderRadius: 6, border: '1px solid var(--border,#e2e8f0)', objectFit: 'cover', background: '#fff', cursor: 'zoom-in', display: 'block', aspectRatio: '1', maxHeight: 140 }} onError={e => { e.target.style.opacity='0.3'; }} />
                                    <span style={{ position: 'absolute', top: 4, left: 4, fontSize: 8, fontWeight: 700, color: '#fff', background: 'rgba(0,0,0,.55)', padding: '1px 5px', borderRadius: 3, fontFamily: 'monospace' }}>@img{idx + 1}</span>
                                  </a>
                                ))}
                              </div>
                              <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'var(--font-body,Inter,sans-serif)' }}>Clica para ver completa</div>
                            </>
                          ) : hasLegacyRef ? (
                            <>
                              <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'var(--font-mono,monospace)', textTransform: 'uppercase' }}>Imagem Referência ({modelo} input)</div>
                              <a href={p.imagem_referencia} target="_blank" rel="noreferrer" onClick={e => e.stopPropagation()} style={{ display: 'block' }}>
                                <img src={p.imagem_referencia} alt="referência" style={{ width: '100%', borderRadius: 8, border: '1px solid var(--border,#e2e8f0)', objectFit: 'contain', background: '#fff', cursor: 'zoom-in', display: 'block', maxHeight: 180 }} onError={e => { e.target.style.display='none'; }} />
                              </a>
                              <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'var(--font-body,Inter,sans-serif)' }}>Clica para ver completa</div>
                            </>
                          ) : (
                            <div style={{ fontSize: 12, color: '#94a3b8', fontStyle: 'italic', padding: '20px 0' }}>Sem imagem de referência</div>
                          )}
                        </div>
                        {/* Coluna dir — prompt prose */}
                        <div>
                          <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'var(--font-mono,monospace)', textTransform: 'uppercase', marginBottom: 4 }}>Prompt {modelo}</div>
                          <div style={{ fontSize: 12, color: 'var(--text,#1d2e38)', lineHeight: 1.7, background: 'var(--bg-app,#f5f6f8)', padding: '10px 14px', borderRadius: 8, whiteSpace: 'pre-wrap', fontFamily: 'var(--font-body,Inter,sans-serif)' }}>
                            {promptVisualText || p.prompt_video}
                          </div>
                        </div>
                      </div>
                    </div>
                  );
                })()}

                {/* ── SECÇÃO 3: Composição Final (merge Fundo + Texto) — 1fr | 1fr ── */}
                {(() => {
                  const ov = p.overlay_config ? (typeof p.overlay_config === 'string' ? (() => { try { return JSON.parse(p.overlay_config); } catch { return {}; } })() : p.overlay_config) : {};
                  const overlayTool = tipoConteudo === 'video' ? 'Puppeteer' : 'Bannerbear';
                  const mergePlatform = tipoConteudo === 'video' ? 'FFmpeg / Puppeteer' : 'Bannerbear Compositor';
                  const mergeConfig = {
                    platform: mergePlatform,
                    background_layer: `flux_output.${tipoConteudo === 'video' ? 'mp4' : 'png'}`,
                    text_layer: `${overlayTool.toLowerCase()}_text.png`,
                    blend_mode: 'normal',
                    output_format: tipoConteudo === 'video' ? 'MP4' : 'PNG',
                    font_family: ov.font_family || 'Montserrat',
                    font_weight: ov.font_weight || '700',
                    text_color: ov.text_color || '#FFFFFF',
                    text_position: ov.headline_zone || ov.text_position || 'bottom',
                    logo_zone: ov.logo_zone || 'top-left',
                    overlay_type: ov.overlay || 'dark_gradient',
                    overlay_opacity: ov.overlay_opacity || 0.55,
                    accent_color: ov.accent_color,
                    ...(tipoConteudo === 'video' && ov.text_timing_start ? { text_in: ov.text_timing_start, text_out: ov.text_timing_end, animation: ov.animation } : {}),
                  };
                  return (
                    <div style={{ padding: '14px 16px', borderTop: '1px solid var(--border-light,#f1f5f9)' }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
                        <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#059669', fontFamily: 'var(--font-mono,monospace)' }}>3 · Composição Final</div>
                        <span style={{ fontSize: 9, padding: '2px 7px', borderRadius: 99, background: 'rgba(5,150,105,.1)', color: '#059669', fontFamily: 'var(--font-mono,monospace)', fontWeight: 700 }}>{mergePlatform}</span>
                      </div>
                      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
                        {/* Coluna esq — inputs do merge */}
                        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                          <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'var(--font-mono,monospace)', textTransform: 'uppercase', marginBottom: 2 }}>Inputs</div>
                          {[
                            { label: 'Fundo', value: mergeConfig.background_layer, color: '#3859D0' },
                            { label: 'Texto (PNG)', value: mergeConfig.text_layer, color: '#7c3aed' },
                            { label: 'Output', value: mergeConfig.output_format },
                            { label: 'Blend', value: mergeConfig.blend_mode },
                          ].map(f => (
                            <div key={f.label} style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
                              <span style={{ fontSize: 9, fontWeight: 700, color: f.color || '#64748b', fontFamily: 'monospace', textTransform: 'uppercase', minWidth: 50 }}>{f.label}</span>
                              <span style={{ fontSize: 11, color: 'var(--text,#1d2e38)', fontFamily: 'monospace' }}>{f.value}</span>
                            </div>
                          ))}
                        </div>
                        {/* Coluna dir — prompt de merge como JSON */}
                        <div>
                          <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'var(--font-mono,monospace)', textTransform: 'uppercase', marginBottom: 4 }}>Prompt {mergePlatform}</div>
                          <div style={{ fontSize: 11, color: 'var(--text,#1d2e38)', lineHeight: 1.7, background: 'var(--bg-app,#f5f6f8)', padding: '10px 14px', borderRadius: 8, fontFamily: 'var(--font-mono,monospace)', whiteSpace: 'pre-wrap', overflowX: 'auto' }}>
                            {JSON.stringify(mergeConfig, null, 2)}
                          </div>
                        </div>
                      </div>
                    </div>
                  );
                })()}

                {/* ── SECÇÃO 4: Art Direction (creative_plan) — collapsível ── */}
                {(() => {
                  const cp = p.creative_plan
                    ? (typeof p.creative_plan === 'string' ? (() => { try { return JSON.parse(p.creative_plan); } catch { return null; } })() : p.creative_plan)
                    : null;
                  if (!cp) return null;

                  const MODEL_COLORS = {
                    'fal-ai/nano-banana/edit':     { bg: 'rgba(5,150,105,.1)',  color: '#059669', label: 'nano-banana · product preserve' },
                    'fal-ai/flux-pro/v1.1-ultra':  { bg: 'rgba(56,89,208,.1)', color: '#3859D0', label: 'Flux Pro Ultra · photoreal'      },
                    'fal-ai/flux-pro/v1.1':        { bg: 'rgba(56,89,208,.07)',color: '#3859D0', label: 'Flux Pro · background'           },
                    'fal-ai/ideogram-v2':           { bg: 'rgba(124,58,237,.1)',color: '#7c3aed', label: 'Ideogram v2 · text rendering'   },
                    'fal-ai/recraft-v3':            { bg: 'rgba(234,88,12,.1)', color: '#ea580c', label: 'Recraft v3 · graphic design'    },
                  };
                  const modelStyle = MODEL_COLORS[cp.model] || { bg: 'rgba(100,116,139,.1)', color: '#64748b', label: cp.model || 'modelo' };

                  return (
                    <div style={{ padding: '12px 16px', borderTop: '1px solid var(--border-light,#f1f5f9)' }}>
                      {/* Header clicável */}
                      <div
                        onClick={() => setShowArtDir(v => !v)}
                        style={{ display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', userSelect: 'none' }}
                      >
                        <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#c026d3', fontFamily: 'var(--font-mono,monospace)' }}>
                          4 · Art Direction
                        </div>
                        <span style={{ fontSize: 9, padding: '2px 8px', borderRadius: 99, background: modelStyle.bg, color: modelStyle.color, fontFamily: 'var(--font-mono,monospace)', fontWeight: 700 }}>
                          {modelStyle.label}
                        </span>
                        {cp.copy_anchor?.tension_or_promise && (
                          <span style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'monospace', marginLeft: 4 }}>
                            · {cp.copy_anchor.tension_or_promise}
                          </span>
                        )}
                        <span style={{ marginLeft: 'auto', fontSize: 10, color: '#94a3b8' }}>{showArtDir ? '▲' : '▼'}</span>
                      </div>

                      {showArtDir && (
                        <div style={{ marginTop: 12, display: 'flex', flexDirection: 'column', gap: 12 }}>

                          {/* Copy anchor */}
                          {cp.copy_anchor && (
                            <div style={{ background: 'rgba(192,38,211,.05)', border: '1px solid rgba(192,38,211,.15)', borderRadius: 8, padding: '10px 14px' }}>
                              <div style={{ fontSize: 9, fontWeight: 700, color: '#c026d3', fontFamily: 'monospace', textTransform: 'uppercase', marginBottom: 6 }}>Copy Anchor</div>
                              <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                                {[
                                  ['Headline echoes', cp.copy_anchor.headline_echoes],
                                  ['CTA supports',    cp.copy_anchor.cta_supports],
                                  ['Tension/Promise', cp.copy_anchor.tension_or_promise],
                                ].filter(([,v]) => v).map(([k, v]) => (
                                  <div key={k} style={{ display: 'flex', gap: 8 }}>
                                    <span style={{ fontSize: 9, fontWeight: 700, color: '#94a3b8', minWidth: 100, fontFamily: 'monospace', textTransform: 'uppercase' }}>{k}</span>
                                    <span style={{ fontSize: 11, color: 'var(--text,#1d2e38)', lineHeight: 1.5 }}>{v}</span>
                                  </div>
                                ))}
                              </div>
                            </div>
                          )}

                          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
                            {/* Coluna esq — Image refs */}
                            <div>
                              <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', marginBottom: 6 }}>Referências R2 ({(cp.image_urls || []).length})</div>
                              {(cp.image_urls || []).length > 0 ? (
                                <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
                                  {cp.image_urls.map((url, i) => (
                                    <a key={i} href={url} target="_blank" rel="noreferrer" style={{ display: 'block', flexShrink: 0 }}>
                                      <img src={url} alt={`ref ${i+1}`} style={{ width: 72, height: 72, objectFit: 'cover', borderRadius: 6, border: '1px solid var(--border,#e2e8f0)', cursor: 'zoom-in' }} onError={e => { e.target.style.display='none'; }} />
                                    </a>
                                  ))}
                                </div>
                              ) : (
                                <div style={{ fontSize: 11, color: '#94a3b8', fontStyle: 'italic' }}>Sem refs R2 — geração text-to-image</div>
                              )}
                              {cp.image_urls_rationale && (
                                <div style={{ marginTop: 6, fontSize: 10, color: '#64748b', lineHeight: 1.5 }}>{cp.image_urls_rationale}</div>
                              )}

                              {/* Negative cues */}
                              {(cp.negative_cues || []).length > 0 && (
                                <div style={{ marginTop: 10 }}>
                                  <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', marginBottom: 4 }}>Negative cues</div>
                                  <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
                                    {cp.negative_cues.map((nc, i) => (
                                      <span key={i} style={{ fontSize: 9, padding: '2px 7px', borderRadius: 99, background: 'rgba(239,68,68,.08)', color: '#dc2626', fontFamily: 'monospace', border: '1px solid rgba(239,68,68,.2)' }}>{nc}</span>
                                    ))}
                                  </div>
                                </div>
                              )}
                            </div>

                            {/* Coluna dir — Art Direction details */}
                            <div>
                              <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', marginBottom: 6 }}>Direcção Fotográfica</div>
                              {cp.art_direction ? (
                                <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                                  {[
                                    ['Lens',        cp.art_direction.lens],
                                    ['Grain',       cp.art_direction.grain],
                                    ['Lighting',    cp.art_direction.lighting],
                                    ['Composition', cp.art_direction.composition],
                                  ].filter(([,v]) => v).map(([k, v]) => (
                                    <div key={k} style={{ display: 'flex', gap: 8 }}>
                                      <span style={{ fontSize: 9, fontWeight: 700, color: '#94a3b8', minWidth: 75, fontFamily: 'monospace', textTransform: 'uppercase' }}>{k}</span>
                                      <span style={{ fontSize: 11, color: 'var(--text,#1d2e38)', lineHeight: 1.5 }}>{v}</span>
                                    </div>
                                  ))}
                                  {cp.art_direction.rationale && (
                                    <div style={{ marginTop: 4, padding: '6px 10px', background: 'var(--bg-app,#f5f6f8)', borderRadius: 6, fontSize: 10, color: '#64748b', lineHeight: 1.5, fontStyle: 'italic' }}>
                                      {cp.art_direction.rationale}
                                    </div>
                                  )}
                                </div>
                              ) : (
                                <div style={{ fontSize: 11, color: '#94a3b8', fontStyle: 'italic' }}>—</div>
                              )}

                              {/* Model rationale */}
                              {cp.model_rationale && (
                                <div style={{ marginTop: 10 }}>
                                  <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', marginBottom: 4 }}>Modelo · Justificação</div>
                                  <div style={{ fontSize: 10, color: '#64748b', lineHeight: 1.5, padding: '6px 10px', background: modelStyle.bg, borderRadius: 6, border: `1px solid ${modelStyle.color}22` }}>
                                    {cp.model_rationale}
                                  </div>
                                </div>
                              )}
                            </div>
                          </div>

                          {/* Enriched prompt */}
                          {cp.enriched_prompt && (
                            <div>
                              <div style={{ fontSize: 9, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', marginBottom: 4 }}>Prompt Enriquecido (Art Director)</div>
                              <div style={{ fontSize: 11, color: 'var(--text,#1d2e38)', lineHeight: 1.7, background: 'var(--bg-app,#f5f6f8)', padding: '10px 14px', borderRadius: 8, fontFamily: 'var(--font-mono,monospace)', whiteSpace: 'pre-wrap', overflowX: 'auto' }}>
                                {cp.enriched_prompt}
                              </div>
                            </div>
                          )}
                        </div>
                      )}
                    </div>
                  );
                })()}

              </div>
            </td>
          </tr>
        )}
      </React.Fragment>
    );
  };

  const renderPromptsSection = (items, isOrganic) => {
    if (!items.length) return null;
    const accentColor = isOrganic ? '#059669' : '#0ea5e9';
    const label       = isOrganic ? 'Orgânico · Plano de Comunicação' : 'Performance · Anúncios Pagos';
    const countLabel  = isOrganic ? `${items.length} peças` : `${items.length} ads`;
    return (
      <CollapsBlock key={isOrganic ? 'org' : 'perf'} title={label} count={countLabel} defaultOpen={true} accentColor={accentColor}>
        <table style={{ width: '100%', borderCollapse: 'collapse' }}>
          <thead>
            <tr style={{ background: 'var(--bg-app,#f5f6f8)' }}>
              <th style={thStPr}>Canal</th>
              <th style={thStPr}>Tipo · Modelo</th>
              <th style={thStPr}>Conceito Visual</th>
              <th style={thStPr}>Acções</th>
            </tr>
          </thead>
          <tbody>
            {items.map(p => <PromptRow key={p.id} p={p} />)}
          </tbody>
        </table>
      </CollapsBlock>
    );
  };

  // Empty state — padrão centrado igual às outras fases
  if (total === 0) return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '64px 0', gap: 20 }}>
      <div style={{ width: 56, height: 56, borderRadius: 14, background: 'rgba(236,72,153,.08)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#ec4899" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
          <rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/>
        </svg>
      </div>
      <div style={{ textAlign: 'center' }}>
        <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--text,#1d2e38)', fontFamily: 'var(--font-display,Montserrat,sans-serif)', marginBottom: 10 }}>Prompts Visuais por gerar</div>
        <div style={{ display: 'flex', gap: 12, justifyContent: 'center', flexWrap: 'wrap' }}>
          {perfVisual.length > 0 && (
            <div style={{ background: 'rgba(14,165,233,.06)', border: '1px solid rgba(14,165,233,.2)', borderRadius: 10, padding: '10px 18px', textAlign: 'left' }}>
              <div style={{ fontSize: 10, fontWeight: 700, color: '#0ea5e9', fontFamily: 'var(--font-mono,monospace)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 4 }}>Performance</div>
              <div style={{ fontSize: 13, color: 'var(--text,#1d2e38)', fontWeight: 600 }}>{perfVisual.length} {perfVisual.length === 1 ? 'canal' : 'canais'}</div>
            </div>
          )}
          {orgVisual.length > 0 && (
            <div style={{ background: 'rgba(5,150,105,.06)', border: '1px solid rgba(5,150,105,.2)', borderRadius: 10, padding: '10px 18px', textAlign: 'left' }}>
              <div style={{ fontSize: 10, fontWeight: 700, color: '#059669', fontFamily: 'var(--font-mono,monospace)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 4 }}>Orgânico</div>
              <div style={{ fontSize: 13, color: 'var(--text,#1d2e38)', fontWeight: 600 }}>{orgVisual.length} {orgVisual.length === 1 ? 'peça' : 'peças'}</div>
            </div>
          )}
        </div>
      </div>
      <button onClick={() => onAction('generatePrompts')} disabled={generating.prompts} className="btn btn-ai" style={{ fontSize: 13, padding: '10px 24px' }} data-tutorial-step="prompts">
        Gerar Prompts com Digi AI
      </button>
    </div>
  );

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

      {/* Secções — Performance primeiro, Orgânico depois */}
      {renderPromptsSection(perfPrompts, false)}
      {renderPromptsSection(organicPrompts, true)}


      {/* Modal de edição de prompt */}
      {editingPrompt && (
        <EditPromptModal
          prompt={editingPrompt}
          onClose={() => setEditingPrompt(null)}
          onSave={(id, data) => { onAction('saveCopy', id, data); setEditingPrompt(null); }}
        />
      )}
    </div>
  );
};

// ── TabAprovacao ───────────────────────────────────────────────────────────────
const MKT_APPROVERS = ['fabio.costa@digidelta.pt', 'rui.leitao@digidelta.pt', 'armando.mota@digidelta.pt', 'joao.paulino@digidelta.pt'];

// ── Modais de Aprovação ────────────────────────────────────────────────────────
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   = MKT_APPROVERS.includes((userEmail || '').toLowerCase()) || isMktOwner(userEmail);
  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 [showPedirAlt, setShowPedirAlt] = React.useState(false);
  const [showRejeitar, setShowRejeitar] = React.useState(false);
  const [submitting,   setSubmitting]   = React.useState(false);
  const [sending,      setSending]      = React.useState(false);
  const [orcStatus,    setOrcStatus]    = React.useState(null);
  const [segStatus,    setSegStatus]    = React.useState(null);
  const [planStatus,   setPlanStatus]   = React.useState(null);

  React.useEffect(() => {
    if (!campanha?.id) return;
    const mapS = (rows) => {
      if (!rows?.length) return 'pending';
      return rows.every(r => r.approved_at) ? 'approved' : rows.some(r => r.approved_at) ? 'partial' : 'done';
    };
    campApiCall(`/api/marketing/campanhas/${campanha.id}/orcamento`).then(d => setOrcStatus(mapS(d.rows))).catch(() => setOrcStatus('pending'));
    campApiCall(`/api/marketing/campanhas/${campanha.id}/segmentacao`).then(d => setSegStatus(mapS(d.rows))).catch(() => setSegStatus('pending'));
    campApiCall(`/api/marketing/campanhas/${campanha.id}/planeamento`).then(d => setPlanStatus(mapS(d.rows))).catch(() => setPlanStatus('pending'));
  }, [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_mix || []).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?.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' ? 'Aprovada' : segStatus === 'done' ? 'Gerada — aguarda aprovação' : 'Não gerada' },
    { 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 ? (isPending || isInProducao ? 'approved' : 'done') : 'pending',
      detail: markets.length ? `${allChs.map(c=>chLabels[c]||c).join(' · ')} · 5 layers` : 'Depende da Estratégia' },
  ];
  // Ordem: Briefing → Estratégia → Conceito → Orçamento → Target → Planeamento → Funil

  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 >= 5; // briefing + estrategia + conceito + planeamento + funil 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 allCopy    = campanha?.copy    || [];
    const allPrompts = campanha?.prompts || [];
    const orgCount   = allCopy.filter(c => !c.copy_type || c.copy_type === 'organico').length;
    const perfCount  = allCopy.filter(c => c.copy_type === 'performance').length;
    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:6 }}>
            <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>
          {approveRecord && (
            <div style={{ fontSize:12, color:'#64748b', marginBottom:16 }}>
              Aprovado por <strong>{approveRecord.approver_name || approveRecord.approver_email}</strong> · {fmtDate(approveRecord.created_at)}
            </div>
          )}
          <div style={{ display:'grid', gridTemplateColumns:'repeat(4,1fr)', gap:10, marginBottom:16 }}>
            {[
              { lbl:'Peças orgânicas', val: orgCount > 0 ? String(orgCount) : '—' },
              { lbl:'Anúncios',        val: perfCount > 0 ? String(perfCount) : '—' },
              { lbl:'Prompts visuais', val: allPrompts.length > 0 ? String(allPrompts.length) : '—' },
              { 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={() => { if (window.mktNavToSub) window.mktNavToSub('conteudos'); }}
            className="btn btn-ai" style={{ fontSize:13 }}>→ Acompanhar no Módulo Produção de Conteúdos</button>
        </div>
        {/* Histórico */}
        <AprovacaoHistory approvals={approvals} timeAgo={timeAgo} />
      </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} · Campanha "{campanha?.titulo}"</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>

        {/* Resumo da campanha para revisão executiva */}
        <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 }}>Campanha · Resumo para Aprovação</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>

        {/* Fases — estado actual */}
        <div>
          <div style={{ fontSize:10, fontWeight:700, fontFamily:'var(--font-mono)', letterSpacing:'.08em', textTransform:'uppercase', color:'var(--text-dim)', marginBottom:10 }}>Estado das Fases ({phasesApproved}/{CAMP_PHASES.length} aprovadas)</div>
          <div style={{ display:'grid', gridTemplateColumns:'repeat(7,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>
                </div>
              );
            })}
          </div>
        </div>

        <AprovacaoHistory approvals={approvals} timeAgo={timeAgo} />

        {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 }}>
            {isMktOwner(userEmail) && (
              <button className="btn btn-ai" disabled={sending || !canSubmit} style={{ opacity: canSubmit ? 1 : .5 }}
                onClick={async () => { setSending(true); await onAction('enviarAprovacao').catch(()=>{}); setSending(false); }}>
                {sending ? 'A enviar...' : '→ Enviar para Aprovação Executiva'}
              </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>

      {/* Fases checklist */}
      <div>
        <div style={{ fontSize:10, fontWeight:700, fontFamily:'var(--font-mono)', letterSpacing:'.08em', textTransform:'uppercase', color:'var(--text-dim)', marginBottom:10 }}>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 e Funil Multicanal concluídos.
        </div>
      )}

      <AprovacaoHistory approvals={approvals} timeAgo={timeAgo} />
    </div>
  );
};

const AprovacaoHistory = ({ approvals, timeAgo }) => {
  if (!approvals?.length) return null;

  const fmtExact = iso => iso ? new Date(iso).toLocaleDateString('pt-PT', { day:'2-digit', month:'short', year:'numeric', hour:'2-digit', minute:'2-digit' }) : '—';

  const ACTION_META = {
    approved:                  { label:'Aprovado',                      col:'#15803d', bg:'rgba(22,163,74,.08)' },
    rejected:                  { label:'Rejeitado',                     col:'#dc2626', bg:'rgba(220,38,38,.07)' },
    changes_requested:         { label:'Alterações pedidas',            col:'#d97706', bg:'rgba(217,119,6,.07)' },
    request_changes:           { label:'Alterações pedidas',            col:'#d97706', bg:'rgba(217,119,6,.07)' },
    submitted_for_approval:    { label:'Enviado para aprovação exec.',   col:'#3859D0', bg:'rgba(56,89,208,.07)' },
    reverted:                  { label:'Revertido',                     col:'#64748b', bg:'rgba(100,116,139,.07)' },
  };

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

  return (
    <div>
      <div style={{ fontSize:10, fontWeight:700, fontFamily:'var(--font-mono)', letterSpacing:'.08em', textTransform:'uppercase', color:'var(--text-dim)', marginBottom:10 }}>Histórico de Decisões</div>
      <div style={{ display:'flex', flexDirection:'column', gap:6 }}>
        {approvals.slice(0, 10).map((a, i) => {
          const meta = ACTION_META[a.action] || { label: a.action, col:'#64748b', bg:'var(--bg-sunken)' };
          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;
          return (
            <div key={i} style={{ display:'grid', gridTemplateColumns:'auto 1fr auto', gap:12, alignItems:'flex-start', padding:'12px 16px', background:meta.bg, border:'1px solid var(--border)', borderRadius:8 }}>
              {/* Left: dot + action */}
              <div style={{ display:'flex', flexDirection:'column', alignItems:'center', gap:4 }}>
                <div style={{ width:10, height:10, borderRadius:'50%', background:meta.col, marginTop:2, flexShrink:0 }} />
              </div>
              {/* Center: action + phase + person + notes */}
              <div>
                <div style={{ display:'flex', alignItems:'center', gap:6, flexWrap:'wrap', marginBottom:3 }}>
                  <span style={{ fontSize:12, fontWeight:700, color:meta.col }}>{meta.label}</span>
                  {a.phase && <span style={{ fontSize:10, fontFamily:'var(--font-mono)', color:'var(--text-dim)', background:'rgba(0,0,0,.06)', padding:'1px 7px', borderRadius:4 }}>{PHASE_LABEL[a.phase] || a.phase}</span>}
                </div>
                {(name || email) && (
                  <div style={{ display:'flex', alignItems:'center', gap:6 }}>
                    <div style={{ width:18, height:18, borderRadius:'50%', background:'var(--bg-sunken)', border:'1px solid var(--border)', display:'flex', alignItems:'center', justifyContent:'center', fontSize:9, fontWeight:700, color:'var(--text-dim)', fontFamily:'var(--font-mono)' }}>
                      {(name || email || '?')[0].toUpperCase()}
                    </div>
                    <div>
                      {name && <span style={{ fontSize:12, fontWeight:600, color:'var(--text)' }}>{name}</span>}
                      {email && <span style={{ fontSize:10, color:'var(--text-muted)', marginLeft: name ? 5 : 0 }}>{email}</span>}
                    </div>
                  </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>
              {/* Right: timestamps */}
              <div style={{ textAlign:'right', flexShrink:0 }}>
                <div style={{ fontSize:11, fontWeight:600, color:'var(--text-muted)' }}>{timeAgo(a.created_at)}</div>
                <div style={{ fontSize:10, color:'var(--text-dim)', marginTop:2, fontFamily:'var(--font-mono)' }}>{fmtExact(a.created_at)}</div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
};


// ── TabProducao ────────────────────────────────────────────────────────────────
const TabProducao = ({ campanha, onAction, userEmail }) => {
  const [producaoStatus, setProducaoStatus] = React.useState(null);
  const [loadingStatus, setLoadingStatus]   = React.useState(true);
  const intervalRef = React.useRef(null);

  const status = campanha?.status;
  const isInProducao = ['em_producao', 'publicado'].includes(status);
  const approvals = campanha?.camp_approvals || [];
  const approveRecord   = approvals.find(a => a.phase === 'producao' && a.action === 'approved');
  const submitRecord    = approvals.find(a => a.phase === 'prompts'  && a.action === 'approved');

  const allCopy    = campanha?.copy || [];
  const allPrompts = campanha?.prompts || [];
  const payload    = campanha?.production_payload ? (typeof campanha.production_payload === 'string' ? (() => { try { return JSON.parse(campanha.production_payload); } catch { return null; } })() : campanha.production_payload) : null;

  const orgCount  = allCopy.filter(c => (!c.copy_type || c.copy_type === 'organico') && (!c.lingua || c.lingua === 'pt')).length;
  const perfCount = allCopy.filter(c => c.copy_type === 'performance' && (!c.lingua || c.lingua === 'pt')).length;

  const fetchStatus = React.useCallback(() => {
    if (!campanha?.id || !isInProducao) return;
    fetch(`/api/marketing/campanhas/${campanha.id}/producao-status`)
      .then(r => r.ok ? r.json() : null)
      .then(data => { if (data) setProducaoStatus(data); })
      .catch(() => {})
      .finally(() => setLoadingStatus(false));
  }, [campanha?.id, isInProducao]);

  React.useEffect(() => {
    fetchStatus();
    if (status === 'em_producao') {
      intervalRef.current = setInterval(fetchStatus, 30000);
    }
    return () => { if (intervalRef.current) clearInterval(intervalRef.current); };
  }, [fetchStatus, status]);

  const dateStr = (iso) => iso ? new Date(iso).toLocaleDateString('pt-PT', { day: 'numeric', month: 'long', year: 'numeric', hour: '2-digit', minute: '2-digit' }) : null;
  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 1 hora' : h < 24 ? `há ${h}h` : `há ${d} dia${d > 1 ? 's' : ''}`; };

  const ProgressBar = ({ done, total, color = '#3859D0' }) => {
    const pct = total > 0 ? Math.round((done / total) * 100) : 0;
    return (
      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
        <div style={{ flex: 1, height: 6, borderRadius: 99, background: 'var(--bg-app,#f5f6f8)', overflow: 'hidden' }}>
          <div style={{ height: '100%', borderRadius: 99, background: pct === 0 ? '#e2e8f0' : color, width: `${Math.max(pct, pct > 0 ? 3 : 0)}%`, transition: 'width 0.3s' }} />
        </div>
        <span style={{ fontSize: 11, fontWeight: 700, color: pct > 0 ? color : '#94a3b8', fontFamily: 'monospace', flexShrink: 0, minWidth: 50, textAlign: 'right' }}>{done}/{total}</span>
      </div>
    );
  };

  if (!isInProducao) {
    return (
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '64px 0', gap: 16 }}>
        <div style={{ width: 56, height: 56, borderRadius: 14, background: 'rgba(34,197,94,.08)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#22c55e" strokeWidth="1.5" strokeLinecap="round"><path d="M20 7H4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="2"/></svg>
        </div>
        <div style={{ textAlign: 'center' }}>
          <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--text,#1d2e38)', fontFamily: 'var(--font-display,Montserrat,sans-serif)', marginBottom: 6 }}>Aguarda aprovação executiva</div>
          <div style={{ fontSize: 13, color: '#64748b', maxWidth: 380, lineHeight: 1.6 }}>A campanha precisa de ser aprovada no tab Aprovação por Fábio, Rui ou Armando antes de avançar para Produção.</div>
        </div>
      </div>
    );
  }

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

      {/* ── CARD 1: ESTADO DE APROVAÇÃO + PACOTE ── */}
      <div style={{ background: 'var(--bg-elev,#fff)', border: '1px solid rgba(21,128,61,.25)', borderRadius: 12, padding: '20px 24px' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 16 }}>
          <div style={{ width: 10, height: 10, borderRadius: '50%', background: status === 'publicado' ? '#3859D0' : '#22c55e', animation: status === 'em_producao' ? 'cpulse 2s infinite' : 'none' }} />
          <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--text,#1d2e38)', fontFamily: 'var(--font-display,Montserrat,sans-serif)' }}>
            {status === 'publicado' ? 'Campanha Publicada' : 'Em Produção'}
          </div>
          {approveRecord && <span style={{ fontSize: 12, color: '#64748b', marginLeft: 4 }}>{timeAgo(approveRecord.created_at)}</span>}
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10, marginBottom: 16 }}>
          {approveRecord && (
            <div style={{ padding: '10px 14px', background: 'rgba(21,128,61,.04)', borderRadius: 8 }}>
              <div style={{ fontSize: 9, fontWeight: 700, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', marginBottom: 3 }}>Aprovado por</div>
              <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--text,#1d2e38)' }}>{approveRecord.approver_name || approveRecord.approver_email}</div>
              <div style={{ fontSize: 11, color: '#64748b' }}>{dateStr(approveRecord.created_at)}</div>
            </div>
          )}
          {submitRecord && (
            <div style={{ padding: '10px 14px', background: 'var(--bg-app,#f5f6f8)', borderRadius: 8 }}>
              <div style={{ fontSize: 9, fontWeight: 700, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', marginBottom: 3 }}>Submetido por</div>
              <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--text,#1d2e38)' }}>{submitRecord.approver_name || submitRecord.approver_email}</div>
              <div style={{ fontSize: 11, color: '#64748b' }}>{dateStr(submitRecord.created_at)}</div>
            </div>
          )}
        </div>

        <div style={{ borderTop: '1px solid var(--border-light,#f1f5f9)', paddingTop: 14, marginBottom: 16 }}>
          <div style={{ fontSize: 10, fontWeight: 700, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', marginBottom: 10 }}>Pacote enviado para Produção de Conteúdos</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {orgCount > 0  && <div style={{ fontSize: 13, color: 'var(--text,#1d2e38)', fontFamily: 'var(--font-body,Inter,sans-serif)' }}>▸ <strong>{orgCount}</strong> peças orgânicas (PT {[...new Set(allCopy.filter(c => c.lingua && c.lingua !== 'pt').map(c => c.lingua.toUpperCase()))].join(' + ')})</div>}
            {perfCount > 0 && <div style={{ fontSize: 13, color: 'var(--text,#1d2e38)', fontFamily: 'var(--font-body,Inter,sans-serif)' }}>▸ <strong>{perfCount}</strong> anúncios performance</div>}
            {allPrompts.length > 0 && <div style={{ fontSize: 13, color: 'var(--text,#1d2e38)', fontFamily: 'var(--font-body,Inter,sans-serif)' }}>▸ <strong>{allPrompts.length}</strong> prompts visuais (Flux + Kling)</div>}
            {payload?.pecas && <div style={{ fontSize: 13, color: 'var(--text,#1d2e38)', fontFamily: 'var(--font-body,Inter,sans-serif)' }}>▸ <strong>{payload.pecas.length}</strong> peças no pacote final</div>}
            {(payload?.periodo?.inicio || payload?.periodo?.fim) && <div style={{ fontSize: 13, color: 'var(--text,#1d2e38)', fontFamily: 'var(--font-body,Inter,sans-serif)' }}>▸ Período: {payload?.periodo?.inicio} → {payload?.periodo?.fim}</div>}
          </div>
        </div>

        <button
          onClick={() => { if (window.mktNavToSub) window.mktNavToSub('conteudos'); }}
          style={{ background: '#3859D0', border: 'none', borderRadius: 8, padding: '10px 18px', fontSize: 13, color: '#fff', cursor: 'pointer', fontFamily: 'var(--font-body,Inter,sans-serif)', fontWeight: 600 }}>
          → Acompanhar no Módulo Produção de Conteúdos
        </button>
      </div>

      {/* ── CARD 2: ESTADO DE EXECUÇÃO ── */}
      <div style={{ background: 'var(--bg-elev,#fff)', border: '1px solid var(--border,#e2e8f0)', borderRadius: 12, padding: '20px 24px' }}>
        <div style={{ fontSize: 10, fontWeight: 700, color: '#94a3b8', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.07em', marginBottom: 16 }}>Estado de Execução</div>

        {producaoStatus ? (
          <>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 16 }}>
              {[
                { label: 'Imagens geradas',  done: producaoStatus.imagens_geradas,    total: producaoStatus.imagens_total,      color: '#3859D0' },
                { label: 'Vídeos gerados',   done: producaoStatus.videos_gerados,     total: producaoStatus.videos_total,       color: '#ec4899' },
                { label: 'Composições',      done: producaoStatus.composicoes_prontas, total: producaoStatus.composicoes_total,  color: '#8b5cf6' },
                { label: 'Publicações',      done: producaoStatus.publicacoes_feitas,  total: producaoStatus.publicacoes_total,  color: '#059669' },
              ].map(({ label, done, total, color }) => (
                <div key={label} style={{ display: 'grid', gridTemplateColumns: '140px 1fr', gap: 10, alignItems: 'center' }}>
                  <div style={{ fontSize: 12, color: '#64748b', fontFamily: 'var(--font-body,Inter,sans-serif)' }}>{label}</div>
                  <ProgressBar done={done} total={total} color={color} />
                </div>
              ))}
            </div>
            {producaoStatus.publicacoes_feitas === 0 && (
              <div style={{ background: 'rgba(245,158,11,.06)', border: '1px solid rgba(245,158,11,.2)', borderRadius: 8, padding: '12px 14px', fontSize: 12, color: '#92400e', lineHeight: 1.55 }}>
                Produção agendada — a iniciar em breve. O Módulo Produção de Conteúdos vai gerar os assets visuais. Esta página actualiza automaticamente a cada 30 segundos.
              </div>
            )}
            {producaoStatus.ultima_publicacao && (
              <div style={{ fontSize: 12, color: '#64748b', marginTop: 10 }}>Última publicação: {dateStr(producaoStatus.ultima_publicacao)}</div>
            )}
          </>
        ) : loadingStatus ? (
          <div style={{ fontSize: 13, color: '#94a3b8', fontStyle: 'italic' }}>A carregar estado…</div>
        ) : (
          <div style={{ background: 'rgba(245,158,11,.06)', border: '1px solid rgba(245,158,11,.2)', borderRadius: 8, padding: '12px 14px', fontSize: 12, color: '#92400e' }}>
            Produção agendada — a iniciar em breve.
          </div>
        )}
      </div>

    </div>
  );
};

const NegativePromptBlock = ({ text }) => {
  const [open, setOpen] = React.useState(false);
  if (!text) return null;
  return (
    <div style={{ borderTop: '1px solid var(--border,#1e293b)', paddingTop: 8, marginTop: 2 }}>
      <button onClick={() => setOpen(o => !o)} style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 6, color: '#ef4444', fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', fontFamily: 'var(--font-mono,monospace)' }}>
        ⛔ Negative Prompt {open ? '▲' : '▼'}
      </button>
      {open && <div style={{ marginTop: 8, fontSize: 11, color: '#ef4444', lineHeight: 1.5, fontFamily: 'var(--font-mono,monospace)', background: 'rgba(239,68,68,.05)', borderRadius: 4, padding: '6px 8px' }}>{text}</div>}
    </div>
  );
};

const VariantPromptsBlock = ({ variants }) => {
  const [open, setOpen] = React.useState(false);
  if (!variants || !variants.length) return null;
  return (
    <div style={{ borderTop: '1px solid var(--border,#1e293b)', paddingTop: 8, marginTop: 2 }}>
      <button onClick={() => setOpen(o => !o)} style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 6, color: '#f59e0b', fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', fontFamily: 'var(--font-mono,monospace)' }}>
        🔀 Variantes ({variants.length}) {open ? '▲' : '▼'}
      </button>
      {open && (
        <div style={{ marginTop: 8, display: 'flex', flexDirection: 'column', gap: 8 }}>
          {variants.map((v, i) => (
            <div key={i} style={{ fontSize: 11, color: 'var(--text-muted,#64748b)', lineHeight: 1.55, borderLeft: '2px solid #f59e0b', paddingLeft: 10 }}>
              <span style={{ fontSize: 10, fontWeight: 700, color: '#f59e0b', fontFamily: 'var(--font-mono,monospace)', marginRight: 6 }}>V{i+1}</span>{v}
            </div>
          ))}
        </div>
      )}
    </div>
  );
};

const PromptCardFull = ({ pr, onApprove, onFlag }) => {
  const isApproved = pr.status === 'aprovado';
  const isFlagged  = pr.status === 'correcao';
  const cfg        = TIPO_CONFIG[pr.tipo] || TIPO_CONFIG.imagem;
  const [copied, setCopied] = React.useState(false);
  const variants = pr.variant_prompts ? (typeof pr.variant_prompts === 'string' ? JSON.parse(pr.variant_prompts) : pr.variant_prompts) : [];

  const handleCopy = () => {
    navigator.clipboard?.writeText(pr.prompt_texto || '').then(() => {
      setCopied(true); setTimeout(() => setCopied(false), 1800);
    });
  };

  // composicao: pretty-print JSON spec
  let composicaoSpec = null;
  if (pr.tipo === 'composicao' && pr.prompt_texto) {
    try { composicaoSpec = JSON.parse(pr.prompt_texto); } catch { composicaoSpec = null; }
  }

  return (
    <div style={{
      background: '#fff', display: 'flex', flexDirection: 'column',
      borderLeft: `3px solid ${isApproved ? '#22c55e' : isFlagged ? '#fb923c' : cfg.color}`,
    }}>
      {/* Imagem de referência (thumbnail) — só background e video */}
      {pr.imagem_referencia && pr.tipo !== 'composicao' && (
        <div style={{ position: 'relative', height: 100, overflow: 'hidden', background: '#f8fafc' }}>
          <img src={pr.imagem_referencia} alt="ref" style={{ width: '100%', height: '100%', objectFit: 'contain', padding: '8px' }} />
          <span style={{ position: 'absolute', bottom: 6, right: 8, fontSize: 9, fontWeight: 700, color: '#fff', background: 'rgba(0,0,0,.5)', padding: '2px 6px', borderRadius: 4, fontFamily: 'monospace' }}>REF</span>
        </div>
      )}

      {/* Card header */}
      <div style={{ padding: '11px 16px 9px', borderBottom: '1px solid #f1f5f9', background: isApproved ? 'rgba(34,197,94,.04)' : cfg.bg, display: 'flex', alignItems: 'center', gap: 8 }}>
        <span style={{ fontSize: 11, fontWeight: 700, color: cfg.color, fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.06em' }}>{cfg.label}</span>
        <span style={{ fontSize: 10, color: '#94a3b8', background: '#f1f5f9', padding: '1px 7px', borderRadius: 99, fontFamily: 'monospace', flexShrink: 0 }}>{pr.plataforma || cfg.plat}</span>
        {pr.ratio && <span style={{ fontSize: 10, color: '#94a3b8', fontFamily: 'monospace' }}>{pr.ratio}</span>}
        <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 6 }}>
          {isApproved
            ? <span style={{ fontSize: 10, color: '#16a34a', fontWeight: 600, fontFamily: 'monospace' }}>✓ Aprovado</span>
            : isFlagged
            ? <span style={{ fontSize: 10, color: '#ea580c', fontWeight: 600, fontFamily: 'monospace' }}>⚑ Correcção</span>
            : <span style={{ fontSize: 10, color: '#94a3b8', fontFamily: 'monospace' }}>Pendente</span>}
        </div>
      </div>

      {/* Card body */}
      <div style={{ padding: '12px 16px', display: 'flex', flexDirection: 'column', gap: 10, flex: 1 }}>
        {composicaoSpec ? (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {composicaoSpec.copy_overlay && (
              <div style={{ background: 'rgba(56,89,208,.06)', border: '1px solid rgba(56,89,208,.2)', borderRadius: 8, padding: '10px 12px' }}>
                <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: '#3859D0', fontFamily: 'monospace', marginBottom: 6 }}>Texto sobre a imagem</div>
                <div style={{ fontSize: 15, fontWeight: 700, color: '#1d2e38', fontFamily: 'Montserrat, sans-serif', lineHeight: 1.3 }}>{composicaoSpec.copy_overlay}</div>
                {composicaoSpec.cta_overlay && <div style={{ fontSize: 12, color: '#3859D0', fontWeight: 600, marginTop: 4 }}>{composicaoSpec.cta_overlay} →</div>}
              </div>
            )}
            <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#94a3b8', fontFamily: 'monospace' }}>Layout</div>
            {Object.entries(composicaoSpec).filter(([k]) => !['copy_overlay','cta_overlay'].includes(k)).map(([k, v]) => (
              <div key={k} style={{ display: 'flex', gap: 8 }}>
                <span style={{ fontSize: 10, color: '#94a3b8', fontFamily: 'monospace', minWidth: 120, flexShrink: 0 }}>{k}</span>
                <span style={{ fontSize: 11, color: '#1d2e38' }}>{String(v)}</span>
              </div>
            ))}
          </div>
        ) : (
          pr.prompt_texto && (
            <div>
              <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#94a3b8', fontFamily: 'monospace', marginBottom: 4 }}>Prompt</div>
              <div style={{ fontSize: 12, color: '#1d2e38', lineHeight: 1.65, fontFamily: 'Inter, sans-serif' }}>{pr.prompt_texto}</div>
            </div>
          )
        )}

        {pr.negative_prompt && (
          <details style={{ marginTop: 2 }}>
            <summary style={{ cursor: 'pointer', fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#ef4444', fontFamily: 'monospace', listStyle: 'none' }}>⛔ Negative prompt</summary>
            <div style={{ marginTop: 6, fontSize: 11, color: '#ef4444', lineHeight: 1.5, fontFamily: 'monospace', background: 'rgba(239,68,68,.04)', borderRadius: 4, padding: '6px 8px' }}>{pr.negative_prompt}</div>
          </details>
        )}

        {variants.length > 0 && (
          <details style={{ marginTop: 2 }}>
            <summary style={{ cursor: 'pointer', fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#f59e0b', fontFamily: 'monospace', listStyle: 'none' }}>🔀 {variants.length} variantes</summary>
            <div style={{ marginTop: 8, display: 'flex', flexDirection: 'column', gap: 8 }}>
              {variants.map((v, i) => (
                <div key={i} style={{ fontSize: 11, color: '#475569', lineHeight: 1.55, borderLeft: '2px solid #f59e0b', paddingLeft: 10 }}>
                  <span style={{ fontSize: 10, fontWeight: 700, color: '#f59e0b', fontFamily: 'monospace', marginRight: 6 }}>V{i+1}</span>{v}
                </div>
              ))}
            </div>
          </details>
        )}

        {pr.referencia && (
          <div style={{ fontSize: 10, color: '#94a3b8', fontStyle: 'italic', borderTop: '1px solid #f1f5f9', paddingTop: 8 }}>{pr.referencia}</div>
        )}

        {/* Actions */}
        <div style={{ display: 'flex', gap: 6, marginTop: 4, paddingTop: 10, borderTop: '1px solid #f1f5f9' }}>
          {!isApproved && (
            <>
              <button onClick={onApprove} className="btn btn-ai" style={{ fontSize: 11 }}>✓ Aprovar</button>
              <button onClick={onFlag}    className="btn" style={{ fontSize: 11, color: '#fb923c', borderColor: 'rgba(251,146,60,.3)' }}>⚑ Corrigir</button>
            </>
          )}
          {pr.tipo !== 'composicao' && (
            <button onClick={handleCopy} className="btn" style={{ fontSize: 11, marginLeft: 'auto', color: copied ? '#16a34a' : '#64748b' }}>
              {copied ? '✓ Copiado' : '⎘ Copiar'}
            </button>
          )}
        </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          || '');
  const [narProposta, setNarProposta] = React.useState(pj.narrativa_proposta   || '');

  // ── 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) }));

  // ── Ad Copy ──
  const [adCopy, setAdCopy] = React.useState(pj.ad_copy || {});
  const updAdCopy = (platform, k, v) => setAdCopy(c => ({ ...c, [platform]: { ...(c[platform]||{}), [k]: v } }));

  // ── Campanha Anúncios ──
  const [campAnuncios, setCampAnuncios] = React.useState(pj.campanha_anuncios || {});
  const updCA = (ch, path, v) => {
    setCampAnuncios(ca => {
      const updated = JSON.parse(JSON.stringify(ca));
      if (!updated[ch]) updated[ch] = {};
      const parts = path.split('.');
      let obj = updated[ch];
      for (let i = 0; i < parts.length - 1; i++) { if (!obj[parts[i]]) obj[parts[i]] = {}; obj = obj[parts[i]]; }
      obj[parts[parts.length - 1]] = v;
      return updated;
    });
  };
  const updCAAdSet = (ch, i, k, v) => setCampAnuncios(ca => ({ ...ca, [ch]: { ...ca[ch], ad_sets: (ca[ch]?.ad_sets||[]).map((s, j) => j === i ? { ...s, [k]: v } : s) } }));
  const updCACrHook = (ch, i, k, v) => setCampAnuncios(ca => ({ ...ca, [ch]: { ...ca[ch], criativo_principal: { ...(ca[ch]?.criativo_principal||{}), hooks: (ca[ch]?.criativo_principal?.hooks||[]).map((h, j) => j === i ? { ...h, [k]: v } : h) } } }));

  // ── Comm Plan ──
  const [commPlan, setCommPlan] = React.useState(
    (pj.comm_plan || []).map(c => ({ canal: c.canal||'', content_type: c.content_type||'', titulo: c.titulo||'', hook: c.hook||c.body||'', planned_week: c.planned_week||'', planned_date: c.planned_date||'' }))
  );
  const updCP = (i, k, v) => setCommPlan(ps => ps.map((p, j) => j === i ? { ...p, [k]: v } : p));

  // ── Image / Video Prompts ──
  const [imgPrompts, setImgPrompts] = React.useState(
    (pj.image_prompts || []).map(p => ({ for: p.for||'', prompt: p.prompt||'', model: p.model||'' }))
  );
  const [vidPrompts, setVidPrompts] = React.useState(
    (pj.video_prompts || []).map(p => ({ for: p.for||'', prompt: p.prompt||'', concept: p.concept||'', hook_visual: p.hook_visual||'' }))
  );

  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 };
        });
      });

      const payload = {
        action: 'editar',
        big_idea: bigIdea, posicionamento: posic, narrativa, tom_campanha: tom,
        key_message: keyMsg, anchor_type: anchor, narrativa_proposta: narProposta,
        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,
        ad_copy: adCopy,
        campanha_anuncios: campAnuncios,
        comm_plan: commPlan.map((c, i) => ({ ...(pj.comm_plan?.[i]||{}), ...c })),
        image_prompts: imgPrompts.map((p, i) => ({ ...(pj.image_prompts?.[i]||{}), ...p })),
        video_prompts: vidPrompts.map((p, i) => ({ ...(pj.video_prompts?.[i]||{}), ...p })),
      };
      await CampAPI.patchConceito(campanha.id, payload);
      onSave(); onClose();
    } catch { setSaving(false); }
  };

  const hookChannels = Object.keys(hooks);
  const caChannels   = Object.keys(campAnuncios);
  const adChannels   = Object.keys(adCopy);

  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} />

          {/* ── 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>
          ))}

          {/* ── Ad Copy ── */}
          {adChannels.length > 0 && (<>
            <_EcmSection title="Ad Copy" />
            {adChannels.map(pl => {
              const c = adCopy[pl] || {};
              return (
                <div key={pl}>
                  <div style={{ fontSize: 11, fontWeight: 700, color: '#475569', fontFamily: 'monospace', marginBottom: 6, textTransform: 'uppercase' }}>{pl}</div>
                  <_EcmCard>
                    {pl === 'meta' && (<>
                      <_EcmLabel label="Headline" /><_EcmIn value={c.headline} onChange={v => updAdCopy(pl,'headline',v)} />
                      <_EcmLabel label="Primary Text" /><_EcmTa value={c.primary_text} onChange={v => updAdCopy(pl,'primary_text',v)} rows={3} />
                      <_EcmLabel label="Description" /><_EcmIn value={c.description} onChange={v => updAdCopy(pl,'description',v)} />
                    </>)}
                    {pl === 'linkedin' && (<>
                      <_EcmLabel label="Headline" /><_EcmIn value={c.headline} onChange={v => updAdCopy(pl,'headline',v)} />
                      <_EcmLabel label="Intro Text" /><_EcmTa value={c.intro_text} onChange={v => updAdCopy(pl,'intro_text',v)} rows={3} />
                    </>)}
                    {pl === 'google' && (<>
                      <_EcmLabel label="Headlines" hint="Uma por linha" />
                      <_EcmTa value={(c.headlines||[]).join('\n')} onChange={v => updAdCopy(pl,'headlines',v.split('\n'))} rows={4} />
                      <_EcmLabel label="Descriptions" hint="Uma por linha" />
                      <_EcmTa value={(c.descriptions||[]).join('\n')} onChange={v => updAdCopy(pl,'descriptions',v.split('\n'))} rows={3} />
                    </>)}
                    {!['meta','linkedin','google'].includes(pl) && (<>
                      <_EcmLabel label="Conteúdo" /><_EcmTa value={typeof c === 'string' ? c : JSON.stringify(c,null,2)} onChange={v => setAdCopy(ac => ({ ...ac, [pl]: v }))} rows={4} />
                    </>)}
                  </_EcmCard>
                </div>
              );
            })}
          </>)}

          {/* ── Campanha Anúncios ── */}
          {caChannels.length > 0 && (<>
            <_EcmSection title="Campanha Anúncios (Performance)" />
            {caChannels.map(ch => {
              const cfg = campAnuncios[ch] || {};
              const cp  = cfg.criativo_principal || {};
              const ads = cfg.ad_sets || [];
              return (
                <div key={ch}>
                  <div style={{ fontSize: 11, fontWeight: 700, color: '#475569', fontFamily: 'monospace', marginBottom: 6, textTransform: 'uppercase' }}>{ch}</div>
                  <_EcmCard>
                    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
                      <div><_EcmLabel label="Objectivo" /><_EcmIn value={cfg.campanha_objectivo} onChange={v => updCA(ch,'campanha_objectivo',v)} /></div>
                      <div><_EcmLabel label="Conversão" hint="formulario_nativo · landing_page" /><_EcmIn value={cfg.meta_ads_conversao} onChange={v => updCA(ch,'meta_ads_conversao',v)} /></div>
                    </div>
                    <_EcmLabel label="Mensagem Central" /><_EcmTa value={cfg.campanha_mensagem_central} onChange={v => updCA(ch,'campanha_mensagem_central',v)} rows={2} />

                    {/* Criativo Principal */}
                    <div style={{ fontSize: 10, fontWeight: 700, color: '#3859D0', fontFamily: 'monospace', marginTop: 4 }}>CRIATIVO ÚNICO</div>
                    <_EcmLabel label="Headline" /><_EcmIn value={cp.headline} onChange={v => updCA(ch,'criativo_principal.headline',v)} />
                    <_EcmLabel label="Body" /><_EcmTa value={cp.body} onChange={v => updCA(ch,'criativo_principal.body',v)} rows={3} />
                    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
                      <div><_EcmLabel label="CTA" /><_EcmIn value={cp.cta} onChange={v => updCA(ch,'criativo_principal.cta',v)} /></div>
                      <div><_EcmLabel label="Visual Direction" /><_EcmIn value={cp.visual_direction} onChange={v => updCA(ch,'criativo_principal.visual_direction',v)} /></div>
                    </div>

                    {/* Hooks do criativo */}
                    {(cp.hooks||[]).length > 0 && (<>
                      <div style={{ fontSize: 10, fontWeight: 700, color: '#3859D0', fontFamily: 'monospace', marginTop: 4 }}>HOOKS DO CRIATIVO</div>
                      {(cp.hooks||[]).map((h, i) => (
                        <div key={i} style={{ display: 'grid', gridTemplateColumns: '1fr auto auto', gap: 8, alignItems: 'start' }}>
                          <_EcmTa value={h.texto||h} onChange={v => updCACrHook(ch, i, 'texto', v)} rows={2} />
                          <_EcmIn value={h.tipo||''} onChange={v => updCACrHook(ch, i, 'tipo', v)} placeholder="tipo" />
                          <_EcmIn value={String(h.prioridade||i+1)} onChange={v => updCACrHook(ch, i, 'prioridade', Number(v))} placeholder="P" />
                        </div>
                      ))}
                    </>)}

                    {/* Ad Sets */}
                    {ads.length > 0 && (<>
                      <div style={{ fontSize: 10, fontWeight: 700, color: '#3859D0', fontFamily: 'monospace', marginTop: 4 }}>PÚBLICOS-ALVO ({ads.length})</div>
                      {ads.map((s, i) => (
                        <div key={i} style={{ padding: '8px 10px', background: '#f1f5f9', borderRadius: 6, display: 'flex', flexDirection: 'column', gap: 6 }}>
                          <div style={{ display: 'grid', gridTemplateColumns: '1fr auto auto', gap: 8, alignItems: 'center' }}>
                            <_EcmIn value={s.nome} onChange={v => updCAAdSet(ch, i, 'nome', v)} placeholder="Nome do ad set" />
                            <_EcmIn value={s.tipo} onChange={v => updCAAdSet(ch, i, 'tipo', v)} placeholder="awareness" />
                            <_EcmIn value={String(s.awareness_stage||'')} onChange={v => updCAAdSet(ch, i, 'awareness_stage', Number(v))} placeholder="S" />
                          </div>
                          <_EcmTa value={s.targeting_resumo} onChange={v => updCAAdSet(ch, i, 'targeting_resumo', v)} rows={2} placeholder="Targeting resumo" />
                          <_EcmTa value={s.targeting_rationale} onChange={v => updCAAdSet(ch, i, 'targeting_rationale', v)} rows={1} placeholder="Targeting rationale" />
                        </div>
                      ))}
                    </>)}

                    <_EcmLabel label="Reutilização Orgânica" /><_EcmTa value={cfg.reutilizacao_organica} onChange={v => updCA(ch,'reutilizacao_organica',v)} rows={2} />
                  </_EcmCard>
                </div>
              );
            })}
          </>)}

          {/* ── Plano Editorial ── */}
          <_EcmSection title="Plano Editorial" />
          {commPlan.map((c, i) => (
            <_EcmCard key={i} extra={<_EcmRemBtn onClick={() => setCommPlan(ps => ps.filter((_, j) => j !== i))} />}>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 80px 60px', gap: 8 }}>
                <div><_EcmLabel label="Canal" /><_EcmIn value={c.canal} onChange={v => updCP(i,'canal',v)} /></div>
                <div><_EcmLabel label="Tipo" /><_EcmIn value={c.content_type} onChange={v => updCP(i,'content_type',v)} /></div>
                <div><_EcmLabel label="Semana" /><_EcmIn value={String(c.planned_week||'')} onChange={v => updCP(i,'planned_week',v)} placeholder="1" /></div>
                <div><_EcmLabel label="Data" /><_EcmIn value={c.planned_date||''} onChange={v => updCP(i,'planned_date',v)} placeholder="YYYY-MM-DD" /></div>
              </div>
              <_EcmLabel label="Título" /><_EcmIn value={c.titulo} onChange={v => updCP(i,'titulo',v)} />
              <_EcmLabel label="Hook / Body" /><_EcmTa value={c.hook} onChange={v => updCP(i,'hook',v)} rows={2} />
            </_EcmCard>
          ))}
          <_EcmAddBtn label="Adicionar item" onClick={() => setCommPlan(ps => [...ps, { canal:'', content_type:'', titulo:'', hook:'', planned_week:'', planned_date:'' }])} />

          {/* ── Image Prompts ── */}
          <_EcmSection title="Image Prompts (FLUX)" />
          {imgPrompts.map((p, i) => (
            <_EcmCard key={i} extra={<_EcmRemBtn onClick={() => setImgPrompts(ps => ps.filter((_, j) => j !== i))} />}>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
                <div><_EcmLabel label="Para" /><_EcmIn value={p.for} onChange={v => setImgPrompts(ps => ps.map((x, j) => j === i ? { ...x, for: v } : x))} /></div>
                <div><_EcmLabel label="Modelo" /><_EcmIn value={p.model} onChange={v => setImgPrompts(ps => ps.map((x, j) => j === i ? { ...x, model: v } : x))} /></div>
              </div>
              <_EcmLabel label="Prompt" /><_EcmTa value={p.prompt} onChange={v => setImgPrompts(ps => ps.map((x, j) => j === i ? { ...x, prompt: v } : x))} rows={4} />
            </_EcmCard>
          ))}
          <_EcmAddBtn label="Adicionar prompt de imagem" onClick={() => setImgPrompts(ps => [...ps, { for:'', prompt:'', model:'flux-1.1-pro' }])} />

          {/* ── Video Prompts ── */}
          <_EcmSection title="Video Prompts (Kling)" />
          {vidPrompts.map((p, i) => (
            <_EcmCard key={i} extra={<_EcmRemBtn onClick={() => setVidPrompts(ps => ps.filter((_, j) => j !== i))} />}>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
                <div><_EcmLabel label="Para" /><_EcmIn value={p.for} onChange={v => setVidPrompts(ps => ps.map((x, j) => j === i ? { ...x, for: v } : x))} /></div>
                <div><_EcmLabel label="Modelo" /><_EcmIn value={p.model} onChange={v => setVidPrompts(ps => ps.map((x, j) => j === i ? { ...x, model: v } : x))} /></div>
              </div>
              <_EcmLabel label="Prompt" /><_EcmTa value={p.prompt} onChange={v => setVidPrompts(ps => ps.map((x, j) => j === i ? { ...x, prompt: v } : x))} rows={3} />
              <_EcmLabel label="Conceito Visual" /><_EcmTa value={p.concept} onChange={v => setVidPrompts(ps => ps.map((x, j) => j === i ? { ...x, concept: v } : x))} rows={2} />
              <_EcmLabel label="Hook Visual (3s)" /><_EcmTa value={p.hook_visual} onChange={v => setVidPrompts(ps => ps.map((x, j) => j === i ? { ...x, hook_visual: v } : x))} rows={2} />
            </_EcmCard>
          ))}
          <_EcmAddBtn label="Adicionar prompt de vídeo" onClick={() => setVidPrompts(ps => [...ps, { for:'', prompt:'', concept:'', hook_visual:'', model:'kling' }])} />

          {/* ── Narrativa Interna ── */}
          <_EcmSection title="Narrativa Interna" />
          <_EcmLabel label="Narrativa da Proposta" hint="Para revisão RL/FC — 4 parágrafos" />
          <_EcmTa value={narProposta} onChange={setNarProposta} rows={8} />
        </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 [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 titulo = selected.commercial_name || selected.product_name || `Campanha #${selected.id}`;
      const camp = await CampAPI.create({ briefing_id: selected.id, titulo });
      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>
          )}
          {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) ──────────────────────────────────────────────────
// ── TabPerformanceFull ─────────────────────────────────────────────────────────
const TabPerformanceFull = ({ campanhaId }) => {
  const [data, setData] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState('');

  const load = React.useCallback(() => {
    if (!campanhaId) return;
    setLoading(true);
    campApiCall(`/api/marketing/campanhas/${campanhaId}/performance`)
      .then(d => { setData(d); setLoading(false); })
      .catch(e => { setError(e.message); setLoading(false); });
  }, [campanhaId]);

  React.useEffect(() => { load(); }, [load]);

  if (loading) return <div style={{ padding: 40, textAlign: 'center', fontSize: 13, color: 'var(--text-muted)' }}>A carregar métricas...</div>;
  if (error)   return <div style={{ padding: 24, fontSize: 13, color: '#dc2626' }}>{error}</div>;
  if (!data || !data.metrics?.length) return (
    <div style={{ padding: 40, textAlign: 'center', fontSize: 13, color: 'var(--text-muted)', lineHeight: 1.7, maxWidth: 400, margin: '0 auto' }}>
      <div style={{ fontWeight: 600, color: 'var(--text)', marginBottom: 8 }}>Sem métricas disponíveis</div>
      <div>{data?.message || 'Quando a campanha for publicada e ligada a um external_id (ad_id), as métricas aparecerão aqui.'}</div>
    </div>
  );

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
      <div style={{ fontSize: 11, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', letterSpacing: '0.06em', marginBottom: 4, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <span>PERFORMANCE · {data.metrics.length} {data.metrics.length === 1 ? 'publicação' : 'publicações'}</span>
        <button onClick={load} className="btn" style={{ height: 24, padding: '0 10px', fontSize: 11 }}>Actualizar</button>
      </div>
      {data.metrics.map((m, i) => (
        <div key={i} style={{ background: '#fff', borderRadius: 10, border: '1px solid var(--border)', padding: '16px 20px' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
            <CanalSvgIcon canal={m.canal} size={16} />
            <span style={{ fontSize: 13, fontWeight: 700, color: 'var(--text)', fontFamily: 'var(--font-display)' }}>
              {CANAL_LABEL[m.canal] || m.canal}
            </span>
            {m.external_id && <span style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)' }}>#{m.external_id}</span>}
            {m.publicado_em && <span style={{ fontSize: 10, color: 'var(--text-dim)' }}>{new Date(m.publicado_em).toLocaleDateString('pt-PT', { day: '2-digit', month: 'short' })}</span>}
          </div>
          {m.error ? (
            <div style={{ fontSize: 12, color: '#d97706', padding: '8px 12px', background: 'rgba(217,119,6,.06)', borderRadius: 6, border: '1px solid rgba(217,119,6,.2)' }}>
              {m.error}
            </div>
          ) : (
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(100px, 1fr))', gap: 10 }}>
              {[
                { label: 'Impressões', val: m.impressions || m.reach || '—' },
                { label: 'Cliques',    val: m.clicks || '—' },
                { label: 'CTR',        val: m.ctr ? `${(m.ctr * 100).toFixed(2)}%` : '—' },
                { label: 'Leads',      val: m.leads || m.conversions || '—' },
                { label: 'CPL',        val: m.cpl ? `€${Number(m.cpl).toFixed(2)}` : '—' },
                { label: 'Gasto',      val: m.spend ? `€${Number(m.spend).toFixed(2)}` : '—' },
              ].map(kv => (
                <div key={kv.label} style={{ textAlign: 'center', padding: '8px 4px', background: 'var(--bg-sunken)', borderRadius: 6 }}>
                  <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--text)', fontFamily: 'var(--font-mono)' }}>{kv.val}</div>
                  <div style={{ fontSize: 10, color: 'var(--text-dim)', marginTop: 2 }}>{kv.label}</div>
                </div>
              ))}
            </div>
          )}
        </div>
      ))}
    </div>
  );
};

// ── CampanhaDetail ─────────────────────────────────────────────────────────────
// Vista de detalhe: fetch da campanha completa + tabs (Briefing/Conceito/Copy/…)
const CampanhaDetail = ({ campanhaSummary, onBack, onRefresh, initialTab = 'conceito', userEmail }) => {
  const [campanha,   setCampanha]   = React.useState(null);
  const [activeTab,  setActiveTab]  = React.useState(initialTab);
  const [generating, setGenerating] = React.useState({});
  const [genStep,    setGenStep]    = React.useState({ key: null, step: 0, message: '', total: 0, langs: [] });
  const [toast,      setToast]      = React.useState('');
  const [toastType,  setToastType]  = React.useState('success');

  const load = React.useCallback(async () => {
    try {
      const c = await CampAPI.get(campanhaSummary.id);
      setCampanha(c);
    } catch (e) {
      console.error('[CampanhaDetail load]', e);
    }
  }, [campanhaSummary.id]);

  React.useEffect(() => { load(); }, [load]);

  const showToast = (msg, type = 'success', ms = 3000) => { setToast(msg); setToastType(type); setTimeout(() => setToast(''), ms); };

  const userName = window.currentUser?.name || window.currentUser?.displayName || userEmail?.split('@')[0] || '';

  const handleAction = async (action, ...args) => {
    if (!campanha) return;
    const id = campanha.id;
    try {
      if (action === 'generateConceito') {
        setGenerating({ conceito: true });
        await CampAPI.generateConceito(id, (step, msg) => setGenStep({ key: 'conceito', step, message: msg, total: 0, langs: [] }));
        showToast('Conceito gerado');
      } else if (action === 'generateCommPlan') {
        setGenerating({ commPlan: true });
        await CampAPI.generateCommPlan(id, (step, msg) => setGenStep({ key: 'commPlan', step, message: msg, total: 0, langs: [] }));
        showToast('Plano de comunicação gerado');
      } else if (action === 'aprovarConceito') {
        setGenerating({ approve: true });
        await CampAPI.aprovarConceito(id, { approver_name: userName, approver_email: userEmail });
        showToast('Conceito aprovado');
      } else if (action === 'generateCopy') {
        setGenerating({ copy: true });
        await CampAPI.generateCopy(id, (step, msg) => setGenStep({ key: 'copy', step, message: msg, total: 0, langs: [] }));
        showToast('Copy gerado');
      } else if (action === 'approveCopy') {
        const [copyId, status] = args;
        await CampAPI.patchCopy(copyId, { status });
      } else if (action === 'aprovarCopy') {
        await CampAPI.approveCopyAll(id);
        showToast('Copy aprovado');
      } else if (action === 'saveCopy') {
        const [copyId, data] = args;
        await CampAPI.patchCopy(copyId, data);
        showToast('Copy guardado');
      } else if (action === 'regenerateCopy') {
        showToast('Regenerar individual ainda por implementar', 'info', 4000);
      } else if (action === 'generateIdiomas') {
        setGenerating({ idiomas: true });
        const [lang] = args;
        await CampAPI.generateIdiomas(id, lang ? [lang] : [], (step, msg, total, langs) => setGenStep({ key: 'idiomas', step, message: msg, total: total || 0, langs: langs || [] }));
        showToast('Idiomas gerados');
      } else if (action === 'regenerateIdioma') {
        const [copyId, lang] = args;
        setGenerating({ idiomas: true });
        await CampAPI.generateIdiomas(id, [lang], (step, msg) => setGenStep({ key: 'idiomas', step, message: msg, total: 0, langs: [] }));
        showToast('Tradução regenerada');
      } else if (action === 'aprovarIdiomas') {
        await CampAPI.patchIdiomas(id, { action: 'aprovar', approver_name: userName, approver_email: userEmail });
        showToast('Idiomas aprovados');
      } else if (action === 'generatePrompts') {
        setGenerating({ prompts: true });
        await CampAPI.generatePrompts(id);
        showToast('Prompts gerados');
      } else if (action === 'regeneratePrompt') {
        showToast('Regenerar prompt individual ainda por implementar', 'info', 4000);
      } else if (action === 'approvePrompt') {
        const [pId, status] = args;
        await CampAPI.patchPrompt(pId, { status });
      } else if (action === 'generateEstrategia') {
        setGenerating({ estrategia: true });
        await CampAPI.generateEstrategia(id, (step, msg) => setGenStep({ key: 'estrategia', step, message: msg, total: 0, langs: [] }));
        showToast('Estratégia gerada');
      } else if (action === 'aprovarEstrategia') {
        setGenerating({ approveEstrategia: true });
        await CampAPI.aprovarEstrategia(id, { email: userEmail, name: userEmail?.split('@')[0] });
        showToast('Estratégia aprovada');
      } else if (action === 'enviarProducao') {
        await CampAPI.enviarProducao(id, { approver_name: userName, approver_email: userEmail });
        showToast('Enviado para produção');
      } else if (action === 'refreshCampanha') {
        // Apenas força load() do parent sem side-effects
      } else if (action === 'aprovarOrcamento') {
        setGenerating({ approve: true });
        await campApiCall(`/api/marketing/campanhas/${id}/orcamento/approve`, {
          method: 'POST', body: JSON.stringify({ user_email: userEmail, user_name: userName })
        });
        showToast('Orçamento aprovado');
      } else if (action === 'generateOrcamento') {
        setGenerating({ orcamento: true });
        await campApiCall(`/api/marketing/campanhas/${id}/orcamento/generate`, { method: 'POST', body: JSON.stringify({ user_email: userEmail }) });
        showToast('Orçamento gerado');
      } else if (action === 'generateSegmentacao') {
        setGenerating({ segmentacao: true });
        await campApiCall(`/api/marketing/campanhas/${id}/segmentacao/generate`, { method: 'POST', body: JSON.stringify({ user_email: userEmail }) });
        showToast('Target gerado');
      } else if (action === 'generatePlaneamento') {
        setGenerating({ planeamento: true });
        await campApiCall(`/api/marketing/campanhas/${id}/planeamento/generate`, { method: 'POST', body: JSON.stringify({ user_email: userEmail }) });
        showToast('Planeamento gerado');
      } else if (action === 'enviarAprovacao') {
        setGenerating({ enviarAprovacao: true });
        await campApiCall(`/api/marketing/campanhas/${id}`, { method: 'PATCH', body: JSON.stringify({ status: 'pending_executive', actor_name: userName, actor_email: userEmail }) });
        showToast('Enviado para aprovação executiva — email enviado');
      } else if (action === 'rejeitarAprovacao') {
        const [notes] = args;
        await CampAPI.requestChanges(id, { action: 'request_changes', notes });
        showToast('Alterações solicitadas', 'info');
      } else if (action === 'pedirAlteracoes') {
        const [{ notes, phase }] = args;
        const revertMap = { briefing: 'rascunho', estrategia: 'estrategia_pendente', conceito: 'conceito_pendente', orcamento: 'orcamento_pendente', segmentacao: 'segmentacao_pendente', planeamento: 'planeamento_pendente', funil: 'funil_pendente' };
        const revertStatus = revertMap[phase] || 'funil_pendente';
        await campApiCall(`/api/marketing/campanhas/${id}`, { method: 'PATCH', body: JSON.stringify({ status: revertStatus, actor_name: userName, actor_email: userEmail }) });
        showToast('Alterações pedidas — campanha voltou a ' + phase, 'info');
      } else {
        console.warn('[handleAction] unknown action', action, args);
      }
      await load();
      if (onRefresh) onRefresh();
    } catch (e) {
      console.error('[handleAction]', action, e);
      showToast('Erro: ' + (e.message || 'operação falhou'), 'error', 5000);
    } finally {
      setGenerating({});
      setGenStep({ key: null, step: 0, message: '', total: 0, langs: [] });
    }
  };

  if (!campanha) {
    return (
      <div data-theme="light" style={{ height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#f8fafc' }}>
        <div style={{ fontSize: 13, color: '#64748b' }}>A carregar campanha…</div>
      </div>
    );
  }

  const bCol    = _brandColor(campanha.brand_slug);
  const copy    = campanha.copy    || [];
  const idiomas = campanha.idiomas || [];
  const prompts = campanha.prompts || [];

  const TABS = [
    { id: 'briefing',    label: 'Briefing' },
    { id: 'estrategia',  label: 'Estratégia' },
    { id: 'conceito',    label: 'Conceito' },
    { id: 'orcamento',   label: 'Orçamento' },
    { id: 'segmentacao', label: 'Target' },
    { id: 'planeamento', label: 'Planeamento' },
    { id: 'funil',       label: 'Funil Multicanal' },
    { id: 'aprovacao',   label: 'Aprovação' },
  ];

  const conceitoStep = genStep.key === 'conceito' ? genStep.step    : 0;
  const conceitoMsg  = genStep.key === 'conceito' ? genStep.message : '';
  const copyStep     = genStep.key === 'copy'     ? genStep.step    : 0;
  const copyMsg      = genStep.key === 'copy'     ? genStep.message : '';
  const idiomasStep  = genStep.key === 'idiomas'  ? genStep.step    : 0;
  const idiomasMsg   = genStep.key === 'idiomas'  ? genStep.message : '';
  const idiomasTotal = genStep.key === 'idiomas'  ? genStep.total   : 0;
  const idiomasLangs = genStep.key === 'idiomas'  ? genStep.langs   : [];

  // ── CTAs contextuais no topo direito, conforme activeTab + estado ──
  const status = campanha.status || '';
  const hasConceito   = !!(campanha.big_idea);
  const hasEstrategia = !!(campanha.estrategia_json && Object.keys(campanha.estrategia_json).length > 0);
  const estrategiaApproved = !!campanha.estrategia_approved_at;
  const hasCopy       = Array.isArray(copy)    && copy.length > 0;
  const hasIdiomas    = Array.isArray(idiomas) && idiomas.length > 0;
  const hasPrompts    = Array.isArray(prompts) && prompts.length > 0;

  // Fluxo novo: conceito → orçamento → segmentação → planeamento → funil → aprovação → produção
  // (Copy/Idiomas/Prompts continuam a existir por compat mas ficam entre planeamento e aprovação)
  const _POST_CONCEITO_NEW = ['orcamento_pendente','orcamento_gerado','orcamento_aprovado','segmentacao_pendente','segmentacao_gerada','segmentacao_aprovada','planeamento_pendente','planeamento_aprovado','funil_pendente','funil_revisto'];
  const _POST_COPY_IDIOMAS_PROMPTS = ['copy_pendente','copy_gerado','copy_aprovado','idiomas_pendente','idiomas_gerados','idiomas_aprovados','prompts_pendentes','prompts_gerados'];
  const _POST_APROVACAO = ['prompts_aprovado','pending_executive','em_aprovacao','em_producao','publicado','concluida'];

  const STATUS_POST_CONCEITO = [..._POST_CONCEITO_NEW, ..._POST_COPY_IDIOMAS_PROMPTS, ..._POST_APROVACAO];
  const STATUS_POST_COPY     = ['copy_aprovado','idiomas_pendente','idiomas_gerados','idiomas_aprovados','prompts_pendentes','prompts_gerados', ..._POST_APROVACAO];
  const STATUS_POST_IDIOMAS  = ['idiomas_aprovados','prompts_pendentes','prompts_gerados', ..._POST_APROVACAO];

  const headerCTAs = [];
  if (activeTab === 'estrategia') {
    headerCTAs.push({
      key: 'generateEstrategia',
      label: generating.estrategia ? 'A gerar…' : (hasEstrategia ? 'Regenerar Estratégia' : 'Gerar Estratégia'),
      primary: !hasEstrategia, disabled: !!generating.estrategia,
      onClick: () => handleAction('generateEstrategia'),
    });
    if (hasEstrategia && !estrategiaApproved) {
      headerCTAs.push({
        key: 'aprovarEstrategia', label: 'Aprovar Estratégia', primary: true,
        disabled: !!generating.approveEstrategia, onClick: () => handleAction('aprovarEstrategia'),
      });
    }
  } else if (activeTab === 'conceito') {
    headerCTAs.push({
      key: 'generateConceito',
      label: generating.conceito ? 'A gerar…' : (hasConceito ? 'Regenerar Conceito' : 'Gerar Conceito'),
      primary: !hasConceito, disabled: !!generating.conceito,
      onClick: () => handleAction('generateConceito'),
    });
    if (hasConceito) {
      const commPlanItems = campanha?.proposta_json?.comm_plan || [];
      headerCTAs.push({
        key: 'generateCommPlan',
        label: generating.commPlan ? 'A gerar plano…' : (commPlanItems.length > 3 ? 'Regenerar Plano' : 'Gerar Plano de Comunicação'),
        primary: commPlanItems.length <= 3, disabled: !!generating.commPlan,
        onClick: () => handleAction('generateCommPlan'),
      });
    }
    if (hasConceito && status !== 'conceito_aprovado' && !STATUS_POST_CONCEITO.includes(status)) {
      headerCTAs.push({
        key: 'aprovarConceito', label: 'Aprovar Conceito', primary: true,
        disabled: !!generating.approve, onClick: () => handleAction('aprovarConceito'),
      });
    }
  } else if (activeTab === 'orcamento') {
    headerCTAs.push({
      key: 'generateOrcamento',
      label: generating.orcamento ? 'A gerar…' : 'Gerar Orçamento',
      primary: true, disabled: !!generating.orcamento || !hasEstrategia,
      onClick: () => handleAction('generateOrcamento'),
    });
  } else if (activeTab === 'segmentacao') {
    // Botão Gerar vive dentro da tab (o toggle Ads|CRM decide qual endpoint chamar)
    // Header fica limpo — o "Abrir CRM" contextual já aparece ao lado
  } else if (activeTab === 'planeamento') {
    headerCTAs.push({
      key: 'generatePlaneamento',
      label: generating.planeamento ? 'A gerar…' : 'Gerar Planeamento',
      primary: true, disabled: !!generating.planeamento || !hasEstrategia,
      onClick: () => handleAction('generatePlaneamento'),
    });
  } else if (activeTab === 'funil') {
    if (['funil_pendente','planeamento_aprovado'].includes(status)) {
      headerCTAs.push({
        key: 'enviarAprovacao',
        label: '→ Enviar para Aprovação',
        primary: true, disabled: !!generating.enviarAprovacao,
        onClick: () => handleAction('enviarAprovacao'),
      });
    }
  } else if (activeTab === 'producao') {
    if (!['em_producao','concluida'].includes(status)) {
      headerCTAs.push({
        key: 'enviarProducao', label: generating.producao ? 'A enviar…' : '→ Enviar para Produção',
        primary: true, disabled: !!generating.producao,
        onClick: () => handleAction('enviarProducao'),
      });
    }
  }

  return (
    <div data-theme="light" style={{ height: '100%', display: 'flex', flexDirection: 'column', background: '#f8fafc' }}>
      {/* Header — padrão BriefingForm */}
      <div style={{ background: 'var(--bg-elev, #ffffff)', borderBottom: '1px solid var(--border, #ECEFF5)', padding: '20px 40px 0', flexShrink: 0 }}>

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

        {/* Title + actions */}
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16, marginBottom: 10 }}>
          <div style={{ minWidth: 0 }}>
            <h1 style={{ margin: 0, fontSize: 21, fontWeight: 700, color: 'var(--text)', fontFamily: 'var(--font-display)', letterSpacing: '-0.01em', lineHeight: 1.2 }}>
              {campanha?.titulo || '—'}
            </h1>
            {/* Meta row: status + brand pill */}
            <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 8, flexWrap: 'wrap' }}>
              {campanha && <CampStatusBadge status={campanha.status || 'conceito_pendente'} />}
              {(campanha?.brand_name || campanha?.briefing?.brand_name) && (() => {
                const bn = campanha.brand_name || campanha.briefing?.brand_name;
                return <span style={{ fontSize: 10, fontWeight: 700, padding: '2px 8px', borderRadius: 4, background: bCol + '15', color: bCol, fontFamily: 'var(--font-mono)', border: `1px solid ${bCol}25`, letterSpacing: '0.04em' }}>{bn.toUpperCase()}</span>;
              })()}
            </div>
          </div>

          {/* Actions row — apenas navegação entre tabs + Sair (CTAs contextuais vivem dentro de cada tab) */}
          <div style={{ display: 'flex', gap: 6, alignItems: 'center', flexShrink: 0, marginTop: 4 }}>
            {/* Prev / Next com label da fase */}
            {(() => {
              const tabIds = TABS.map(t => t.id);
              const curIdx = tabIds.indexOf(activeTab);
              const prevTab = curIdx > 0 ? TABS[curIdx - 1] : null;
              const nextTab = curIdx < tabIds.length - 1 ? TABS[curIdx + 1] : null;
              const navBtn = (dir, tab, onClick) => (
                <button
                  onClick={onClick}
                  disabled={!tab}
                  className="btn"
                  title={tab ? tab.label : ''}
                  style={{ height: 32, padding: '0 10px', fontSize: 11, fontWeight: 600,
                    color: tab ? 'var(--text-muted)' : 'var(--text-dim)',
                    opacity: tab ? 1 : 0.4, display: 'flex', alignItems: 'center', gap: 5, whiteSpace: 'nowrap' }}
                >
                  {dir === 'prev' && <span style={{ fontSize: 13 }}>←</span>}
                  <span style={{ maxWidth: 80, overflow: 'hidden', textOverflow: 'ellipsis' }}>{tab ? tab.label : '—'}</span>
                  {dir === 'next' && <span style={{ fontSize: 13 }}>→</span>}
                </button>
              );
              return (
                <>
                  {navBtn('prev', prevTab, () => prevTab && setActiveTab(prevTab.id))}
                  {navBtn('next', nextTab, () => nextTab && setActiveTab(nextTab.id))}
                </>
              );
            })()}
            {/* Separador antes do Sair */}
            <div style={{ width: 1, height: 20, background: 'var(--border)', flexShrink: 0, margin: '0 2px' }} />
            {/* Sair — ícone X */}
            <button
              onClick={onBack}
              className="btn"
              title="Fechar campanha e voltar à lista"
              style={{ height: 32, width: 32, padding: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--text-muted)' }}
            >
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
            </button>
          </div>
        </div>

        {/* Tabs bar — só as tabs, scrollable */}
        <div style={{ display: 'flex', gap: 2, alignItems: 'center', overflowX: 'auto' }}>
          {TABS.map(t => (
            <button key={t.id} onClick={() => setActiveTab(t.id)} style={{
              background: 'transparent',
              border: 'none', borderBottom: `2px solid ${activeTab === t.id ? bCol : 'transparent'}`,
              padding: '10px 14px', fontSize: 12.5, fontWeight: activeTab === t.id ? 700 : 500,
              color: activeTab === t.id ? 'var(--text)' : 'var(--text-muted)', cursor: 'pointer',
              fontFamily: 'inherit', whiteSpace: 'nowrap', transition: 'color 120ms, border-color 120ms',
              marginBottom: -1,
            }}>{t.label}</button>
          ))}
        </div>
      </div>
      {/* Content */}
      <div style={{ flex: 1, overflow: 'auto', padding: '20px 40px 40px' }}>
        {activeTab === 'briefing'    && <TabBriefingFull  campanha={campanha} />}
        {activeTab === 'estrategia'  && <TabEstrategiaFull key={`estrat-${campanha.estrategia_generated_at || 'none'}-${campanha.estrategia_approved_at || 'noapp'}`} campanha={campanha} userEmail={userEmail} generating={generating} genStep={genStep} onAction={handleAction} />}
        {activeTab === 'funil'       && <TabFunilMulticanal campanha={campanha} onAction={handleAction} />}
        {activeTab === 'orcamento'    && <TabOrcamento    campanha={campanha} userEmail={userEmail} onAction={handleAction} />}
        {activeTab === 'segmentacao'  && <TabTarget       campanha={campanha} userEmail={userEmail} onAction={handleAction} />}
        {activeTab === 'planeamento'  && <TabPlaneamento  campanha={campanha} copy={copy} prompts={prompts} userEmail={userEmail} />}
        {activeTab === 'conceito'    && <TabConceitoFull  campanha={campanha} onAction={handleAction} generating={generating} conceitoStep={conceitoStep} conceitoMsg={conceitoMsg} />}
        {activeTab === 'aprovacao'   && <TabAprovacao     campanha={campanha} onAction={handleAction} userEmail={userEmail} />}
      </div>
      <MktCampToast msg={toast} type={toastType} />
    </div>
  );
};

const MktCampanhasScreen = ({ onOpenChat, userEmail }) => {
  const [campanhas,     setCampanhas]     = React.useState(null);
  const [allBrands,     setAllBrands]     = React.useState([]);
  const [filterBrand,    setFilterBrand]    = React.useState('all');
  const [filterCol,      setFilterCol]      = React.useState('all');
  const [filterProduto,  setFilterProduto]  = React.useState('all');
  const [filterCriador,  setFilterCriador]  = React.useState('all');
  const [filterMecanica, setFilterMecanica] = React.useState('all');

  const MECANICA_LABEL = { digirent:'Digirent', printplan:'PrintPlan', voucher:'Voucher on Demand', direct_discount:'Desc. Directo', trade_in:'Trade-In' };
  const MECANICA_COLOR = { digirent:'#7C3AED', printplan:'#D97706', voucher:'#0EA5E9', direct_discount:'#DC2626', trade_in:'#059669' };
  const today = new Date();
  const [dateRange,     setDateRange]     = React.useState({ preset: 'all', start: new Date(today.getFullYear(), 0, 1), end: today });
  const [search,        setSearch]        = React.useState('');
  const [selected,      setSelected]      = React.useState(null);
  const [editing,       setEditing]       = React.useState(null);
  const [showModal,     setShowModal]     = React.useState(false);
  const [toast,         setToast]         = React.useState('');
  const [initialTab,    setInitialTab]    = React.useState('conceito');

  const BRAND_ORDER = ['mimaki', 'biond', 'decal', 'alldecor', 'sensek', 'netscreen', 'digidelta'];

  const load = React.useCallback(async () => {
    try {
      const live = await CampAPI.list();
      setCampanhas(Array.isArray(live) ? live : []);
    } catch {
      setCampanhas([]);
    }
  }, []);

  React.useEffect(() => { load(); }, [load]);

  // Auto-abrir campanha vinda do briefing (via localStorage)
  React.useEffect(() => {
    if (!campanhas) return;
    try {
      const pendingId = localStorage.getItem('mkt-open-campanha-id');
      if (!pendingId) return;
      localStorage.removeItem('mkt-open-campanha-id');
      const camp = campanhas.find(c => String(c.id) === pendingId);
      if (camp) setSelected(camp);
    } catch {}
  }, [campanhas]);

  React.useEffect(() => {
    CampAPI.getBrands().then(d => {
      if (!Array.isArray(d)) return;
      const sorted = d.filter(b => b.active !== false && b.slug !== 'todas').sort((a, b) => {
        const ia = BRAND_ORDER.indexOf(a.slug), ib = BRAND_ORDER.indexOf(b.slug);
        return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
      });
      setAllBrands(sorted);
    }).catch(() => {});
  }, []);

  const [campUsageStats, setCampUsageStats] = React.useState(null);
  React.useEffect(() => {
    fetch('/api/marketing/campanhas/usage-stats')
      .then(r => r.ok ? r.json() : null)
      .then(data => { if (data) setCampUsageStats(data); })
      .catch(() => {});
  }, []);

  const toast_ = (msg) => { setToast(msg); setTimeout(() => setToast(''), 3200); };

  const [showDeleteModal, setShowDeleteModal] = React.useState(false);
  const [pendingDeleteCamp, setPendingDeleteCamp] = React.useState(null);

  const handleDelete = (campOrId) => {
    if (!canActOnCampaign(userEmail)) { toast_('Sem permissão para apagar campanhas.'); return; }
    const camp = typeof campOrId === 'object' && campOrId !== null ? campOrId : { id: campOrId, titulo: `Campanha #${campOrId}` };
    if (FINAL_STATUSES.includes(camp.status)) {
      toast_('Campanha em produção ou publicada não pode ser apagada. Contacta o admin.');
      return;
    }
    setPendingDeleteCamp(camp);
    setShowDeleteModal(true);
  };

  const handleDeleteConfirm = async () => {
    if (!pendingDeleteCamp) return;
    try {
      await CampAPI.remove(pendingDeleteCamp.id);
      await load();
      toast_('Campanha apagada.');
    } catch { toast_('Erro ao apagar campanha.'); }
    setShowDeleteModal(false);
    setPendingDeleteCamp(null);
  };

  const COL_TAB_MAP = { briefing: 'briefing', estrategia: 'estrategia', conceito: 'conceito', orcamento: 'orcamento', segmentacao: 'segmentacao', planeamento: 'planeamento', funil: 'funil', aprovacao: 'aprovacao', producao: 'producao' };
  const STATUS_TAB_MAP = {
    estrategia_pendente: 'estrategia', estrategia_gerada: 'estrategia', estrategia_aprovada: 'estrategia',
    conceito_pendente: 'estrategia', conceito_gerado: 'conceito',   conceito: 'conceito',
    canais_pendente:   'canais',    canais_gerado:   'canais',      canais_aprovado: 'copy',
    copy_pendente:     'copy',      copy_gerado:     'copy',        copy_aprovado:   'idiomas',
    idiomas_pendente:  'idiomas',   idiomas_gerado:  'idiomas',     idiomas_aprovado: 'prompts',
    prompts_pendente:  'prompts',   prompts_gerado:  'prompts',     geracao: 'prompts', aprovacao: 'prompts',
    prompts_aprovado:  'aprovacao', pending_executive: 'aprovacao',
    em_producao: 'producao', publicado: 'producao',
  };
  const handleCardClick = (c) => {
    setInitialTab(STATUS_TAB_MAP[c.status] || 'conceito');
    setSelected(c);
  };


  const produtos = React.useMemo(() => {
    const s = new Set((campanhas || []).map(c => c.commercial_name || c.product_name).filter(Boolean));
    return [...s].sort().map(v => ({ value: v, label: v }));
  }, [campanhas]);

  const criadores = React.useMemo(() => {
    const s = new Set((campanhas || []).map(c => c.created_by_name).filter(Boolean));
    return [...s].sort().map(v => ({ value: v, label: v }));
  }, [campanhas]);

  const filtered = React.useMemo(() => {
    if (!campanhas) return [];
    return campanhas.filter(c => {
      if (filterBrand !== 'all' && c.brand_slug !== filterBrand) return false;
      if (filterCol !== 'all') {
        const col = KANBAN_COLS.find(k => k.id === filterCol);
        if (col && !col.statuses.includes(c.status)) return false;
      }
      if (filterProduto !== 'all' && (c.commercial_name || c.product_name) !== filterProduto) return false;
      if (filterCriador !== 'all' && c.created_by_name !== filterCriador) return false;
      if (filterMecanica !== 'all') {
        const ofType = c.commercial_offer?.type || null;
        if (filterMecanica === 'none' ? ofType !== null : ofType !== filterMecanica) return false;
      }
      if (dateRange.preset !== 'all' && dateRange.start && dateRange.end) {
        const d = new Date(c.created_at);
        if (d < dateRange.start || d > dateRange.end) return false;
      }
      if (search.trim()) {
        const q = search.toLowerCase();
        if (!(c.titulo || '').toLowerCase().includes(q) && !(c.commercial_name || c.product_name || '').toLowerCase().includes(q)) return false;
      }
      return true;
    });
  }, [campanhas, filterBrand, filterCol, filterProduto, filterCriador, filterMecanica, dateRange, search]);

  const kpi = React.useMemo(() => {
    const all = campanhas || [];
    const count = (id) => { const col = KANBAN_COLS.find(k => k.id === id); return col ? all.filter(c => col.statuses.includes(c.status)).length : 0; };
    return { total: all.length, briefing: count('briefing'), estrategia: count('estrategia'), conceito: count('conceito'), orcamento: count('orcamento'), segmentacao: count('segmentacao'), planeamento: count('planeamento'), funil: count('funil'), aprovacao: count('aprovacao'), producao: count('producao') };
  }, [campanhas]);

  const kpiCards = [
    { id: 'briefing',    label: 'Briefing',    value: kpi.briefing,    accent: '#3859D0', fill: kpi.total ? kpi.briefing    / kpi.total : 0 },
    { id: 'estrategia',  label: 'Estratégia',  value: kpi.estrategia,  accent: '#5B43C5', fill: kpi.total ? kpi.estrategia  / kpi.total : 0 },
    { id: 'conceito',    label: 'Conceito',    value: kpi.conceito,    accent: '#94a3b8', fill: kpi.total ? kpi.conceito    / kpi.total : 0 },
    { id: 'orcamento',   label: 'Orçamento',   value: kpi.orcamento,   accent: '#0891b2', fill: kpi.total ? kpi.orcamento   / kpi.total : 0 },
    { id: 'segmentacao', label: 'Target', value: kpi.segmentacao, accent: '#7c3aed', fill: kpi.total ? kpi.segmentacao / kpi.total : 0 },
    { id: 'planeamento', label: 'Planeamento', value: kpi.planeamento, accent: '#0f766e', fill: kpi.total ? kpi.planeamento / kpi.total : 0 },
    { id: 'funil',       label: 'Funil',       value: kpi.funil,       accent: '#112954', fill: kpi.total ? kpi.funil       / kpi.total : 0 },
    { id: 'aprovacao',   label: 'Aprovação',   value: kpi.aprovacao,   accent: '#ea580c', fill: kpi.total ? kpi.aprovacao   / kpi.total : 0 },
    { id: 'producao',    label: 'Produção',    value: kpi.producao,    accent: '#22c55e', fill: kpi.total ? kpi.producao    / kpi.total : 0 },
  ];

  // Auto-collapse: vazias colapsadas, com cards expandidas.
  // manualExpanded = colunas vazias que o user forçou a abrir (opt-in override).
  const [manualExpanded, setManualExpanded] = React.useState(() => new Set());
  const toggleCol = (colId) => setManualExpanded(prev => {
    const next = new Set(prev);
    next.has(colId) ? next.delete(colId) : next.add(colId);
    return next;
  });

  const colCards = KANBAN_COLS.map(col => ({
    ...col,
    cards: filtered.filter(c => col.statuses.includes(c.status)),
  }));

  const hasFilter = filterBrand !== 'all' || filterCol !== 'all' || filterProduto !== 'all' || filterCriador !== 'all' || filterMecanica !== 'all' || dateRange.preset !== 'all' || search.trim();

  // ── Vista de detalhe (sempre CampanhaDetail — stepper como navegação principal) ──
  if (selected) {
    return (
      <>
        <CampanhaDetail campanhaSummary={selected} onBack={() => setSelected(null)} onRefresh={load} initialTab={initialTab} userEmail={userEmail} />
        {editing && (
          <EditTituloModal campanha={editing} onSave={load} onClose={() => setEditing(null)} />
        )}
      </>
    );
  }

  // ── Vista Kanban ────────────────────────────────────────────────────────────
  return (
    <>
      {showModal && (
        <NovaCampanhaModal
          onClose={() => setShowModal(false)}
          onCreated={(camp) => { setShowModal(false); load(); toast_(`Campanha "${camp.titulo}" criada.`); }}
        />
      )}
      <div data-theme="light" style={{ height: '100%', display: 'flex', flexDirection: 'column', background: '#f8fafc' }}>

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

          {/* Breadcrumb */}
          <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>

          {/* Title + action */}
          <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 }}>
                Campanhas
              </h1>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6, flexWrap: 'wrap' }}>
                <span style={{ fontSize: 11, color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>
                  {kpi.total} campanha{kpi.total !== 1 ? 's' : ''} · {kpi.producao} em produção
                </span>
                {kpi.total > 0 && (() => {
                  const pct = Math.round(kpi.producao / kpi.total * 100);
                  return (
                    <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                      <div style={{ width: 60, height: 4, borderRadius: 99, background: 'var(--border, #ECEFF5)', overflow: 'hidden' }}>
                        <div style={{ width: `${pct}%`, height: '100%', borderRadius: 99, background: 'var(--success, #22c55e)', transition: 'width 0.4s ease' }} />
                      </div>
                      <span style={{ fontSize: 11, color: 'var(--text-muted, #94A4C4)', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{pct}% em produção</span>
                    </div>
                  );
                })()}
              </div>
            </div>
            <button onClick={() => setShowModal(true)} data-tutorial-step="create_campaign" className="btn btn-ai"
              style={{ height: 30, padding: '0 16px', fontSize: 12, whiteSpace: 'nowrap', flexShrink: 0, marginTop: 2 }}>
              + Nova Campanha
            </button>
          </div>

          {/* Separador */}
          <div style={{ height: 1, background: 'var(--border, #ECEFF5)', margin: '16px -40px 0' }} />

          {/* Brand tabs — flush com o conteúdo (primeira tab alinhada com o título) */}
          <div style={{ display: 'flex', gap: 0, overflowX: 'auto', marginLeft: 0 }}>
            {[{ slug: 'all', name: 'Todas' }, ...allBrands].map(b => {
              const active = b.slug === filterBrand;
              return (
                <button key={b.slug} onClick={() => setFilterBrand(b.slug === 'all' ? 'all' : b.slug)} style={{
                  background: 'none', border: 'none', outline: 'none',
                  borderBottom: `2px solid ${active ? 'var(--ai-500, #3859D0)' : 'transparent'}`,
                  color: active ? 'var(--text, #283252)' : 'var(--text-muted, #94A4C4)',
                  fontSize: 13, fontWeight: active ? 600 : 500,
                  fontFamily: 'var(--font-display, Montserrat, sans-serif)', letterSpacing: '.01em',
                  padding: '10px 16px 12px', cursor: 'pointer', whiteSpace: 'nowrap',
                  transition: 'color .15s, border-color .15s',
                }}>
                  {b.name}
                </button>
              );
            })}
          </div>
        </div>

        {/* ── Scrollable body — container único com padding uniforme ── */}
        <div className="scrollbar" style={{ flex: 1, minHeight: 0, overflowY: 'auto', overflowX: 'auto' }}>
          <div style={{ padding: '28px 40px 48px', minWidth: 640, display: 'flex', flexDirection: 'column', gap: 16 }}>
            <CampBanner kpi={kpi} onFilter={setFilterCol} campanhas={filtered} userEmail={userEmail} />

          {/* Filter row */}
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8 }}>
            <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', alignItems: 'center' }}>
              <CampDropdown label="Produto" value={filterProduto} onChange={setFilterProduto}
                options={[{ value: 'all', label: 'Todos' }, ...produtos]}
              />
              {criadores.length > 0 && (
                <CampDropdown label="Criado por" value={filterCriador} onChange={setFilterCriador}
                  options={[{ value: 'all', label: 'Todos' }, ...criadores]}
                />
              )}
              <CampDropdown label="Estado" value={filterCol} onChange={setFilterCol}
                options={[
                  { value: 'all',      label: 'Todos' },
                  { value: 'briefing', label: 'Briefing' },
                  { value: 'conceito', label: 'Conceito' },
                  { value: 'copy',     label: 'Copy' },
                  { value: 'idiomas',  label: 'Idiomas' },
                  { value: 'prompts',  label: 'Prompts AI' },
                  { value: 'producao', label: 'Produção' },
                ]}
              />
              <CampDropdown label="Mecânica" value={filterMecanica} onChange={setFilterMecanica}
                options={[
                  { value: 'all',            label: 'Todas' },
                  { value: 'none',           label: 'Sem oferta' },
                  { value: 'digirent',       label: 'Digirent' },
                  { value: 'printplan',      label: 'PrintPlan' },
                  { value: 'voucher',        label: 'Voucher on Demand' },
                  { value: 'direct_discount',label: 'Desconto Directo' },
                  { value: 'trade_in',       label: 'Trade-In' },
                ]}
              />
              {hasFilter && (
                <button onClick={() => { setFilterBrand('all'); setFilterCol('all'); setFilterProduto('all'); setFilterCriador('all'); setFilterMecanica('all'); setDateRange({ preset: 'all', start: new Date(new Date().getFullYear(), 0, 1), end: new Date() }); setSearch(''); }}
                  style={{ background: 'none', border: 'none', color: 'var(--text-muted, #64748b)', fontSize: 11, cursor: 'pointer', padding: '4px 6px', fontFamily: 'var(--font-mono, monospace)' }}>
                  ✕ limpar
                </button>
              )}
              <span style={{ fontSize: 12, color: 'var(--text-muted, #64748b)', paddingLeft: 4 }}>
                {filtered.length} resultado{filtered.length !== 1 ? 's' : ''}
              </span>
            </div>
            <CampDateRangePicker value={dateRange} onChange={setDateRange} />
          </div>

          {/* KPI strip + Kanban — layout unificado por coluna com collapse */}
          <div style={{ overflowX: 'auto', paddingBottom: 8 }}>
            {campanhas === null ? (
              <div style={{ display: 'flex', gap: 12, minWidth: 'max-content' }}>
                {KANBAN_COLS.map(col => (
                  <div key={col.id} style={{ width: 240, flexShrink: 0 }}>
                    <div style={{ height: 72, borderRadius: 8, background: '#ffffff', marginBottom: 12, opacity: 0.5 }} />
                    {[1, 2].map(i => <div key={i} style={{ height: 88, borderRadius: 8, background: '#ffffff', marginBottom: 8, opacity: 1 - i * 0.3 }} />)}
                  </div>
                ))}
              </div>
            ) : filtered.length === 0 && campanhas.length === 0 ? (
              <div style={{ padding: '48px 0', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}>
                <div style={{ fontSize: 13, color: 'var(--text-muted, #64748b)', textAlign: 'center' }}>
                  Ainda não há campanhas. Aprova um briefing e clica em "+ Criar Campanha" para começar.
                </div>
              </div>
            ) : filtered.length === 0 ? (
              <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 resultados para os filtros actuais.</div>
                <button onClick={() => { setFilterBrand('all'); setFilterCol('all'); setSearch(''); }} className="btn" style={{ fontSize: 12 }}>Limpar filtros</button>
              </div>
            ) : (
              <div style={{ display: 'flex', gap: 10, minWidth: 'max-content', alignItems: 'flex-start' }}>
                {colCards.map((col, i) => {
                  const k       = kpiCards.find(k => k.id === col.id) || { label: col.label, value: col.cards.length, accent: col.color, fill: 0 };
                  const collapsed = col.cards.length === 0 && !manualExpanded.has(col.id);
                  const colW    = collapsed ? 52 : 280;
                  return (
                    <div key={col.id} style={{ width: colW, minWidth: colW, flexShrink: 0, transition: 'width 0.22s ease',  }}>
                      {/* KPI card — header da coluna */}
                      {collapsed ? (
                        /* ── Collapsed strip ── */
                        <div onClick={() => toggleCol(col.id)} title={`Expandir ${k.label}`} style={{
                          width: 52, borderRadius: 8, cursor: 'pointer', overflow: 'hidden',
                          background: k.accent + '10',
                          border: `1px solid ${k.accent}30`,
                          display: 'flex', flexDirection: 'column', alignItems: 'center',
                          padding: '10px 0 8px', gap: 6, marginBottom: 10,
                          transition: 'background 0.15s',
                        }}>
                          {/* Count badge */}
                          <div style={{ width: 24, height: 24, borderRadius: '50%', background: k.value > 0 ? k.accent : 'transparent', border: `1.5px solid ${k.accent}`, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                            <span style={{ fontSize: 11, fontWeight: 700, color: k.value > 0 ? '#fff' : k.accent, fontFamily: 'monospace', lineHeight: 1 }}>{k.value}</span>
                          </div>
                          {/* Label vertical */}
                          <div style={{ writingMode: 'vertical-rl', textOrientation: 'mixed', transform: 'rotate(180deg)', fontSize: 10, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: k.accent, fontFamily: 'var(--font-mono, monospace)', userSelect: 'none' }}>
                            {k.label}
                          </div>
                          {/* Expand arrow */}
                          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke={k.accent} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="9 18 15 12 9 6"/></svg>
                        </div>
                      ) : (
                        /* ── Expanded card ── */
                        <div style={{
                          background: '#ffffff', border: '1px solid var(--border, #e2e8f0)',
                          borderRadius: 8, marginBottom: 10, overflow: 'hidden', cursor: 'pointer',
                          transition: 'border-color 150ms',
                        }} onClick={() => toggleCol(col.id)}>
                          <div style={{ padding: '10px 14px 0' }}>
                            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>
                              <div style={{ fontSize: 9.5, fontWeight: 600, letterSpacing: '0.10em', textTransform: 'uppercase', color: 'var(--text-muted, #64748b)', fontFamily: 'var(--font-mono, monospace)', whiteSpace: 'nowrap' }}>{k.label}</div>
                              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="var(--text-dim,#475569)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="15 18 9 12 15 6"/></svg>
                            </div>
                            <div style={{ fontSize: 22, fontWeight: 700, fontFamily: 'var(--font-display, Montserrat, sans-serif)', color: k.accent, lineHeight: 1, paddingBottom: 8 }}>{k.value}</div>
                            <div style={{ height: 3, background: 'var(--bg-sunken, #f1f5f9)', overflow: 'hidden' }}>
                              <div style={{ height: '100%', width: `${(k.fill || 0) * 100}%`, background: k.accent, transition: 'width .4s' }} />
                            </div>
                          </div>
                        </div>
                      )}

                      {/* Kanban column — só se expandido */}
                      {!collapsed && (
                        <KanbanColumn
                          col={col} cards={col.cards}
                          onCardClick={handleCardClick}
                          onEdit={setEditing}
                          onDelete={handleDelete}
                          userEmail={userEmail}
                        />
                      )}
                    </div>
                  );
                })}
              </div>
            )}
          </div>
        </div>

        {showDeleteModal && (
          <CampDeleteConfirmModal
            campanha={pendingDeleteCamp}
            onConfirm={handleDeleteConfirm}
            onClose={() => { setShowDeleteModal(false); setPendingDeleteCamp(null); }}
          />
        )}

        <MktCampToast msg={toast} type="info" />
        </div> {/* fim scrollable body */}
        {campUsageStats && (() => {
          const fmtEur = (v) => v != null ? `€${parseFloat(v).toFixed(4).replace(/0+$/, '').replace(/\.$/, '')}` : '—';
          const fmtTok = (v) => Number(v || 0).toLocaleString('pt-PT');
          const sep = <span style={{ margin: '0 5px', opacity: 0.35 }}>·</span>;
          return (
            <div style={{
              flexShrink: 0, borderTop: '1px solid var(--border, #dde3ef)',
              background: '#ffffff', padding: '5px 24px',
              display: 'flex', alignItems: 'center', gap: 0,
              fontSize: 10.5, fontFamily: 'var(--font-mono)', color: 'var(--text-dim, #6b7fa3)',
            }}>
              <span style={{ fontWeight: 600, color: 'var(--text-muted, #4a5e7a)', marginRight: 10, fontSize: 10 }}>DIGI AI</span>
              <span>Custo total: <strong style={{ color: 'var(--text-muted)', fontWeight: 600 }}>{fmtEur(campUsageStats.custo_total)}</strong></span>
              {sep}<span>Média/campanha: <strong style={{ color: 'var(--text-muted)', fontWeight: 600 }}>{fmtEur(campUsageStats.media_por_campanha)}</strong></span>
              {sep}<span>Tokens: <strong style={{ color: 'var(--text-muted)', fontWeight: 600 }}>{fmtTok(campUsageStats.total_tokens)}</strong></span>
              {sep}<span>{Number(campUsageStats.total_chamadas || 0)} chamadas API</span>
            </div>
          );
        })()}
      </div>

      {editing && (
        <EditTituloModal campanha={editing} onSave={load} onClose={() => setEditing(null)} />
      )}

      {/* Tutorial — floating button + drawer */}
      {window.MktTutorial && (() => {
        // Construir detail sintético a partir do selected (summary da lista)
        const CANAIS_STATUSES = ['canais_gerado','canais_aprovado','copy_pendente','copy_gerado','copy_aprovado','idiomas_pendente','idiomas_gerado','idiomas_aprovado','prompts_pendente','prompts_gerado','prompts_aprovado','pending_executive','em_producao','publicado'];
        const tutDetail = selected ? {
          id: selected.id,
          big_idea: selected.big_idea || null,
          canais_setup: CANAIS_STATUSES.includes(selected.status) ? true : null,
          copy: selected.num_copy_aprovado > 0
            ? Array(Number(selected.num_copy_aprovado)).fill({ status: 'aprovado' })
            : (selected.num_copy > 0 ? [{ status: 'pendente' }] : []),
          idiomas: selected.num_idiomas > 0 ? [{}] : [],
          prompts: selected.num_prompts_aprovado > 0
            ? Array(Number(selected.num_prompts_aprovado)).fill({ status: 'aprovado' })
            : (selected.num_prompts > 0 ? [{ status: 'pendente' }] : []),
          status: selected.status,
        } : null;
        return (
          <window.MktTutorial
            moduleName="campanhas"
            moduleLabel="Campanhas"
            moduleState={{ detail: tutDetail }}
            userEmail={userEmail}
          />
        );
      })()}
    </>
  );
};

window.MktCampanhasScreen = MktCampanhasScreen;
// ─── (fim) ───────────────────────────────────────────────────────────────────
