/* screen_marketing_producao.jsx
   Marketing · Produção de Conteúdos
   Pipeline: FLUX (fal.ai) → Puppeteer overlay → R2 storage → activacao_payloads
   Expõe window.MktProducaoScreen.
*/

// ── Conversão USD → EUR (custos fal.ai/R2 são em USD; UI mostra euros) ───────
const _USD_TO_EUR = 0.92;
const fmtEur = (usd, decimals = 2) => {
  const v = parseFloat(usd || 0) * _USD_TO_EUR;
  return `€${v.toFixed(decimals)}`;
};

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

// ── Status config ──────────────────────────────────────────────────────────────
const PROD_STATUS_CFG = {
  pending:             { label: 'A aguardar',     color: '#9CA3AF', bg: 'rgba(156,163,175,0.12)' },
  generating_img:      { label: 'A gerar',         color: '#F59E0B', bg: 'rgba(245,158,11,0.12)',  pulse: true },
  composing_overlay:   { label: 'A compor',        color: '#F59E0B', bg: 'rgba(245,158,11,0.12)',  pulse: true },
  uploading_r2:        { label: 'A guardar',       color: '#F59E0B', bg: 'rgba(245,158,11,0.12)',  pulse: true },
  ready_review:        { label: 'Para rever',      color: '#3859D0', bg: 'rgba(56,89,208,0.12)' },
  regenerating:        { label: 'A regenerar',     color: '#D97706', bg: 'rgba(217,119,6,0.12)',   pulse: true },
  blocked:             { label: 'Bloqueada',        color: '#F97316', bg: 'rgba(249,115,22,0.12)' },
  approved:            { label: 'Aprovada',         color: '#10B981', bg: 'rgba(16,185,129,0.12)' },
  pending_activation:  { label: 'Pend. activação', color: '#059669', bg: 'rgba(5,150,105,0.12)' },
  failed:              { label: 'Falhou',           color: '#EF4444', bg: 'rgba(239,68,68,0.12)' },
  deferred:            { label: 'Fase 2',            color: '#8B92A8', bg: 'rgba(139,146,168,0.12)' },
};

function PecaStatusBadge({ status }) {
  const cfg = PROD_STATUS_CFG[status] || PROD_STATUS_CFG.pending;
  return (
    <span style={{
      display: 'inline-block', fontSize: 11, fontWeight: 600,
      padding: '3px 9px', borderRadius: 99,
      color: cfg.color, background: cfg.bg,
      fontFamily: 'var(--font-mono, monospace)',
      animation: cfg.pulse ? 'pulse 1.5s ease-in-out infinite' : 'none',
    }}>
      {cfg.label}
    </span>
  );
}

// ── API ────────────────────────────────────────────────────────────────────────
const ProdAPI = {
  kpis:           ()     => fetch('/api/marketing/producao/kpis').then(r => r.json()),
  campanhas:      ()     => fetch('/api/marketing/producao/campanhas').then(r => r.json()),
  campanha:       (id)   => fetch(`/api/marketing/producao/campanha/${id}`).then(r => r.json()),
  peca:           (id)   => fetch(`/api/marketing/producao/peca/${id}`).then(r => r.json()),
  regenerate:     (id, prompt_override) => fetch(`/api/marketing/producao/peca/${id}/regenerate`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ prompt_override }),
  }).then(r => r.json()),
  approve:        (id, email) => fetch(`/api/marketing/producao/peca/${id}/approve`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ approver_email: email }),
  }).then(r => r.json()),
  block:          (id, reason) => fetch(`/api/marketing/producao/peca/${id}/block`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ reason }),
  }).then(r => r.json()),
  unblock:        (id) => fetch(`/api/marketing/producao/peca/${id}/unblock`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' },
  }).then(r => r.json()),
  approveBulk:    (campId, peca_ids, email) => fetch(`/api/marketing/producao/campanha/${campId}/pecas/approve-bulk`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ peca_ids, approver_email: email }),
  }).then(r => r.json()),
  regenerateBulk: (campId, peca_ids) => fetch(`/api/marketing/producao/campanha/${campId}/pecas/regenerate-bulk`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ peca_ids }),
  }).then(r => r.json()),
  finalize:       (campId, email) => fetch(`/api/marketing/producao/campanha/${campId}/finalize`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ approver_email: email }),
  }).then(r => r.json()),
  iniciar:        (campId) => fetch(`/api/marketing/producao/campanha/${campId}/iniciar-producao`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' },
  }).then(r => r.json()),
  custos:         (campId) => fetch(`/api/marketing/producao/campanha/${campId}/custos`).then(r => r.json()),
  deleteCampanha: (id)     => fetch(`/api/marketing/producao/campanha/${id}`, { method: 'DELETE' }).then(r => r.json()),
};

// ── Modal de confirmação de eliminação ────────────────────────────────────────
const ProdDeleteConfirmModal = ({ pendingDelete, onConfirm, onClose }) => {
  const [input, setInput] = React.useState('');
  const valid = input === 'DELETE';
  if (!pendingDelete) return null;
  const count = pendingDelete.ids.length;
  const labels = pendingDelete.labels || [];
  return (
    <div style={{ position: 'fixed', inset: 0, zIndex: 999, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(10,16,30,.55)' }}
      onClick={e => { if (e.target === e.currentTarget) onClose(); }}>
      <div style={{ background: '#fff', borderRadius: 14, width: 460, boxShadow: '0 24px 64px rgba(0,0,0,.2)', overflow: 'hidden' }}>
        {/* Header */}
        <div style={{ background: '#fef2f2', padding: '20px 24px 16px', borderBottom: '1px solid #fecaca' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 6 }}>
            <div style={{ width: 32, height: 32, borderRadius: 8, background: '#fee2e2', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <Icon name="alert" size={17} style={{ color: '#dc2626' }} />
            </div>
            <span style={{ fontSize: 15, fontWeight: 700, color: '#1d2e38', fontFamily: 'var(--font-display)' }}>
              Eliminar {count === 1 ? 'campanha' : `${count} campanhas`} de produção
            </span>
          </div>
          <div style={{ fontSize: 12, color: '#6b7fa3', lineHeight: 1.5 }}>
            Esta acção é <strong style={{ color: '#dc2626' }}>permanente e irreversível</strong>. Todas as peças geradas, aprovações e dados de produção serão eliminados. Os briefings voltarão ao estado "Aprovado".
          </div>
        </div>
        {/* Body */}
        <div style={{ padding: '18px 24px' }}>
          <div style={{ marginBottom: 12, display: 'flex', flexDirection: 'column', gap: 4 }}>
            {labels.slice(0, 3).map((l, i) => (
              <div key={i} style={{ padding: '8px 12px', borderRadius: 7, background: '#f8fafc', border: '1px solid #dde3ef', fontSize: 13, color: '#1d2e38', fontWeight: 500 }}>{l}</div>
            ))}
            {labels.length > 3 && (
              <div style={{ fontSize: 12, color: '#6b7fa3', paddingLeft: 4 }}>+ {labels.length - 3} mais</div>
            )}
          </div>
          <div style={{ fontSize: 12, color: '#6b7fa3', marginBottom: 8 }}>
            Para confirmar, escreve <code style={{ fontFamily: 'monospace', background: '#f1f5f9', padding: '1px 5px', borderRadius: 3, fontSize: 11 }}>DELETE</code> no campo abaixo:
          </div>
          <input
            autoFocus
            value={input}
            onChange={e => setInput(e.target.value)}
            onKeyDown={e => e.key === 'Enter' && valid && onConfirm()}
            placeholder="DELETE"
            style={{ width: '100%', padding: '9px 12px', borderRadius: 8, fontSize: 13, border: `1.5px solid ${valid ? '#dc2626' : '#dde3ef'}`, outline: 'none', fontFamily: 'monospace', boxSizing: 'border-box', letterSpacing: '0.05em' }}
          />
        </div>
        {/* Footer */}
        <div style={{ padding: '12px 24px 20px', display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
          <button onClick={onClose} className="btn btn-sm" style={{ fontFamily: 'var(--font-body)' }}>Cancelar</button>
          <button onClick={onConfirm} disabled={!valid}
            style={{ padding: '7px 16px', borderRadius: 8, fontSize: 13, fontWeight: 600, fontFamily: 'var(--font-body)', cursor: valid ? 'pointer' : 'not-allowed', border: 'none', background: valid ? '#dc2626' : '#fee2e2', color: valid ? '#fff' : '#fca5a5', transition: 'background .15s' }}>
            Eliminar {count === 1 ? 'campanha' : `${count} campanhas`}
          </button>
        </div>
      </div>
    </div>
  );
};

// ── DateRangePicker simplificado (presets) ────────────────────────────────────
const ProducaoDateRangePicker = ({ value, onChange }) => {
  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 presets = [
    { id: 'all',        label: 'Todo o período' },
    { 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 current = presets.find(p => p.id === value) || presets[0];
  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button onClick={() => setOpen(v => !v)} style={{
        background: 'var(--bg-elev)', color: 'var(--text)',
        border: '1px solid var(--border)', 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)', fontSize: 10, textTransform: 'uppercase', letterSpacing: '.06em', fontFamily: 'var(--font-mono)' }}>Período</span>
        <span style={{ fontWeight: 500 }}>{current.label}</span>
        <span style={{ color: 'var(--text-muted)', fontSize: 9 }}>▾</span>
      </button>
      {open && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 4px)', right: 0, zIndex: 50,
          background: 'var(--bg-elev)', border: '1px solid var(--border)', borderRadius: 6,
          boxShadow: '0 8px 24px rgba(17,41,84,0.12)', minWidth: 180, padding: 4,
        }}>
          {presets.map(p => (
            <button key={p.id} onClick={() => { onChange(p.id); setOpen(false); }} style={{
              display: 'block', width: '100%', textAlign: 'left',
              background: p.id === value ? 'color-mix(in oklch, var(--ai-500) 12%, transparent)' : 'transparent',
              color: p.id === value ? 'var(--ai-500)' : 'var(--text)',
              border: 'none', padding: '7px 10px', borderRadius: 4,
              fontSize: 12, fontWeight: p.id === value ? 600 : 500, cursor: 'pointer', fontFamily: 'inherit',
            }}>{p.label}</button>
          ))}
        </div>
      )}
    </div>
  );
};

// ── Dropdown (nome único do módulo — regra) ───────────────────────────────────
const ProducaoDropdown = ({ 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: 'var(--bg-elev, #ffffff)', color: disabled ? 'var(--text-dim, #475569)' : 'var(--text, #112954)',
        border: '1px solid var(--border, #dde3ef)', 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, #112954)' }}>{current?.label || '—'}</span>
        <span style={{ color: 'var(--text-muted, #64748b)', fontSize: 9 }}>▾</span>
      </button>
      {open && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 4px)', left: 0, zIndex: 50,
          background: 'var(--bg-elev, #ffffff)', border: '1px solid var(--border, #dde3ef)', borderRadius: 6,
          boxShadow: '0 8px 24px rgba(17,41,84,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, #112954)',
              border: 'none', padding: '7px 10px', borderRadius: 4,
              fontSize: 12, fontWeight: o.value === value ? 600 : 500,
              cursor: 'pointer', fontFamily: 'inherit',
            }}>{o.label}</button>
          ))}
        </div>
      )}
    </div>
  );
};

// ── Banner Digi AI ─────────────────────────────────────────────────────────────
const ProducaoBanner = ({ kpi, campanhas = [] }) => {
  const [seed] = React.useState(Math.random);
  const hour = new Date().getHours();
  const rawName = window.currentUser?.nome_apresentar || window.currentUser?.nome || '';
  const first = rawName.split(' ')[0] || '';
  const n = first ? first.charAt(0).toUpperCase() + first.slice(1).toLowerCase() : 'Olá';
  const pl = (k, s, p) => k === 1 ? s : p;

  const review    = parseInt(kpi?.ready_review || 0);
  const aprovadas = parseInt(kpi?.aprovadas || 0);
  const falhas    = parseInt(kpi?.falhas || 0);
  const emProd    = parseInt(kpi?.em_producao || 0);

  const { heading, body } = React.useMemo(() => {
    let pool;
    if (review > 0) pool = [
      `${n}, tens ${review} ${pl(review, 'peça pronta', 'peças prontas')} para rever. A Digi AI fez a parte dela.`,
      `${n}, ${review} ${pl(review, 'asset gerado está', 'assets gerados estão')} à tua espera.`,
      `${n}, a parte criativa está feita. Agora és tu a aprovar.`,
    ];
    else if (falhas > 0) pool = [
      `${n}, ${falhas} ${pl(falhas, 'peça falhou', 'peças falharam')} a geração. Vê o que se passou.`,
      `${n}, há ${falhas} ${pl(falhas, 'peça', 'peças')} que ${pl(falhas, 'precisa', 'precisam')} de regeneração.`,
    ];
    else if (emProd > 0) pool = [
      `${n}, a Digi AI está a trabalhar em ${emProd} ${pl(emProd, 'campanha', 'campanhas')}. Volta em breve.`,
      `${n}, pipeline em movimento — ${emProd} ${pl(emProd, 'campanha', 'campanhas')} em produção.`,
    ];
    else if (aprovadas > 0) pool = [
      `${n}, tudo aprovado. ${aprovadas} ${pl(aprovadas, 'peça segue', 'peças seguem')} para Activação.`,
      `${n}, trabalho limpo — ${aprovadas} ${pl(aprovadas, 'peça', 'peças')} ${pl(aprovadas, 'pronta', 'prontas')} a publicar.`,
    ];
    else {
      const h = hour < 12 ? 'Bom dia' : hour < 19 ? 'Boa tarde' : 'Boa noite';
      pool = [
        `${h}${n !== 'Olá' ? `, ${n}` : ''}. Nenhuma campanha em produção agora.`,
        `${n}, pipeline limpo. Aprova uma campanha em Campanhas para começar.`,
      ];
    }
    const heading = pool[Math.floor(seed * pool.length)];

    const partes = [];
    if (review)    partes.push(`${review} ${pl(review, 'peça pronta', 'peças prontas')} para aprovares.`);
    if (falhas)    partes.push(`${falhas} ${pl(falhas, 'peça falhou', 'peças falharam')} — podes regenerar.`);
    if (aprovadas) partes.push(`${aprovadas} ${pl(aprovadas, 'peça aprovada aguarda', 'peças aprovadas aguardam')} Activação.`);
    if (emProd && !review && !falhas) partes.push('A Digi AI está a gerar os assets. Esta página actualiza sozinha à medida que as peças saem.');
    if (!partes.length) partes.push('Aprova uma campanha no módulo Campanhas para iniciar a produção de assets.');
    return { heading, body: partes.join(' ') };
  }, [kpi, seed]);

  return (
    <div style={{ flexShrink: 0, padding: '10px 0 18px' }}>
      <div style={{
        fontSize: 22, fontWeight: 700, color: 'var(--text)',
        fontFamily: 'var(--font-display)', marginBottom: 6, letterSpacing: '-0.02em',
      }}>{heading}</div>
      <div style={{ fontSize: 13, color: 'var(--text-muted)', lineHeight: 1.75 }}>
        {body}
      </div>
    </div>
  );
};

// ── KPI strip ──────────────────────────────────────────────────────────────────
const ProducaoKPIs = ({ kpi, filterEstado, onFilter }) => {
  const total      = parseInt(kpi?.em_producao || 0) + parseInt(kpi?.aprovadas || 0);
  const review     = parseInt(kpi?.ready_review || 0);
  const aprovadas  = parseInt(kpi?.aprovadas || 0);
  const falhas     = parseInt(kpi?.falhas || 0);

  // 4 KPIs alinhados com padrão Briefings (cards largos, accent + fill bar)
  const items = [
    { id: 'review',      label: 'Para rever',   value: review,    accent: 'var(--ai-500, #3859D0)', fill: total ? review/total : 0 },
    { id: 'aprovadas',   label: 'Aprovadas',    value: aprovadas, accent: 'var(--success, #22c55e)', fill: total ? aprovadas/total : 0 },
    { id: 'falhas',      label: 'Falharam',     value: falhas,    accent: 'var(--danger, #ef4444)',  fill: total ? falhas/total : 0 },
    { id: 'all',         label: 'Total peças',  value: total,     accent: 'var(--text-muted, #4a5e7a)', fill: 1, noClick: true },
  ];

  return (
    <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
      {items.map(k => {
        const isActive = !k.noClick && filterEstado === k.id;
        return (
          <div key={k.id}
            onClick={() => { if (k.noClick) return; onFilter && onFilter(isActive ? 'all' : k.id); }}
            style={{
              flex: '1 1 0', minWidth: 130,
              background: 'var(--bg-elev, #ffffff)',
              border: `1px solid ${isActive ? k.accent : 'var(--border, #dde3ef)'}`,
              borderRadius: 8, padding: '12px 16px 0',
              display: 'flex', flexDirection: 'column', gap: 4, overflow: 'hidden',
              cursor: k.noClick ? 'default' : 'pointer',
              transition: 'border-color 0.15s',
            }}>
            <div style={{ fontSize: 9.5, fontWeight: 600, letterSpacing: '0.10em', textTransform: 'uppercase', color: 'var(--text-muted, #4a5e7a)', 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: 8 }}>
              {k.value}
            </div>
            <div style={{ height: 3, background: 'var(--bg-sunken, #e8edf5)', marginLeft: -16, marginRight: -16 }}>
              <div style={{ height: '100%', width: `${(k.fill || 0) * 100}%`, background: k.accent, transition: 'width 0.4s' }} />
            </div>
          </div>
        );
      })}
    </div>
  );
};

// ── Drawer detalhe de peça ─────────────────────────────────────────────────────
const PecaDrawer = ({ peca, onClose, onRefresh, userEmail }) => {
  const [pData, setPData] = React.useState(peca);
  const [lang, setLang] = React.useState('pt');
  const [promptEdit, setPromptEdit] = React.useState('');
  const [saving, setSaving] = React.useState('');
  const [toast, setToast] = React.useState('');

  React.useEffect(() => {
    setPData(peca);
    setPromptEdit(peca?.payload?.visual?.prompt_fundo || '');
  }, [peca?.id]);

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

  const doAction = async (label, fn) => {
    setSaving(label);
    try {
      const r = await fn();
      if (r?.error) { showToast(r.error); } else { showToast('Guardado'); onRefresh && onRefresh(); }
    } catch (e) { showToast(e.message); }
    setSaving('');
  };

  if (!pData) return null;
  const payload   = pData.payload || {};
  const idiomas   = payload.idiomas || ['pt'];
  const copy      = payload.copy_por_idioma?.[lang] || {};
  const overlay   = payload.overlay || {};
  const composedUrl = pData.composed_urls?.[lang]?.['1x1'];
  const isActive  = ['generating_img','composing_overlay','uploading_r2','regenerating'].includes(pData.status);
  const canApproveHere = pData.status === 'ready_review';

  return (
    <div style={{
      position: 'fixed', top: 0, right: 0, bottom: 0, width: 480,
      background: 'var(--bg-elev, #fff)', boxShadow: '-4px 0 24px rgba(17,41,84,0.12)',
      zIndex: 1000, display: 'flex', flexDirection: 'column', overflowY: 'hidden',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '16px 20px', borderBottom: '1px solid var(--border, #dde3ef)', flexShrink: 0 }}>
        <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 20, color: 'var(--text-muted)', lineHeight: 1 }}>×</button>
        <div style={{ flex: 1, fontSize: 14, fontWeight: 600, color: 'var(--text)' }}>
          {pData.canal_pai} {pData.canal_filho ? `/ ${pData.canal_filho}` : ''} · {pData.marca_slug}
        </div>
        <PecaStatusBadge status={pData.status} />
      </div>

      <div style={{ flex: 1, overflowY: 'auto', padding: '20px' }}>
        <div style={{ marginBottom: 20 }}>
          {composedUrl ? (
            <img src={composedUrl} alt="preview" style={{ width: '100%', borderRadius: 8, border: '1px solid var(--border, #dde3ef)' }} />
          ) : (
            <div style={{ width: '100%', aspectRatio: '1/1', background: 'var(--bg, #f8fafc)', borderRadius: 8, display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--text-dim)', fontSize: 13, border: '1px solid var(--border, #dde3ef)' }}>
              {isActive ? 'A gerar...' : 'Sem imagem'}
            </div>
          )}
        </div>

        {idiomas.length > 1 && (
          <div style={{ display: 'flex', gap: 8, marginBottom: 16 }}>
            {idiomas.map(l => (
              <button key={l} onClick={() => setLang(l)} style={{
                padding: '4px 14px', borderRadius: 99, fontSize: 12, fontWeight: 600, cursor: 'pointer',
                border: `1px solid ${lang === l ? 'var(--ai-500)' : 'var(--border)'}`,
                background: lang === l ? 'var(--ai-500)' : 'var(--bg-elev)',
                color: lang === l ? '#fff' : 'var(--text-muted)',
              }}>{l.toUpperCase()}</button>
            ))}
          </div>
        )}

        <div style={{ background: 'var(--bg, #f8fafc)', borderRadius: 8, padding: '14px 16px', marginBottom: 16, fontSize: 13 }}>
          {overlay.headline_imagem && <div style={{ marginBottom: 8 }}><span style={{ color: 'var(--text-dim)', fontSize: 11 }}>Headline</span><div style={{ fontWeight: 600, color: 'var(--text)' }}>{overlay.headline_imagem}</div></div>}
          {overlay.cta_imagem      && <div style={{ marginBottom: 8 }}><span style={{ color: 'var(--text-dim)', fontSize: 11 }}>CTA</span><div style={{ fontWeight: 600, color: 'var(--text)' }}>{overlay.cta_imagem}</div></div>}
          {copy.caption            && <div style={{ marginBottom: 8 }}><span style={{ color: 'var(--text-dim)', fontSize: 11 }}>Caption</span><div style={{ color: 'var(--text-muted)' }}>{copy.caption}</div></div>}
          {copy.hashtags           && <div><span style={{ color: 'var(--text-dim)', fontSize: 11 }}>Hashtags</span><div style={{ color: 'var(--ai-500)', fontFamily: 'var(--font-mono)' }}>{copy.hashtags}</div></div>}
        </div>

        {pData.scheduled_for && (
          <div style={{ fontSize: 12, color: 'var(--text-muted)', marginBottom: 16 }}>
            Agendada: {new Date(pData.scheduled_for).toLocaleDateString('pt-PT', { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' })}
            {payload.conta_publicacao && <> · <span style={{ color: 'var(--text)', fontWeight: 500 }}>{payload.conta_publicacao}</span></>}
          </div>
        )}

        <div style={{ marginBottom: 16 }}>
          <div style={{ fontSize: 11, color: 'var(--text-dim)', marginBottom: 6 }}>Prompt FLUX (editável)</div>
          <textarea
            value={promptEdit}
            onChange={e => setPromptEdit(e.target.value)}
            rows={4}
            style={{ width: '100%', padding: '10px 12px', borderRadius: 8, border: '1px solid var(--border, #dde3ef)', fontSize: 13, fontFamily: 'var(--font-mono)', resize: 'vertical', color: 'var(--text)', background: 'var(--bg-elev)' }}
          />
        </div>

        {pData.cost_total_usd && (
          <div style={{ background: 'color-mix(in srgb, var(--ai-500) 5%, transparent)', borderRadius: 8, padding: '12px 16px', marginBottom: 16, fontSize: 13 }}>
            <div style={{ fontWeight: 600, color: 'var(--text)', marginBottom: 6 }}>Custo: {fmtEur(pData.cost_total_usd, 4)}</div>
            {pData.cost_breakdown && Object.entries(pData.cost_breakdown).map(([k, v]) => (
              <div key={k} style={{ display: 'flex', justifyContent: 'space-between', color: 'var(--text-muted)', fontSize: 12 }}>
                <span>{k}</span><span>{fmtEur(v, 4)}</span>
              </div>
            ))}
          </div>
        )}

        <div style={{ fontSize: 12, color: 'var(--text-dim)', marginBottom: 8 }}>
          Tentativas: {pData.attempts || 0}
          {pData.completed_at && <> · Concluída: {new Date(pData.completed_at).toLocaleTimeString('pt-PT')}</>}
        </div>
        {pData.error_msg && (
          <div style={{ background: 'rgba(239,68,68,0.08)', borderRadius: 8, padding: '10px 14px', fontSize: 12, color: '#EF4444', marginBottom: 16 }}>
            Erro: {pData.error_msg}
          </div>
        )}
        {pData.blocked_reason && (
          <div style={{ background: 'rgba(249,115,22,0.08)', borderRadius: 8, padding: '10px 14px', fontSize: 12, color: '#F97316', marginBottom: 16 }}>
            Motivo: {pData.blocked_reason}
          </div>
        )}
      </div>

      <div style={{ borderTop: '1px solid var(--border, #dde3ef)', padding: '16px 20px', flexShrink: 0 }}>
        {toast && <div style={{ marginBottom: 10, fontSize: 12, color: '#10B981', fontWeight: 600 }}>{toast}</div>}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          {canApproveHere && (
            <button
              onClick={() => doAction('aprovar', () => ProdAPI.approve(pData.id, userEmail || 'fabio.costa@digidelta.pt'))}
              disabled={!!saving}
              className="btn"
              style={{ padding: '10px 0', background: '#10B981', color: '#fff', border: 'none', borderRadius: 8, fontWeight: 700, fontSize: 14, cursor: 'pointer' }}
            >
              {saving === 'aprovar' ? 'A aprovar...' : 'Aprovar para Activação'}
            </button>
          )}
          {pData.status === 'blocked' && (
            <button onClick={() => doAction('unblock', () => ProdAPI.unblock(pData.id))} disabled={!!saving}
              style={{ padding: '10px 0', background: '#F97316', color: '#fff', border: 'none', borderRadius: 8, fontWeight: 700, fontSize: 14, cursor: 'pointer' }}>
              {saving === 'unblock' ? '...' : 'Desbloquear'}
            </button>
          )}
          <button
            onClick={() => doAction('regenerar', () => ProdAPI.regenerate(pData.id, promptEdit !== (payload.visual?.prompt_fundo || '') ? promptEdit : undefined))}
            disabled={!!saving || isActive}
            style={{ padding: '10px 0', background: 'var(--bg-elev)', border: '1px solid var(--ai-500)', color: 'var(--ai-500)', borderRadius: 8, fontWeight: 600, fontSize: 13, cursor: 'pointer' }}
          >
            {saving === 'regenerar' ? 'A regenerar...' : 'Regenerar'}
          </button>
          {pData.status === 'ready_review' && (
            <button
              onClick={() => { const r = prompt('Motivo do bloqueio (opcional):'); doAction('block', () => ProdAPI.block(pData.id, r || '')); }}
              disabled={!!saving}
              style={{ padding: '8px 0', background: 'none', border: 'none', color: '#F97316', fontSize: 12, cursor: 'pointer', textAlign: 'left' }}
            >
              Marcar como problema
            </button>
          )}
        </div>
      </div>
    </div>
  );
};

// ── Sub-node (mini preview de uma etapa do pipeline) ──────────────────────────
const SubNode = ({ label, url, step, kind, active, badge }) => {
  const status = step?.status || 'pending';
  const isDone = status === 'done';
  const isFailed = status === 'failed';
  const SUB = 140;
  const bgStyle = url ? { backgroundImage: `url(${url})`, backgroundSize: 'cover', backgroundPosition: 'center' } : {};
  return (
    <div data-node-click style={{ width: SUB, flexShrink: 0, display: 'flex', flexDirection: 'column', gap: 4 }}>
      <div data-node-click style={{
        width: SUB, height: SUB - 8,
        borderRadius: 6, overflow: 'hidden',
        border: `1px solid ${isDone ? 'var(--border)' : (isFailed ? '#EF4444' : 'var(--border)')}`,
        background: kind === 'text' && url ? 'repeating-conic-gradient(#f3f4f6 0% 25%, #fff 0% 50%) 50% / 16px 16px' : 'var(--bg, #f8fafc)',
        position: 'relative',
        ...bgStyle,
      }}>
        {!url && (
          <div data-node-click style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--text-dim)', fontSize: 10 }}>
            {active ? <span style={{ animation: 'pulse 1.5s ease-in-out infinite' }}>...</span> : (isFailed ? 'falhou' : '—')}
          </div>
        )}
        {badge && url && (
          <div data-node-click style={{ position: 'absolute', top: 4, right: 4, background: 'rgba(56,89,208,0.92)', color: '#fff', fontSize: 8, padding: '2px 6px', borderRadius: 3, fontFamily: 'var(--font-mono)', fontWeight: 700, letterSpacing: '0.04em' }}>
            {badge}
          </div>
        )}
      </div>
      <div data-node-click style={{ display: 'flex', justifyContent: 'space-between', fontSize: 9, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)', textTransform: 'uppercase', letterSpacing: '0.04em' }}>
        <span>{label}</span>
        {step?.duration_ms && isDone && <span style={{ color: 'var(--text-dim)' }}>{(step.duration_ms/1000).toFixed(1)}s</span>}
      </div>
    </div>
  );
};

const Arrow = () => (
  <svg data-node-click width="20" height="14" viewBox="0 0 20 14" style={{ flexShrink: 0 }}>
    <path d="M 1 7 L 17 7 M 13 3 L 17 7 L 13 11" stroke="var(--text-dim, #6b7fa3)" strokeWidth="1.5" fill="none" strokeLinecap="round" strokeLinejoin="round" />
  </svg>
);

// ── Canvas dinâmico: pan + zoom + mini-mapa + swim lanes + sub-nodes ──────────
const ProducaoCanvas = ({ pecas, selected, onToggleSelect, onOpenPeca, brandColor }) => {
  const [zoom, setZoom]   = React.useState(1);
  const [pan, setPan]     = React.useState({ x: 0, y: 0 });
  const [dragging, setDragging] = React.useState(null);
  const wrapRef = React.useRef(null);

  // Agrupar peças por (copy_type → canal+canal_filho+idioma)
  // Performance (Anúncios Pagos) e Orgânico (Plano de Comunicação) ficam como super-lanes
  const groups = React.useMemo(() => {
    const perfMap = new Map();
    const orgMap  = new Map();
    const idiomaPrefer = (p) => {
      if (p.payload?.copy_por_idioma) return Object.keys(p.payload.copy_por_idioma);
      return p.payload?.idiomas || ['pt'];
    };
    (pecas || []).forEach(p => {
      const isPerf = (p.payload?.copy_type || '').toLowerCase() === 'performance';
      const target = isPerf ? perfMap : orgMap;
      const langs = idiomaPrefer(p);
      langs.forEach(lang => {
        const key = `${p.canal_pai}__${p.canal_filho || ''}__${lang}`;
        if (!target.has(key)) target.set(key, {
          key,
          canal: p.canal_pai,
          canal_filho: p.canal_filho,
          lang,
          pecas: [],
        });
        target.get(key).pecas.push({ ...p, _lang: lang });
      });
    });
    const out = [];
    if (perfMap.size) out.push({ id: 'performance', label: 'Performance · Anúncios Pagos', color: '#3859D0', lanes: [...perfMap.values()] });
    if (orgMap.size)  out.push({ id: 'organico',     label: 'Orgânico · Plano de Comunicação', color: '#10B981', lanes: [...orgMap.values()] });
    return out;
  }, [pecas]);

  // Layout: cada peça é um card largo com 3 sub-nodes (text + bg + composition)
  const NODE_W = 520;          // largura da peça (3 sub-nodes lado a lado + caption)
  const NODE_H = 340;          // altura: header + sub-nodes + caption + footer
  const NODE_GAP_X = 20;
  const LANE_LABEL_W = 130;    // largura coluna lane labels (reduzida, group header agora é full-width)
  const LANE_GAP_Y = 24;
  const LANE_HEADER_H = 22;
  const GROUP_HEADER_H = 48;
  const GROUP_GAP_Y = 36;

  const positions = React.useMemo(() => {
    const out = {};
    let y = 20;
    groups.forEach(group => {
      y += GROUP_HEADER_H + 14; // bar horizontal + gap
      group.lanes.forEach(lane => {
        const laneY = y;
        lane.pecas.forEach((p, idx) => {
          out[`${group.id}__${lane.key}__${p.id}`] = {
            x: LANE_LABEL_W + idx * (NODE_W + NODE_GAP_X),
            y: laneY,
            peca: p,
            lang: lane.lang,
            group: group.id,
          };
        });
        y = laneY + NODE_H + LANE_GAP_Y;
      });
      y += GROUP_GAP_Y;
    });
    return out;
  }, [groups]);

  // Calcular dimensões totais
  const allLanes = groups.flatMap(g => g.lanes);
  const maxLaneSize = Math.max(1, ...allLanes.map(l => l.pecas.length));
  const totalH = groups.reduce((s, g) => s + GROUP_HEADER_H + 14 + GROUP_GAP_Y + g.lanes.length * (NODE_H + LANE_GAP_Y), 0) + 60;
  const totalW = LANE_LABEL_W + (maxLaneSize * (NODE_W + NODE_GAP_X)) + 40;

  // Pan: mouse drag em background
  const onMouseDown = (e) => {
    if (e.target.dataset.nodeClick) return;
    setDragging({ startX: e.clientX, startY: e.clientY, panX: pan.x, panY: pan.y });
  };
  React.useEffect(() => {
    if (!dragging) return;
    const onMove = (e) => {
      setPan({ x: dragging.panX + (e.clientX - dragging.startX), y: dragging.panY + (e.clientY - dragging.startY) });
    };
    const onUp = () => setDragging(null);
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseup', onUp);
    return () => { window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp); };
  }, [dragging]);

  // Zoom: wheel
  const onWheel = (e) => {
    if (!e.ctrlKey && !e.metaKey) return;
    e.preventDefault();
    const delta = -e.deltaY / 500;
    setZoom(z => Math.max(0.3, Math.min(2, z + delta)));
  };

  React.useEffect(() => {
    const el = wrapRef.current;
    if (!el) return;
    const handler = (e) => onWheel(e);
    el.addEventListener('wheel', handler, { passive: false });
    return () => el.removeEventListener('wheel', handler);
  }, []);

  const resetView = () => { setPan({ x: 0, y: 0 }); setZoom(1); };
  const zoomIn  = () => setZoom(z => Math.min(2, z + 0.15));
  const zoomOut = () => setZoom(z => Math.max(0.3, z - 0.15));
  const fitToScreen = () => {
    const el = wrapRef.current;
    if (!el) return;
    const r = el.getBoundingClientRect();
    const sx = (r.width - 40) / totalW;
    const sy = (r.height - 40) / totalH;
    const z = Math.max(0.3, Math.min(1, Math.min(sx, sy)));
    setZoom(z);
    setPan({ x: 20, y: 20 });
  };

  return (
    <div ref={wrapRef}
      onMouseDown={onMouseDown}
      style={{
        position: 'relative', flex: 1, minHeight: 0,
        overflow: 'hidden', cursor: dragging ? 'grabbing' : 'grab',
        background: 'var(--bg, #f8fafc)',
        backgroundImage: 'radial-gradient(circle, color-mix(in srgb, var(--border) 60%, transparent) 1px, transparent 1px)',
        backgroundSize: `${20 * zoom}px ${20 * zoom}px`,
        backgroundPosition: `${pan.x}px ${pan.y}px`,
        borderRadius: 12, border: '1px solid var(--border, #dde3ef)',
      }}>

      {/* Canvas inner — transformado */}
      <div style={{
        position: 'absolute', top: 0, left: 0,
        transform: `translate(${pan.x}px, ${pan.y}px) scale(${zoom})`,
        transformOrigin: '0 0',
        pointerEvents: dragging ? 'none' : 'auto',
      }}>
        {/* Group headers + Lane labels + backgrounds */}
        {(() => {
          const out = [];
          let y = 20;
          // Largura total para group headers (até ao fim da última peça do canvas)
          const groupBarWidth = totalW - 40;
          groups.forEach(group => {
            const totalPecas = group.lanes.reduce((s, l) => s + l.pecas.length, 0);
            // Group header — barra horizontal full-width
            out.push(
              <div key={`grp-${group.id}`} style={{
                position: 'absolute',
                left: 0, top: y,
                width: groupBarWidth, height: GROUP_HEADER_H,
                display: 'flex', alignItems: 'center', gap: 12,
                padding: '0 18px',
                background: `color-mix(in srgb, ${group.color} 7%, var(--bg-elev, #fff))`,
                border: `1px solid color-mix(in srgb, ${group.color} 22%, transparent)`,
                borderLeft: `4px solid ${group.color}`,
                borderRadius: 10,
                fontSize: 13, fontWeight: 700, letterSpacing: '0.05em', textTransform: 'uppercase',
                fontFamily: 'var(--font-mono, monospace)', color: group.color,
                whiteSpace: 'nowrap',
                boxShadow: '0 1px 2px rgba(17,41,84,0.04)',
              }}>
                {group.label}
                <span style={{
                  background: `color-mix(in srgb, ${group.color} 18%, transparent)`,
                  color: group.color, padding: '3px 10px', borderRadius: 99, fontSize: 11,
                }}>
                  {totalPecas} {totalPecas === 1 ? 'peça' : 'peças'}
                </span>
                <span style={{ flex: 1 }} />
                <span style={{ fontSize: 10, color: 'var(--text-muted)', fontWeight: 500, letterSpacing: '0.02em', textTransform: 'none' }}>
                  {group.id === 'performance' ? 'Anúncios pagos · A/B' : 'Plano editorial · multi-canal'}
                </span>
              </div>
            );
            y += GROUP_HEADER_H + 14;

            group.lanes.forEach(lane => {
              const ly = y;
              // Lane label
              out.push(
                <div key={`lab-${group.id}-${lane.key}`} style={{
                  position: 'absolute',
                  left: 4, top: ly + NODE_H / 2 - 24,
                  width: LANE_LABEL_W - 14,
                  display: 'flex', flexDirection: 'column', gap: 6,
                  fontFamily: 'var(--font-mono, monospace)',
                }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                    <span style={{ width: 3, height: 14, background: group.color, borderRadius: 2, flexShrink: 0 }} />
                    <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--text)', textTransform: 'uppercase', letterSpacing: '0.04em' }}>
                      {(lane.canal_filho || lane.canal).replace(/_/g, ' ')}
                    </span>
                  </div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginLeft: 9 }}>
                    <span style={{ background: 'var(--bg-sunken)', color: 'var(--text-muted)', padding: '1px 6px', borderRadius: 3, fontSize: 9, fontWeight: 600, letterSpacing: '0.04em' }}>
                      {lane.lang.toUpperCase()}
                    </span>
                    <span style={{ fontSize: 9, color: 'var(--text-dim)', fontWeight: 500 }}>
                      {lane.pecas.length}
                    </span>
                  </div>
                </div>
              );
              // Lane background sutil
              const laneW = lane.pecas.length * (NODE_W + NODE_GAP_X);
              out.push(
                <div key={`bg-${group.id}-${lane.key}`} style={{
                  position: 'absolute',
                  left: LANE_LABEL_W - 4, top: ly - 4,
                  width: laneW + 8, height: NODE_H + 8,
                  background: `color-mix(in srgb, ${group.color} 3%, transparent)`,
                  borderRadius: 6,
                  pointerEvents: 'none',
                }} />
              );
              y = ly + NODE_H + LANE_GAP_Y;
            });
            y += GROUP_GAP_Y;
          });
          return out;
        })()}

        {/* Nodes — cada peça com 3 sub-nodes (text + bg + composition) + caption */}
        {Object.entries(positions).map(([k, pos]) => {
          const p = pos.peca;
          const lang = pos.lang;
          const composedUrl = p.composed_urls?.[lang]?.['1x1'];
          const textUrl     = p.text_layer_url; // shared across languages no nosso modelo (PT)
          const bgUrl       = p.flux_raw_url;
          const steps       = p.pipeline_steps || {};
          const langSteps   = {
            text: steps?.text?.per_lang?.[lang] || steps?.text || { status: 'pending' },
            bg: steps?.bg || { status: 'pending' },
            composition: steps?.composition?.per_lang?.[lang] || steps?.composition || { status: 'pending' },
          };
          const copyLang  = p.payload?.copy_por_idioma?.[lang] || {};
          const overlayCfg = p.payload?.overlay || {};
          const headline = overlayCfg.headline_imagem || copyLang.copy_imagem || copyLang.headline || '';
          const caption  = copyLang.caption || copyLang.body || '';
          const isActive = ['generating_img','composing_overlay','uploading_r2','regenerating'].includes(p.status);
          const isSel = selected.includes(p.id);
          const isDeferred = p.status === 'deferred';

          const SUB_W = 150;
          const SUB_H = 150;

          return (
            <div key={k} data-node-click
              onClick={() => onOpenPeca(p)}
              style={{
                position: 'absolute', left: pos.x, top: pos.y,
                width: NODE_W, height: NODE_H,
                background: 'var(--bg-elev, #fff)',
                border: `1.5px solid ${isSel ? 'var(--ai-500)' : (isDeferred ? 'var(--border)' : 'var(--border, #dde3ef)')}`,
                borderRadius: 10, overflow: 'hidden', cursor: 'pointer',
                boxShadow: isSel ? '0 4px 16px rgba(56,89,208,0.18)' : '0 1px 3px rgba(17,41,84,0.05)',
                opacity: isDeferred ? 0.5 : 1,
                transition: 'border-color .15s, box-shadow .15s',
                display: 'flex', flexDirection: 'column',
              }}>
              {/* Header com headline + status */}
              <div data-node-click style={{ padding: '8px 12px', display: 'flex', alignItems: 'center', gap: 8, borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
                {p.status === 'ready_review' && (
                  <div data-node-click
                    onClick={e => { e.stopPropagation(); onToggleSelect(p.id); }}
                    style={{ width: 16, height: 16, borderRadius: 3, background: isSel ? 'var(--ai-500)' : '#fff', border: `2px solid ${isSel ? 'var(--ai-500)' : 'var(--border)'}`, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}
                  >
                    {isSel && <span style={{ color: '#fff', fontSize: 10, lineHeight: 1, fontWeight: 700 }}>✓</span>}
                  </div>
                )}
                <div data-node-click style={{ flex: 1, minWidth: 0, fontSize: 12, fontWeight: 600, color: 'var(--text)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }} title={headline}>
                  {headline || '(sem headline)'}
                </div>
                <PecaStatusBadge status={p.status} />
              </div>

              {/* 3 sub-nodes lado a lado */}
              <div data-node-click style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '10px 8px 6px', gap: 4, flexShrink: 0 }}>
                <SubNode label="Texto"     url={textUrl}     step={langSteps.text}        kind="text"  active={isActive && !textUrl} />
                <Arrow />
                <SubNode label="Fundo"     url={bgUrl}       step={langSteps.bg}          kind="bg"    active={isActive && !bgUrl} badge={steps?.bg?.model === 'nano-banana' ? 'GEMINI' : null} />
                <Arrow />
                <SubNode label="Composição" url={composedUrl} step={langSteps.composition} kind="comp" active={isActive && !composedUrl} />
              </div>

              {/* Caption */}
              <div data-node-click style={{ padding: '4px 12px 8px', fontSize: 11, color: 'var(--text-muted)', lineHeight: 1.4, flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical' }}>
                {caption || (isDeferred ? 'Aguarda fase 2 (vídeo/email)' : 'Aguarda copy')}
              </div>

              {/* Footer com custo */}
              {p.cost_total_usd && (
                <div data-node-click style={{ padding: '4px 12px 8px', fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', borderTop: '1px dashed var(--border)' }}>
                  {fmtEur(p.cost_total_usd, 4)}
                  {steps?.bg?.duration_ms && <> · {(steps.bg.duration_ms/1000).toFixed(1)}s</>}
                </div>
              )}
            </div>
          );
        })}
      </div>

      {/* Toolbar canvas */}
      <div style={{
        position: 'absolute', top: 12, right: 12, zIndex: 10,
        display: 'flex', gap: 4,
        background: 'var(--bg-elev, #fff)',
        border: '1px solid var(--border, #dde3ef)',
        borderRadius: 8, padding: 3,
        boxShadow: '0 2px 8px rgba(17,41,84,0.08)',
      }}>
        <button onClick={zoomOut} title="Zoom out" style={canvasBtnStyle}>−</button>
        <button onClick={() => setZoom(1)} title="100%" style={{ ...canvasBtnStyle, minWidth: 50, fontFamily: 'var(--font-mono)', fontSize: 11 }}>{Math.round(zoom * 100)}%</button>
        <button onClick={zoomIn}  title="Zoom in" style={canvasBtnStyle}>+</button>
        <div style={{ width: 1, background: 'var(--border)' }} />
        <button onClick={fitToScreen} title="Ajustar à janela" style={canvasBtnStyle}>⤢</button>
        <button onClick={resetView}   title="Reset" style={canvasBtnStyle}>↺</button>
      </div>

      {/* Mini-mapa */}
      {allLanes.length > 1 && (
        <div style={{
          position: 'absolute', bottom: 12, right: 12, zIndex: 10,
          width: 140, height: 90,
          background: 'var(--bg-elev, #fff)', border: '1px solid var(--border, #dde3ef)',
          borderRadius: 6, overflow: 'hidden', pointerEvents: 'none',
        }}>
          <div style={{ position: 'relative', width: '100%', height: '100%' }}>
            {/* Lanes em mini */}
            {(() => {
              const sx = 140 / totalW;
              const sy = 90 / totalH;
              const s = Math.min(sx, sy);
              return Object.entries(positions).map(([k, pos]) => {
                const p = pos.peca;
                const cfg = PROD_STATUS_CFG[p.status] || PROD_STATUS_CFG.pending;
                return (
                  <div key={`mm-${k}`} style={{
                    position: 'absolute',
                    left: pos.x * s, top: pos.y * s,
                    width: NODE_W * s, height: NODE_H * s,
                    background: cfg.color, opacity: 0.6, borderRadius: 1,
                  }} />
                );
              });
            })()}
            {/* Viewport indicator */}
            {(() => {
              const el = wrapRef.current;
              if (!el) return null;
              const r = el.getBoundingClientRect();
              const s = Math.min(140 / totalW, 90 / totalH);
              const vw = (r.width / zoom) * s;
              const vh = (r.height / zoom) * s;
              const vx = (-pan.x / zoom) * s;
              const vy = (-pan.y / zoom) * s;
              return (
                <div style={{
                  position: 'absolute',
                  left: Math.max(0, vx), top: Math.max(0, vy),
                  width: Math.min(140 - Math.max(0, vx), vw),
                  height: Math.min(90 - Math.max(0, vy), vh),
                  border: '1.5px solid var(--ai-500, #3859D0)',
                  background: 'color-mix(in srgb, var(--ai-500) 8%, transparent)',
                  borderRadius: 2,
                }} />
              );
            })()}
          </div>
        </div>
      )}

      {/* Hint de zoom (primeiros segundos) */}
      <div style={{
        position: 'absolute', bottom: 12, left: 12, zIndex: 10,
        fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)',
        background: 'var(--bg-elev, #fff)', padding: '4px 8px',
        border: '1px solid var(--border)', borderRadius: 6,
        opacity: 0.85,
      }}>
        Cmd/Ctrl + scroll para zoom · arrastar para pan
      </div>
    </div>
  );
};

const canvasBtnStyle = {
  minWidth: 28, height: 26, padding: '0 8px',
  background: 'transparent', border: 'none', borderRadius: 5,
  color: 'var(--text-muted, #4a5e7a)', fontSize: 14, fontWeight: 600,
  cursor: 'pointer', fontFamily: 'inherit',
  display: 'flex', alignItems: 'center', justifyContent: 'center',
};

// ── Vista dedicada da peça (substitui drawer) ─────────────────────────────────
const LargePreview = ({ label, url, step, kind, active, badge }) => {
  const status = step?.status || 'pending';
  const isDone = status === 'done';
  const isFailed = status === 'failed';
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
      <div style={{
        position: 'relative',
        aspectRatio: '1/1', width: '100%',
        background: kind === 'text' && url ? 'repeating-conic-gradient(#f3f4f6 0% 25%, #fff 0% 50%) 50% / 24px 24px' : 'var(--bg, #f8fafc)',
        backgroundImage: url ? `url(${url})` : undefined,
        backgroundSize: 'cover', backgroundPosition: 'center',
        borderRadius: 12,
        border: `1px solid ${isFailed ? '#EF4444' : 'var(--border)'}`,
        overflow: 'hidden',
      }}>
        {!url && (
          <div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--text-dim)', fontSize: 13 }}>
            {active ? <span style={{ animation: 'pulse 1.5s ease-in-out infinite' }}>A gerar...</span> : (isFailed ? 'Falhou' : '—')}
          </div>
        )}
        {badge && url && (
          <div style={{ position: 'absolute', top: 10, right: 10, background: 'rgba(56,89,208,0.95)', color: '#fff', fontSize: 10, padding: '4px 10px', borderRadius: 4, fontFamily: 'var(--font-mono)', fontWeight: 700, letterSpacing: '0.06em' }}>
            {badge}
          </div>
        )}
      </div>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', fontFamily: 'var(--font-mono, monospace)' }}>
        <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--text)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>{label}</span>
        <span style={{ fontSize: 10, color: 'var(--text-dim)' }}>
          {step?.duration_ms && isDone ? `${(step.duration_ms/1000).toFixed(1)}s` : (status === 'generating' ? 'a gerar...' : (status === 'failed' ? 'erro' : 'aguarda'))}
        </span>
      </div>
    </div>
  );
};

const PecaView = ({ pecaId, campId, brandSlug, brandName, brandColor, campTitulo, userEmail, onBack }) => {
  const [pData, setPData]     = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [lang, setLang]       = React.useState('pt');
  const [promptEdit, setPromptEdit] = React.useState('');
  const [saving, setSaving]   = React.useState('');
  const [toast, setToast]     = React.useState('');

  const load = React.useCallback(async () => {
    try { const d = await ProdAPI.peca(pecaId); setPData(d); setPromptEdit(d?.payload?.visual?.prompt_fundo || ''); } catch {}
    setLoading(false);
  }, [pecaId]);

  React.useEffect(() => {
    load();
    // SSE para actualizações em tempo real
    const es = new EventSource(`/api/marketing/producao/campanha/${campId}/stream`);
    es.onmessage = e => {
      try { const ev = JSON.parse(e.data); if (ev.type === 'peca_updated' && ev.peca_id === pecaId) load(); } catch {}
    };
    return () => es.close();
  }, [pecaId, campId]);

  const showToast = (msg) => { setToast(msg); setTimeout(() => setToast(''), 2500); };
  const doAction = async (label, fn) => {
    setSaving(label);
    try { const r = await fn(); if (r?.error) showToast(r.error); else { showToast('Guardado'); load(); } }
    catch (e) { showToast(e.message); }
    setSaving('');
  };

  if (loading) return (
    <div data-theme="light" style={{ height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--text-muted)', fontSize: 14 }}>A carregar peça...</div>
  );
  if (!pData) return (
    <div data-theme="light" style={{ height: '100%', padding: 40 }}>
      <button onClick={onBack} className="btn">← Voltar</button>
      <div style={{ marginTop: 20, color: '#EF4444' }}>Peça não encontrada.</div>
    </div>
  );

  const payload     = pData.payload || {};
  const visual      = payload.visual || {};
  const idiomas     = payload.copy_por_idioma ? Object.keys(payload.copy_por_idioma) : ['pt'];
  const overlayCfg  = payload.overlay || {};
  const copyLang    = payload.copy_por_idioma?.[lang] || {};
  const headline    = overlayCfg.headline_imagem || copyLang.copy_imagem || copyLang.headline || '(sem headline)';
  const ctaText     = overlayCfg.cta_imagem || copyLang.cta || '';
  const composedUrl = pData.composed_urls?.[lang]?.['1x1'];
  const bgUrl       = pData.flux_raw_url;
  const textUrl     = pData.text_layer_url;
  const steps       = pData.pipeline_steps || {};
  const langSteps   = {
    text: steps?.text?.per_lang?.[lang] || steps?.text || { status: 'pending' },
    bg: steps?.bg || { status: 'pending' },
    composition: steps?.composition?.per_lang?.[lang] || steps?.composition || { status: 'pending' },
  };
  const refImage  = (visual.imagens_referencia || [])[0] || null;
  const isActive  = ['generating_img','composing_overlay','uploading_r2','regenerating'].includes(pData.status);
  const canApprove = pData.status === 'ready_review';
  const isDeferred = pData.status === 'deferred';

  return (
    <div data-theme="light" style={{
      height: '100%', display: 'flex', flexDirection: 'column', minHeight: 0,
      '--bg': '#f8fafc', '--bg-elev': '#ffffff', '--bg-hover': '#f1f5f9', '--bg-sunken': '#e8edf5',
      '--border': '#dde3ef', '--text': '#112954', '--text-muted': '#4a5e7a', '--text-dim': '#6b7fa3',
      '--ai-500': '#3859D0',
    }}>
      <style>{`@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.5} }`}</style>

      {/* Header — padrão Briefings detail */}
      <div style={{ background: 'var(--bg-elev)', borderBottom: '1px solid var(--border)', 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 · PRODUÇÃO · {(brandSlug || '').toUpperCase()} · {campTitulo}
        </div>

        {/* Title + actions */}
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16 }}>
          <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, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: '55vw' }} title={headline}>
              {headline}
            </h1>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6, flexWrap: 'wrap' }}>
              <PecaStatusBadge status={pData.status} />
              {/* Brand pill */}
              <span style={{ fontSize: 11, fontWeight: 600, padding: '2px 8px', borderRadius: 99, background: brandColor + '18', color: brandColor, border: `1px solid ${brandColor}30`, fontFamily: 'var(--font-body)' }}>
                {brandName || brandSlug}
              </span>
              {/* Channel pill */}
              <span style={{ fontSize: 11, fontWeight: 600, padding: '2px 8px', borderRadius: 99, background: 'var(--bg-sunken)', color: 'var(--text-muted)', fontFamily: 'var(--font-mono)', textTransform: 'uppercase', letterSpacing: '0.04em' }}>
                {pData.canal_pai}{pData.canal_filho ? ` · ${pData.canal_filho}` : ''}
              </span>
              {/* Meta */}
              <span style={{ fontSize: 11, color: 'var(--text-muted)', fontFamily: 'var(--font-body)' }}>
                {pData.cost_total_usd && <><span style={{ fontFamily: 'var(--font-mono)' }}>{fmtEur(pData.cost_total_usd, 4)}</span> · </>}
                tent. {pData.attempts || 0}
                {pData.completed_at && <> · {new Date(pData.completed_at).toLocaleTimeString('pt-PT', { hour: '2-digit', minute: '2-digit' })}</>}
              </span>
            </div>
          </div>
          {/* Right-side actions */}
          <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexShrink: 0, marginTop: 2 }}>
            {toast && <span style={{ fontSize: 12, color: '#10B981', fontWeight: 600, marginRight: 4 }}>{toast}</span>}
            <button onClick={onBack} className="btn" style={{ height: 30, padding: '0 12px', fontSize: 12 }}>← Voltar</button>
            <button onClick={() => doAction('regenerar', () => ProdAPI.regenerate(pData.id, promptEdit !== (visual.prompt_fundo || '') ? promptEdit : undefined))} disabled={!!saving || isActive || isDeferred}
              style={{ height: 30, padding: '0 14px', background: 'var(--bg-elev)', border: '1px solid var(--ai-500)', color: 'var(--ai-500)', borderRadius: 6, fontWeight: 600, fontSize: 12, cursor: 'pointer' }}>
              {saving === 'regenerar' ? 'A regenerar...' : 'Regenerar'}
            </button>
            {pData.status === 'ready_review' && (
              <button onClick={() => { const r = prompt('Motivo do bloqueio (opcional):'); if (r !== null) doAction('block', () => ProdAPI.block(pData.id, r || '')); }} disabled={!!saving}
                style={{ height: 30, padding: '0 12px', background: 'none', border: '1px solid rgba(249,115,22,0.5)', color: '#F97316', borderRadius: 6, fontSize: 12, cursor: 'pointer', fontWeight: 500 }}>
                Marcar problema
              </button>
            )}
            {pData.status === 'blocked' && (
              <button onClick={() => doAction('unblock', () => ProdAPI.unblock(pData.id))} disabled={!!saving}
                style={{ height: 30, padding: '0 14px', background: '#F97316', color: '#fff', border: 'none', borderRadius: 6, fontWeight: 700, fontSize: 12, cursor: 'pointer' }}>
                {saving === 'unblock' ? '...' : 'Desbloquear'}
              </button>
            )}
            {canApprove && (
              <button onClick={() => doAction('aprovar', () => ProdAPI.approve(pData.id, userEmail || 'fabio.costa@digidelta.pt'))} disabled={!!saving}
                style={{ height: 30, padding: '0 16px', background: '#10B981', color: '#fff', border: 'none', borderRadius: 6, fontWeight: 700, fontSize: 12, cursor: 'pointer' }}>
                {saving === 'aprovar' ? 'A aprovar...' : 'Aprovar para Activação'}
              </button>
            )}
          </div>
        </div>

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

        {/* Stepper pipeline da peça (4 passos) */}
        {(() => {
          const stepDefs = [
            { id: 'text',        label: 'Texto',       step: langSteps.text },
            { id: 'bg',          label: 'Fundo',       step: langSteps.bg },
            { id: 'composition', label: 'Composição',  step: langSteps.composition },
            { id: 'aprovacao',   label: 'Aprovação',   step: { status: ['approved','pending_activation'].includes(pData.status) ? 'done' : (pData.status === 'ready_review' ? 'generating' : 'pending') } },
          ];
          return (
            <div style={{ display: 'flex', alignItems: 'flex-start', overflowX: 'auto' }}>
              {stepDefs.map((step, i) => {
                const status = step.step?.status || 'pending';
                const isCompleted = status === 'done';
                const isActiveStep = status === 'generating';
                const isFailed = status === 'failed';
                const circleBg    = isCompleted ? 'rgba(21,128,61,.1)' : isFailed ? 'rgba(239,68,68,.1)' : isActiveStep ? 'var(--ai-500)' : 'transparent';
                const circleBdr   = isCompleted ? '1.5px solid rgba(21,128,61,.3)' : isFailed ? '1.5px solid rgba(239,68,68,.3)' : isActiveStep ? 'none' : '1.5px solid #e2e8f0';
                const circleText  = isCompleted ? '✓' : isFailed ? '!' : String(i + 1);
                const circleColor = isCompleted ? '#15803d' : isFailed ? '#EF4444' : isActiveStep ? '#fff' : '#94a3b8';
                const labelColor  = isActiveStep ? 'var(--text)' : isCompleted ? 'var(--text-muted)' : '#94a3b8';
                const sub = isCompleted && step.step?.duration_ms ? `${(step.step.duration_ms/1000).toFixed(1)}s` : isActiveStep ? 'a gerar...' : isFailed ? 'falhou' : 'aguarda';
                return (
                  <React.Fragment key={step.id}>
                    <div style={{ display: 'flex', alignItems: 'flex-start', gap: 8, padding: '10px 16px 12px', borderBottom: `2px solid ${isActiveStep ? 'var(--ai-500)' : 'transparent'}`, flexShrink: 0 }}>
                      <div style={{ width: 20, height: 20, borderRadius: '50%', flexShrink: 0, marginTop: 1, background: circleBg, border: circleBdr, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                        <span style={{ fontSize: 10, fontWeight: 700, color: circleColor, fontFamily: 'var(--font-body)', lineHeight: 1 }}>{circleText}</span>
                      </div>
                      <div style={{ display: 'flex', flexDirection: 'column', gap: 1, textAlign: 'left' }}>
                        <span style={{ fontSize: 13, fontWeight: 600, color: labelColor, fontFamily: 'var(--font-display)', lineHeight: 1.2 }}>{`${i + 1}. ${step.label}`}</span>
                        <span style={{ fontSize: 10.5, color: '#94a3b8', fontFamily: 'var(--font-body)' }}>{sub}</span>
                      </div>
                    </div>
                    {i < stepDefs.length - 1 && (
                      <div style={{ width: 20, height: 1, flexShrink: 0, alignSelf: 'flex-start', marginTop: 20, background: isCompleted ? 'rgba(21,128,61,.25)' : '#e2e8f0' }} />
                    )}
                  </React.Fragment>
                );
              })}
            </div>
          );
        })()}
      </div>

      {/* Body */}
      <div className="scrollbar" style={{ flex: 1, minHeight: 0, overflowY: 'auto', padding: '32px 40px', background: 'var(--bg)' }}>

        {/* Banners contextuais */}
        {pData.status === 'failed' && (
          <div style={{ marginBottom: 24, padding: 14, borderRadius: 8, background: 'rgba(239,68,68,0.08)', border: '1px solid rgba(239,68,68,0.25)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>
            <div>
              <div style={{ fontWeight: 600, color: '#EF4444', fontSize: 13, marginBottom: 2 }}>Esta peça falhou a geração</div>
              <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>{pData.error_msg || 'Erro desconhecido — clica em Regenerar para tentar de novo.'}</div>
            </div>
            <button onClick={() => doAction('regenerar', () => ProdAPI.regenerate(pData.id))} disabled={!!saving} className="btn btn-ai" style={{ height: 28, padding: '0 12px', fontSize: 12 }}>
              {saving === 'regenerar' ? '...' : 'Regenerar'}
            </button>
          </div>
        )}
        {pData.status === 'blocked' && (
          <div style={{ marginBottom: 24, padding: 14, borderRadius: 8, background: 'rgba(249,115,22,0.08)', border: '1px solid rgba(249,115,22,0.25)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>
            <div>
              <div style={{ fontWeight: 600, color: '#F97316', fontSize: 13, marginBottom: 2 }}>Peça bloqueada</div>
              <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>{pData.blocked_reason || 'Marcada como problema para revisão posterior.'}</div>
            </div>
            <button onClick={() => doAction('unblock', () => ProdAPI.unblock(pData.id))} disabled={!!saving} style={{ height: 28, padding: '0 12px', fontSize: 12, background: '#F97316', color: '#fff', border: 'none', borderRadius: 6, fontWeight: 600, cursor: 'pointer' }}>
              {saving === 'unblock' ? '...' : 'Desbloquear'}
            </button>
          </div>
        )}
        {isDeferred && (
          <div style={{ marginBottom: 24, padding: 14, borderRadius: 8, background: 'rgba(139,146,168,0.10)', border: '1px solid rgba(139,146,168,0.25)' }}>
            <div style={{ fontWeight: 600, color: 'var(--text-muted)', fontSize: 13, marginBottom: 2 }}>Aguarda Fase 2</div>
            <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>Email e vídeo são processados numa fase posterior do roadmap. Esta peça não é gerada agora.</div>
          </div>
        )}
        {canApprove && (
          <div style={{ marginBottom: 24, padding: 14, borderRadius: 8, background: 'color-mix(in srgb, var(--ai-500) 6%, transparent)', border: '1px solid color-mix(in srgb, var(--ai-500) 25%, transparent)' }}>
            <div style={{ fontWeight: 600, color: 'var(--ai-500)', fontSize: 13, marginBottom: 2 }}>Peça pronta para revisão</div>
            <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>Vê as 3 etapas abaixo e clica em "Aprovar para Activação" se estiver tudo conforme.</div>
          </div>
        )}

        {/* 3 previews grandes */}
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 24, marginBottom: 32, maxWidth: 1400 }}>
          <LargePreview label="1 · Texto"      url={textUrl}     step={langSteps.text}        kind="text" active={isActive && !textUrl} />
          <LargePreview label="2 · Fundo"      url={bgUrl}       step={langSteps.bg}          kind="bg"   active={isActive && !bgUrl} badge={steps?.bg?.model === 'nano-banana' ? 'GEMINI' : null} />
          <LargePreview label="3 · Composição" url={composedUrl} step={langSteps.composition} kind="comp" active={isActive && !composedUrl} />
        </div>

        {/* Toggle idiomas */}
        {idiomas.length > 1 && (
          <div style={{ display: 'flex', gap: 8, marginBottom: 20 }}>
            {idiomas.map(l => (
              <button key={l} onClick={() => setLang(l)} style={{
                padding: '6px 18px', borderRadius: 99, fontSize: 12, fontWeight: 700, cursor: 'pointer',
                border: `1px solid ${lang === l ? 'var(--ai-500)' : 'var(--border)'}`,
                background: lang === l ? 'var(--ai-500)' : 'var(--bg-elev)',
                color: lang === l ? '#fff' : 'var(--text-muted)',
                fontFamily: 'var(--font-mono)', letterSpacing: '0.04em',
              }}>{l.toUpperCase()}</button>
            ))}
          </div>
        )}

        {/* Painel duplo: Copy + Prompt/Meta */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 24, maxWidth: 1400 }}>

          {/* Copy/Caption */}
          <div style={{ background: 'var(--bg-elev)', border: '1px solid var(--border)', borderRadius: 12, padding: '20px 22px' }}>
            <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--text-muted)', fontFamily: 'var(--font-mono)', marginBottom: 14 }}>
              Copy · {lang.toUpperCase()}
            </div>
            {overlayCfg.headline_imagem && (
              <div style={{ marginBottom: 14 }}>
                <div style={{ fontSize: 10, color: 'var(--text-dim)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 4 }}>Headline (imagem)</div>
                <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--text)', lineHeight: 1.3 }}>{overlayCfg.headline_imagem}</div>
              </div>
            )}
            {ctaText && (
              <div style={{ marginBottom: 14 }}>
                <div style={{ fontSize: 10, color: 'var(--text-dim)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 4 }}>CTA</div>
                <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--text)' }}>{ctaText}</div>
              </div>
            )}
            {(copyLang.caption || copyLang.body) && (
              <div style={{ marginBottom: 14 }}>
                <div style={{ fontSize: 10, color: 'var(--text-dim)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 4 }}>Caption</div>
                <div style={{ fontSize: 13, color: 'var(--text-muted)', lineHeight: 1.55, whiteSpace: 'pre-wrap' }}>{copyLang.caption || copyLang.body}</div>
              </div>
            )}
            {copyLang.hashtags && (
              <div>
                <div style={{ fontSize: 10, color: 'var(--text-dim)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 4 }}>Hashtags</div>
                <div style={{ fontSize: 12, color: 'var(--ai-500)', fontFamily: 'var(--font-mono)', wordBreak: 'break-word' }}>{copyLang.hashtags}</div>
              </div>
            )}
          </div>

          {/* Prompt + Meta */}
          <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>

            {/* Imagem referência */}
            {refImage && (
              <div style={{ background: 'var(--bg-elev)', border: '1px solid var(--border)', borderRadius: 12, padding: '14px 18px' }}>
                <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--text-muted)', fontFamily: 'var(--font-mono)', marginBottom: 10 }}>
                  Imagem referência (FLUX/Gemini input)
                </div>
                <img src={refImage} alt="referência" style={{ maxWidth: '100%', maxHeight: 140, borderRadius: 6, border: '1px solid var(--border)', background: '#fff' }} />
              </div>
            )}

            {/* Prompt FLUX */}
            <div style={{ background: 'var(--bg-elev)', border: '1px solid var(--border)', borderRadius: 12, padding: '14px 18px' }}>
              <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--text-muted)', fontFamily: 'var(--font-mono)', marginBottom: 10 }}>
                Prompt {steps?.bg?.model === 'nano-banana' ? 'Gemini' : 'FLUX'} (editável)
              </div>
              <textarea value={promptEdit} onChange={e => setPromptEdit(e.target.value)} rows={6}
                style={{ width: '100%', padding: '10px 12px', borderRadius: 8, border: '1px solid var(--border)', fontSize: 12, fontFamily: 'var(--font-mono)', resize: 'vertical', color: 'var(--text)', background: 'var(--bg-elev)', lineHeight: 1.5 }}
              />
            </div>

            {/* Meta */}
            <div style={{ background: 'var(--bg-elev)', border: '1px solid var(--border)', borderRadius: 12, padding: '14px 18px', display: 'flex', flexDirection: 'column', gap: 8, fontSize: 12 }}>
              {pData.scheduled_for && (
                <div style={{ display: 'flex', justifyContent: 'space-between', color: 'var(--text-muted)' }}>
                  <span>Agendada:</span>
                  <span style={{ color: 'var(--text)', fontWeight: 500 }}>{new Date(pData.scheduled_for).toLocaleDateString('pt-PT', { day: 'numeric', month: 'short', year: 'numeric' })}</span>
                </div>
              )}
              {payload.conta_publicacao && (
                <div style={{ display: 'flex', justifyContent: 'space-between', color: 'var(--text-muted)' }}>
                  <span>Conta:</span>
                  <span style={{ color: 'var(--text)', fontWeight: 500 }}>{payload.conta_publicacao}</span>
                </div>
              )}
              {pData.cost_total_usd && (
                <div style={{ display: 'flex', justifyContent: 'space-between', color: 'var(--text-muted)' }}>
                  <span>Custo total:</span>
                  <span style={{ color: 'var(--text)', fontWeight: 600, fontFamily: 'var(--font-mono)' }}>{fmtEur(pData.cost_total_usd, 4)}</span>
                </div>
              )}
              {pData.attempts > 0 && (
                <div style={{ display: 'flex', justifyContent: 'space-between', color: 'var(--text-muted)' }}>
                  <span>Tentativas:</span>
                  <span style={{ color: 'var(--text)', fontFamily: 'var(--font-mono)' }}>{pData.attempts}</span>
                </div>
              )}
            </div>

            {/* Erro */}
            {pData.error_msg && (
              <div style={{ background: 'rgba(239,68,68,0.08)', borderRadius: 12, padding: '12px 18px', fontSize: 12, color: '#EF4444' }}>
                <div style={{ fontWeight: 700, marginBottom: 4 }}>Erro</div>
                <div>{pData.error_msg}</div>
              </div>
            )}
            {pData.blocked_reason && (
              <div style={{ background: 'rgba(249,115,22,0.08)', borderRadius: 12, padding: '12px 18px', fontSize: 12, color: '#F97316' }}>
                <div style={{ fontWeight: 700, marginBottom: 4 }}>Bloqueada</div>
                <div>{pData.blocked_reason}</div>
              </div>
            )}
            {isDeferred && (
              <div style={{ background: 'rgba(139,146,168,0.10)', borderRadius: 12, padding: '12px 18px', fontSize: 12, color: 'var(--text-muted)' }}>
                <div style={{ fontWeight: 700, marginBottom: 4 }}>Aguarda Fase 2</div>
                <div>Email/vídeo são processados numa fase posterior do roadmap.</div>
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
};

// ── Vista da campanha (canvas) ─────────────────────────────────────────────────
const CampanhaView = ({ campId, userEmail, onBack, onRefreshList, onOpenPeca }) => {
  const [data, setData]       = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [selected, setSelected] = React.useState([]);
  const [saving, setSaving]   = React.useState('');
  const [toast, setToast]     = React.useState('');

  const load = React.useCallback(async () => {
    try { const d = await ProdAPI.campanha(campId); setData(d); } catch {}
    setLoading(false);
  }, [campId]);

  React.useEffect(() => {
    load();
    const es = new EventSource(`/api/marketing/producao/campanha/${campId}/stream`);
    es.onmessage = e => {
      try {
        const ev = JSON.parse(e.data);
        if (ev.type === 'peca_updated') load();
      } catch {}
    };
    return () => { es.close(); };
  }, [campId]);

  const showToast = (msg) => { setToast(msg); setTimeout(() => setToast(''), 2500); };
  const toggleSelect = (id) => setSelected(s => s.includes(id) ? s.filter(x => x !== id) : [...s, id]);

  const doApproveBulk = async () => {
    if (!selected.length) return;
    setSaving('bulk');
    const r = await ProdAPI.approveBulk(campId, selected, userEmail || 'fabio.costa@digidelta.pt');
    if (r.error) showToast(r.error);
    else { showToast(`${selected.length} ${selected.length === 1 ? 'peça aprovada' : 'peças aprovadas'}`); setSelected([]); load(); }
    setSaving('');
  };

  const doRegenerateBulk = async () => {
    if (!selected.length) return;
    setSaving('regen-bulk');
    const r = await ProdAPI.regenerateBulk(campId, selected);
    if (r.error) showToast(r.error);
    else { showToast(`${selected.length} a regenerar`); setSelected([]); load(); }
    setSaving('');
  };

  const doIniciar = async () => {
    setSaving('iniciar');
    const r = await ProdAPI.iniciar(campId);
    if (r.error) showToast(r.error);
    else { showToast(`${r.pecas_criadas || 0} peças adicionadas à fila`); load(); }
    setSaving('');
  };

  const doFinalize = async () => {
    if (!confirm('Enviar campanha para Activação? Só peças aprovadas serão incluídas.')) return;
    setSaving('finalize');
    const r = await ProdAPI.finalize(campId, userEmail || 'fabio.costa@digidelta.pt');
    if (r.error) showToast(r.error);
    else { showToast('Campanha enviada para Activação'); setTimeout(() => { onRefreshList && onRefreshList(); onBack(); }, 1500); }
    setSaving('');
  };

  if (loading) return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: 300, color: 'var(--text-muted)', fontSize: 14 }}>
      A carregar...
    </div>
  );
  if (!data) return <div style={{ color: '#EF4444', padding: 20 }}>Erro ao carregar campanha</div>;

  const { campanha, pecas = [] } = data;
  const totalAprovadas = pecas.filter(p => p.status === 'approved').length;
  const totalReview    = pecas.filter(p => p.status === 'ready_review').length;
  const totalFailed    = pecas.filter(p => p.status === 'failed').length;
  const totalCost      = pecas.reduce((s, p) => s + parseFloat(p.cost_total_usd || 0), 0);
  const allDone        = pecas.length > 0 && pecas.every(p => ['approved','blocked','pending_activation','failed'].includes(p.status));
  const brandColor     = _pbrandColor(campanha.brand_slug);

  return (
    <div data-theme="light" style={{
      height: '100%', display: 'flex', flexDirection: 'column', minHeight: 0,
      '--bg': '#f8fafc', '--bg-elev': '#ffffff', '--bg-hover': '#f1f5f9', '--bg-sunken': '#e8edf5',
      '--border': '#dde3ef', '--border-strong': '#bec9de',
      '--text': '#112954', '--text-muted': '#4a5e7a', '--text-dim': '#6b7fa3',
      '--ai-500': '#3859D0',
    }}>
      <style>{`@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.5} }`}</style>
      {/* Header da campanha — padrão Briefings detail */}
      <div style={{ background: 'var(--bg-elev)', borderBottom: '1px solid var(--border)', 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 · PRODUÇÃO DE CONTEÚDOS
        </div>

        {/* Title + actions */}
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16 }}>
          <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>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6, flexWrap: 'wrap' }}>
              {/* Status pill */}
              <span style={{ fontSize: 11, fontWeight: 600, padding: '2px 8px', borderRadius: 99, background: 'color-mix(in srgb, var(--ai-500) 12%, transparent)', color: 'var(--ai-500)', fontFamily: 'var(--font-body)' }}>
                Em Produção
              </span>
              {/* Brand pill */}
              <span style={{ fontSize: 11, fontWeight: 600, padding: '2px 8px', borderRadius: 99, background: brandColor + '18', color: brandColor, border: `1px solid ${brandColor}30`, fontFamily: 'var(--font-body)' }}>
                {campanha.brand_name}
              </span>
              {/* Progress bar */}
              {pecas.length > 0 && (() => {
                const pct = Math.round(totalAprovadas / pecas.length * 100);
                return (
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                    <div style={{ width: 60, height: 4, borderRadius: 99, background: 'var(--border)', overflow: 'hidden' }}>
                      <div style={{ width: `${pct}%`, height: '100%', borderRadius: 99, background: pct === 100 ? 'var(--success, #22c55e)' : 'var(--ai-500)', transition: 'width 0.4s ease' }} />
                    </div>
                    <span style={{ fontSize: 11, color: 'var(--text-muted)', fontFamily: 'var(--font-body)' }}>{pct}%</span>
                  </div>
                );
              })()}
              {/* Meta info */}
              <span style={{ fontSize: 11, color: 'var(--text-muted)', fontFamily: 'var(--font-body)' }}>
                {pecas.length} {pecas.length === 1 ? 'peça' : 'peças'}
                {totalCost > 0 && <> · <span style={{ fontFamily: 'var(--font-mono)' }}>{fmtEur(totalCost)}</span></>}
              </span>
            </div>
          </div>
          {/* Right-side actions */}
          <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexShrink: 0, marginTop: 2 }}>
            {toast && <span style={{ fontSize: 12, color: '#10B981', fontWeight: 600, marginRight: 4 }}>{toast}</span>}
            <button onClick={onBack} className="btn" style={{ height: 30, padding: '0 12px', fontSize: 12 }}>← Voltar</button>
            {pecas.length === 0 && (
              <button onClick={doIniciar} disabled={!!saving} className="btn btn-ai" style={{ height: 30, padding: '0 14px', fontSize: 12 }}>
                {saving === 'iniciar' ? '...' : 'Iniciar produção'}
              </button>
            )}
            {selected.length > 0 && (
              <>
                <button onClick={doRegenerateBulk} disabled={!!saving}
                  style={{ height: 30, padding: '0 14px', fontSize: 12, background: 'var(--bg-elev)', border: '1px solid var(--ai-500)', color: 'var(--ai-500)', borderRadius: 6, fontWeight: 600, cursor: 'pointer' }}>
                  {saving === 'regen-bulk' ? '...' : `Regenerar ${selected.length}`}
                </button>
                <button onClick={doApproveBulk} disabled={!!saving}
                  style={{ height: 30, padding: '0 14px', fontSize: 12, background: '#10B981', color: '#fff', border: 'none', borderRadius: 6, fontWeight: 700, cursor: 'pointer' }}>
                  {saving === 'bulk' ? '...' : `Aprovar ${selected.length}`}
                </button>
              </>
            )}
            {allDone && totalAprovadas > 0 && (
              <button onClick={doFinalize} disabled={!!saving} className="btn btn-ai" style={{ height: 30, padding: '0 14px', fontSize: 12 }}>
                {saving === 'finalize' ? '...' : `Finalizar → Activação`}
              </button>
            )}
          </div>
        </div>

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

        {/* Stepper pipeline da campanha */}
        {pecas.length > 0 && (() => {
          const totalDeferred = pecas.filter(p => p.status === 'deferred').length;
          const totalGoldenPath = pecas.length - totalDeferred;
          const totalGenerated = pecas.filter(p => ['ready_review','approved','pending_activation','failed','blocked'].includes(p.status)).length;
          const totalReady     = pecas.filter(p => p.status === 'ready_review').length;
          const totalApproved  = pecas.filter(p => ['approved','pending_activation'].includes(p.status)).length;
          const totalActivated = pecas.filter(p => p.status === 'pending_activation').length;
          const denom = Math.max(1, totalGoldenPath);
          const steps = [
            { id: 'geracao',   label: 'Geração',     count: `${totalGenerated}/${totalGoldenPath}`, pct: totalGenerated/denom, accent: '#F59E0B' },
            { id: 'revisao',   label: 'Revisão',     count: `${totalReady}/${totalGoldenPath}`,     pct: totalReady > 0 ? 0.5 : (totalReady + totalApproved)/denom, accent: 'var(--ai-500)' },
            { id: 'aprovacao', label: 'Aprovação',   count: `${totalApproved}/${totalGoldenPath}`,  pct: totalApproved/denom, accent: '#10B981' },
            { id: 'activacao', label: 'Activação',   count: totalActivated > 0 ? `${totalActivated} entregues` : '—', pct: totalActivated/denom, accent: '#059669' },
          ];
          const currentStepIdx = totalApproved === totalGoldenPath ? 3 : totalReady > 0 ? 1 : 0;
          return (
            <div style={{ display: 'flex', alignItems: 'flex-start', overflowX: 'auto' }}>
              {steps.map((step, i) => {
                const isSelected = i === currentStepIdx;
                const isCompleted = step.pct >= 1;
                const isPartial = step.pct > 0 && step.pct < 1;
                const circleBg    = isCompleted ? 'rgba(21,128,61,.1)' : isPartial ? 'rgba(245,158,11,.1)' : isSelected ? 'var(--ai-500)' : 'transparent';
                const circleBdr   = isCompleted ? '1.5px solid rgba(21,128,61,.3)' : isPartial ? '1.5px solid rgba(245,158,11,.3)' : isSelected ? 'none' : '1.5px solid #e2e8f0';
                const circleColor = isCompleted ? '#15803d' : isPartial ? '#f59e0b' : isSelected ? '#fff' : '#94a3b8';
                const labelColor  = isSelected ? 'var(--text)' : (isCompleted || isPartial) ? 'var(--text-muted)' : '#94a3b8';
                return (
                  <React.Fragment key={step.id}>
                    <div style={{ display: 'flex', alignItems: 'flex-start', gap: 8, padding: '10px 16px 12px', borderBottom: `2px solid ${isSelected ? 'var(--ai-500)' : 'transparent'}`, flexShrink: 0 }}>
                      <div style={{ width: 20, height: 20, borderRadius: '50%', flexShrink: 0, marginTop: 1, background: circleBg, border: circleBdr, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                        <span style={{ fontSize: 10, fontWeight: 700, color: circleColor, fontFamily: 'var(--font-body)', lineHeight: 1 }}>
                          {isCompleted ? '✓' : String(i + 1)}
                        </span>
                      </div>
                      <div style={{ display: 'flex', flexDirection: 'column', gap: 1, textAlign: 'left' }}>
                        <span style={{ fontSize: 13, fontWeight: 600, color: labelColor, fontFamily: 'var(--font-display)', lineHeight: 1.2 }}>{`${i + 1}. ${step.label}`}</span>
                        <span style={{ fontSize: 10.5, color: '#94a3b8', fontFamily: 'var(--font-body)' }}>{step.count}</span>
                      </div>
                    </div>
                    {i < steps.length - 1 && (
                      <div style={{ width: 20, height: 1, flexShrink: 0, alignSelf: 'flex-start', marginTop: 20, background: step.pct >= 1 ? 'rgba(21,128,61,.25)' : '#e2e8f0' }} />
                    )}
                  </React.Fragment>
                );
              })}
            </div>
          );
        })()}
      </div>

      {/* Banner contextual */}
      {pecas.length > 0 && (() => {
        const banners = [];
        if (totalFailed > 0) banners.push({ kind: 'danger', text: `${totalFailed} ${totalFailed === 1 ? 'peça falhou a geração' : 'peças falharam a geração'} — selecciona e regenera ou abre cada uma para ver o erro.`, color: '#EF4444', bg: 'rgba(239,68,68,0.08)', border: 'rgba(239,68,68,0.25)' });
        else if (allDone && totalAprovadas > 0 && totalAprovadas === pecas.length - pecas.filter(p => p.status === 'deferred').length) banners.push({ kind: 'success', text: `Todas as peças aprovadas. Clica em "Finalizar → Activação" para empurrar para o próximo módulo.`, color: '#10B981', bg: 'rgba(16,185,129,0.08)', border: 'rgba(16,185,129,0.25)' });
        else if (totalReview > 0) banners.push({ kind: 'review', text: `${totalReview} ${totalReview === 1 ? 'peça pronta' : 'peças prontas'} para revisão. Aprova individualmente ou em lote.`, color: 'var(--ai-500)', bg: 'color-mix(in srgb, var(--ai-500) 8%, transparent)', border: 'color-mix(in srgb, var(--ai-500) 25%, transparent)' });
        if (banners.length === 0) return null;
        const b = banners[0];
        return (
          <div style={{ padding: '12px 40px 0', flexShrink: 0 }}>
            <div style={{ background: b.bg, border: `1px solid ${b.border}`, borderRadius: 8, padding: '10px 16px', fontSize: 13, color: b.color, fontWeight: 500 }}>
              {b.text}
            </div>
          </div>
        );
      })()}

      {/* Canvas */}
      <div style={{ flex: 1, minHeight: 0, padding: 16, display: 'flex' }}>
        {pecas.length === 0 ? (
          <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12, color: 'var(--text-muted)' }}>
            <div style={{ fontSize: 14 }}>Nenhuma peça em produção ainda.</div>
            <button onClick={doIniciar} disabled={!!saving} className="btn btn-ai"
              style={{ padding: '8px 20px', fontSize: 13 }}>
              {saving === 'iniciar' ? 'A iniciar...' : 'Iniciar produção'}
            </button>
          </div>
        ) : (
          <ProducaoCanvas
            pecas={pecas}
            selected={selected}
            onToggleSelect={toggleSelect}
            onOpenPeca={(p) => onOpenPeca && onOpenPeca(p, campanha)}
            brandColor={brandColor}
          />
        )}
      </div>
    </div>
  );
};

// ── Lista de campanhas (cards) ─────────────────────────────────────────────────
const CampanhasList = ({ campanhas, onSelect, filterEstado, selectedIds, onToggle, onToggleAll, allSelected }) => {
  if (!campanhas?.length) return null;

  // Filtrar por estado se aplicável
  const filtered = !filterEstado || filterEstado === 'all' ? campanhas : campanhas.filter(c => {
    const review   = parseInt(c.pecas_review || 0);
    const aprov    = parseInt(c.pecas_aprovadas || 0);
    const failed   = parseInt(c.pecas_failed || 0);
    if (filterEstado === 'review')      return review > 0;
    if (filterEstado === 'aprovadas')   return aprov > 0;
    if (filterEstado === 'falhas')      return failed > 0;
    if (filterEstado === 'em_producao') return parseInt(c.total_pecas || 0) > 0 && review === 0 && failed === 0;
    return true;
  });

  if (!filtered.length) return (
    <div style={{ padding: '48px 0', textAlign: 'center', color: 'var(--text-muted)', fontSize: 13 }}>
      Sem resultados para o filtro actual.
    </div>
  );

  // Deriva estado global da campanha a partir das peças
  const inferCampStatus = (c) => {
    const failed   = parseInt(c.pecas_failed || 0);
    const review   = parseInt(c.pecas_review || 0);
    const aprov    = parseInt(c.pecas_aprovadas || 0);
    const pendAct  = parseInt(c.pecas_pending_act || 0);
    const total    = parseInt(c.total_pecas || 0);
    if (total === 0)                              return { label: 'Pendente',     color: '#9CA3AF', bg: 'rgba(156,163,175,0.12)' };
    if (failed > 0)                               return { label: 'Com falhas',   color: '#EF4444', bg: 'rgba(239,68,68,0.12)' };
    if (pendAct > 0 && pendAct + aprov === total) return { label: 'Em Activação', color: '#059669', bg: 'rgba(5,150,105,0.12)' };
    if (aprov === total)                          return { label: 'Aprovada',     color: '#10B981', bg: 'rgba(16,185,129,0.12)' };
    if (review > 0)                               return { label: 'Para rever',   color: '#3859D0', bg: 'rgba(56,89,208,0.12)' };
    return { label: 'A gerar', color: '#F59E0B', bg: 'rgba(245,158,11,0.12)' };
  };

  // Estilos partilhados com Briefings (table real, sem outer border)
  const thSt = {
    textAlign: 'left', padding: '8px 12px',
    fontSize: 10, fontFamily: 'var(--font-display)', fontWeight: 600,
    letterSpacing: '0.10em', textTransform: 'uppercase',
    color: 'var(--text-muted)', whiteSpace: 'nowrap',
  };
  const tdSt = { padding: '10px 12px', overflow: 'hidden' };

  return (
    <div style={{ overflowX: 'auto', width: '100%' }}>
      <table style={{ width: '100%', minWidth: 1100, tableLayout: 'fixed', borderCollapse: 'collapse', fontSize: 12.5, fontFamily: 'var(--font-sans)' }}>
        <colgroup>
          <col style={{ width: 36 }}  />{/* checkbox */}
          <col />{/* Campanha */}
          <col style={{ width: 160 }} />{/* Marca */}
          <col style={{ width: 140 }} />{/* Estado */}
          <col style={{ width: 220 }} />{/* Progresso */}
          <col style={{ width: 100 }} />{/* Custo */}
          <col style={{ width: 120 }} />{/* Actualizado */}
          <col style={{ width: 40 }}  />{/* arrow */}
        </colgroup>
        <thead style={{ position: 'sticky', top: 0, zIndex: 2, background: 'var(--bg-elev)' }}>
          <tr style={{ borderBottom: '1px solid var(--border)' }}>
            <th style={{ ...thSt, textAlign: 'center' }}>
              <input type="checkbox" checked={allSelected} onChange={e => onToggleAll(e.target.checked)}
                style={{ width: 14, height: 14, cursor: 'pointer', accentColor: '#3859D0' }} />
            </th>
            <th style={thSt}>Campanha</th>
            <th style={thSt}>Marca</th>
            <th style={thSt}>Estado</th>
            <th style={thSt}>Progresso</th>
            <th style={{ ...thSt, textAlign: 'right' }}>Custo</th>
            <th style={thSt}>Actualizado</th>
            <th style={thSt} />
          </tr>
        </thead>
        <tbody>
          {filtered.map((c, i) => {
            const total     = parseInt(c.total_pecas || 0);
            const aprovadas = parseInt(c.pecas_aprovadas || 0);
            const pct       = total ? Math.round((aprovadas / total) * 100) : 0;
            const brandColor = _pbrandColor(c.brand_slug);
            const ultimaAct = c.ultima_actividade ? new Date(c.ultima_actividade) : null;
            const tempoRel = ultimaAct ? (() => {
              const diff = Math.floor((Date.now() - ultimaAct.getTime()) / 60000);
              if (diff < 1) return 'agora';
              if (diff < 60) return `há ${diff}m`;
              if (diff < 1440) return `há ${Math.floor(diff/60)}h`;
              return `há ${Math.floor(diff/1440)}d`;
            })() : '—';
            const cfg = inferCampStatus(c);
            return (
              <tr key={c.id} onClick={() => onSelect(c.id)}
                onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover)'}
                onMouseLeave={e => e.currentTarget.style.background = ''}
                style={{ borderBottom: '1px solid var(--border)', cursor: 'pointer', transition: 'background 150ms ease', background: selectedIds.has(c.id) ? 'color-mix(in srgb, #3859D0 5%, transparent)' : '' }}>
                {/* Checkbox */}
                <td style={{ padding: '4px 4px', textAlign: 'center' }} onClick={e => { e.stopPropagation(); onToggle(c.id); }}>
                  <input type="checkbox" checked={selectedIds.has(c.id)} onChange={() => onToggle(c.id)}
                    style={{ width: 14, height: 14, cursor: 'pointer', accentColor: '#3859D0' }} />
                </td>
                {/* Campanha */}
                <td style={tdSt}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10, minWidth: 0 }}>
                    <span style={{ width: 8, height: 8, borderRadius: '50%', background: brandColor, flexShrink: 0 }} />
                    <span style={{ fontWeight: 500, fontSize: 13, color: 'var(--text)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                      {c.titulo}
                    </span>
                  </div>
                </td>
                {/* Marca */}
                <td style={tdSt}>
                  <span style={{ fontSize: 11, fontFamily: 'var(--font-display)', fontWeight: 700, letterSpacing: '0.06em', color: brandColor, display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                    {(c.brand_name || '').toUpperCase()}
                  </span>
                </td>
                {/* Estado */}
                <td style={tdSt}>
                  <span style={{ fontSize: 11, fontFamily: 'var(--font-mono)', fontWeight: 600, padding: '2px 8px', borderRadius: 4, display: 'inline-block', color: cfg.color, background: cfg.bg, textTransform: 'uppercase', letterSpacing: '0.04em' }}>
                    {cfg.label}
                  </span>
                </td>
                {/* Progresso */}
                <td style={tdSt}>
                  {total > 0 ? (
                    <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                      <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11, color: 'var(--text-muted)' }}>
                        <span>{aprovadas}/{total}</span>
                        <span style={{ fontFamily: 'var(--font-mono)' }}>{pct}%</span>
                      </div>
                      <div style={{ height: 4, background: 'var(--bg-sunken)', borderRadius: 99, overflow: 'hidden' }}>
                        <div style={{ height: '100%', width: `${pct}%`, background: brandColor, borderRadius: 99, transition: 'width .4s' }} />
                      </div>
                    </div>
                  ) : (
                    <span style={{ fontSize: 11, color: 'var(--text-dim)', fontStyle: 'italic' }}>—</span>
                  )}
                </td>
                {/* Custo */}
                <td style={{ ...tdSt, textAlign: 'right' }}>
                  <span style={{ fontSize: 12, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>
                    {c.custo_total_usd ? fmtEur(c.custo_total_usd) : '—'}
                  </span>
                </td>
                {/* Actualizado */}
                <td style={tdSt}>
                  <span style={{ fontSize: 11, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)' }}>
                    {tempoRel}
                  </span>
                </td>
                {/* Arrow */}
                <td style={{ ...tdSt, textAlign: 'center', color: 'var(--text-dim)', fontSize: 14 }}>›</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
};

// ── Ecrã principal ─────────────────────────────────────────────────────────────
const MktProducaoScreen = ({ userEmail }) => {
  const [kpi, setKpi]           = React.useState({});
  const [campanhas, setCampanhas] = React.useState(null);
  const [view, setView]         = React.useState('list');
  const [campId, setCampId]     = React.useState(null);
  const [pecaId, setPecaId]     = React.useState(null);
  const [pecaCtx, setPecaCtx]   = React.useState(null);
  const [filterMarca, setFilterMarca]   = React.useState('all');
  const [filterEstado, setFilterEstado] = React.useState('all');
  const [filterProduto, setFilterProduto] = React.useState('all');
  const [filterCriador, setFilterCriador] = React.useState('all');
  const [filterPeriodo, setFilterPeriodo] = React.useState('all');
  const [allBrands, setAllBrands] = React.useState([]);
  const userEml = userEmail || window.currentUser?.email || 'fabio.costa@digidelta.pt';

  const loadAll = React.useCallback(async () => {
    try {
      const [k, c] = await Promise.all([ProdAPI.kpis(), ProdAPI.campanhas()]);
      setKpi(k || {});
      setCampanhas(Array.isArray(c) ? c : []);
    } catch {}
  }, []);

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

  React.useEffect(() => {
    fetch('/api/marketing/brands').then(r => r.ok ? r.json() : []).then(d => {
      const list = (d || []).filter(b => b.active !== false && b.slug !== 'todas');
      const order = ['mimaki','biond','decal','alldecor','sensek','netscreen','digidelta'];
      list.sort((a, b) => {
        const ia = order.indexOf(a.slug), ib = order.indexOf(b.slug);
        return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
      });
      setAllBrands(list);
    }).catch(() => {});
  }, []);

  // Listas únicas para os dropdowns
  const produtos = React.useMemo(() => {
    if (!campanhas) return [];
    const set = new Set();
    campanhas.forEach(c => { const p = c.commercial_name || c.product_name; if (p) set.add(p); });
    return [...set].sort().map(v => ({ value: v, label: v }));
  }, [campanhas]);
  const criadores = React.useMemo(() => {
    if (!campanhas) return [];
    const set = new Set();
    campanhas.forEach(c => { if (c.created_by) set.add(c.created_by); });
    return [...set].sort().map(v => ({ value: v, label: v }));
  }, [campanhas]);

  const filteredCampanhas = React.useMemo(() => {
    if (!campanhas) return [];
    // Período: filtra por created_at
    const now = new Date();
    const presetRange = (id) => {
      const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
      if (id === 'last_7d')    return [new Date(today - 6*86400000), new Date(today.getTime() + 86400000)];
      if (id === 'last_30d')   return [new Date(today - 29*86400000), new Date(today.getTime() + 86400000)];
      if (id === 'this_month') return [new Date(today.getFullYear(), today.getMonth(), 1), new Date(today.getTime() + 86400000)];
      if (id === 'last_month') return [new Date(today.getFullYear(), today.getMonth() - 1, 1), new Date(today.getFullYear(), today.getMonth(), 1)];
      if (id === 'last_90d')   return [new Date(today - 89*86400000), new Date(today.getTime() + 86400000)];
      if (id === 'this_year')  return [new Date(today.getFullYear(), 0, 1), new Date(today.getTime() + 86400000)];
      return null;
    };
    const range = filterPeriodo === 'all' ? null : presetRange(filterPeriodo);
    return campanhas.filter(c => {
      if (filterMarca !== 'all' && c.brand_slug !== filterMarca) return false;
      const produtoLabel = c.commercial_name || c.product_name;
      if (filterProduto !== 'all' && produtoLabel !== filterProduto) return false;
      if (filterCriador !== 'all' && c.created_by !== filterCriador) return false;
      if (range && c.created_at) {
        const d = new Date(c.created_at);
        if (d < range[0] || d >= range[1]) return false;
      }
      return true;
    });
  }, [campanhas, filterMarca, filterProduto, filterCriador, filterPeriodo]);

  const [selectedIds, setSelectedIds] = React.useState(new Set());
  const [pendingDelete, setPendingDelete] = React.useState(null);
  const [deleting, setDeleting] = React.useState(false);

  const clearSelection = () => setSelectedIds(new Set());

  const handleToggle = (id) => setSelectedIds(prev => {
    const next = new Set(prev);
    next.has(id) ? next.delete(id) : next.add(id);
    return next;
  });

  const handleToggleAll = (checked) => {
    if (checked) setSelectedIds(new Set(filteredCampanhas.map(c => c.id)));
    else clearSelection();
  };

  const handleBulkDelete = () => {
    const toDelete = (filteredCampanhas || []).filter(c => selectedIds.has(c.id));
    if (!toDelete.length) return;
    // pass first selected campaign as representative for the modal label
    setPendingDelete({ ids: [...selectedIds], labels: toDelete.map(c => c.titulo) });
  };

  const handleDeleteConfirm = async () => {
    if (!pendingDelete || deleting) return;
    setDeleting(true);
    try {
      await Promise.all(pendingDelete.ids.map(id => ProdAPI.deleteCampanha(id)));
      setPendingDelete(null);
      clearSelection();
      loadAll();
    } catch { /* server logs */ }
    finally { setDeleting(false); }
  };

  const someSelected = selectedIds.size > 0;
  const allSelected  = filteredCampanhas.length > 0 && filteredCampanhas.every(c => selectedIds.has(c.id));

  const handleSelect    = (id) => { setCampId(id); setView('campanha'); };
  const handleBack      = () => { setView('list'); setCampId(null); setPecaId(null); setPecaCtx(null); loadAll(); };
  const handleOpenPeca  = (peca, campanha) => {
    setPecaCtx({
      campId: campanha.id,
      brandSlug: campanha.brand_slug,
      brandName: campanha.brand_name,
      brandColor: _pbrandColor(campanha.brand_slug),
      campTitulo: campanha.titulo,
    });
    setPecaId(peca.id);
    setView('peca');
  };
  const handleBackToCamp = () => { setPecaId(null); setPecaCtx(null); setView('campanha'); };

  if (view === 'peca' && pecaId && pecaCtx) {
    return (
      <PecaView
        pecaId={pecaId}
        campId={pecaCtx.campId}
        brandSlug={pecaCtx.brandSlug}
        brandName={pecaCtx.brandName}
        brandColor={pecaCtx.brandColor}
        campTitulo={pecaCtx.campTitulo}
        userEmail={userEml}
        onBack={handleBackToCamp}
      />
    );
  }

  if (view === 'campanha' && campId) {
    return (
      <CampanhaView campId={campId} userEmail={userEml} onBack={handleBack} onRefreshList={loadAll} onOpenPeca={handleOpenPeca} />
    );
  }

  const hasFilter = filterMarca !== 'all' || filterEstado !== 'all' || filterProduto !== 'all' || filterCriador !== 'all' || filterPeriodo !== 'all';
  const counter   = filteredCampanhas.length;

  return (
    <div data-theme="light" style={{
      height: '100%', display: 'flex', flexDirection: 'column',
      '--bg': '#f8fafc', '--bg-elev': '#ffffff', '--bg-hover': '#f1f5f9', '--bg-sunken': '#e8edf5',
      '--border': '#dde3ef', '--border-strong': '#bec9de',
      '--text': '#112954', '--text-muted': '#4a5e7a', '--text-dim': '#6b7fa3',
      '--ai-500': '#3859D0',
    }}>
      <style>{`
        @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.5} }
      `}</style>

      {/* Page header fixo */}
      <div style={{ background: 'var(--bg-elev)', borderBottom: '1px solid var(--border)', padding: '20px 40px 0', flexShrink: 0 }}>
        <div style={{ fontSize: 11, fontWeight: 600, letterSpacing: '0.07em', color: 'var(--text-muted)', textTransform: 'uppercase', marginBottom: 6, fontFamily: 'var(--font-mono)' }}>
          MARKETING · PRODUÇÃO DE CONTEÚDOS
        </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)', fontFamily: 'var(--font-display)', letterSpacing: '-0.01em', lineHeight: 1.2 }}>
              Produção de Conteúdos
            </h1>
            {(() => {
              const totalPecas    = parseInt(kpi.em_producao || 0) + parseInt(kpi.aprovadas || 0);
              const aprovadas     = parseInt(kpi.aprovadas || 0);
              const pctAprovadas  = totalPecas > 0 ? Math.round(aprovadas / totalPecas * 100) : 0;
              const ctxLabel      = filterMarca !== 'all' && allBrands.find(b => b.slug === filterMarca)
                ? allBrands.find(b => b.slug === filterMarca).name
                : 'todas as marcas';
              return (
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6, flexWrap: 'wrap' }}>
                  <span style={{ fontSize: 11, color: 'var(--text-muted)', fontFamily: 'var(--font-body)' }}>
                    {totalPecas} peça{totalPecas !== 1 ? 's' : ''} · {counter} campanha{counter !== 1 ? 's' : ''} · {ctxLabel}
                  </span>
                  {totalPecas > 0 && (
                    <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                      <div style={{ width: 60, height: 4, borderRadius: 99, background: 'var(--border)', overflow: 'hidden' }}>
                        <div style={{ width: `${pctAprovadas}%`, height: '100%', borderRadius: 99, background: 'var(--success, #22c55e)', transition: 'width 0.4s ease' }} />
                      </div>
                      <span style={{ fontSize: 11, color: 'var(--text-muted)', fontFamily: 'var(--font-body)' }}>{pctAprovadas}% aprovadas</span>
                    </div>
                  )}
                </div>
              );
            })()}
          </div>
        </div>

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

        {/* Brand tabs */}
        <div style={{ display: 'flex', gap: 0, overflowX: 'auto' }}>
          {[{ slug: 'all', name: 'Todas' }, ...allBrands].map(b => {
            const active = b.slug === filterMarca;
            return (
              <button key={b.slug} onClick={() => setFilterMarca(b.slug)} style={{
                background: 'none', border: 'none', outline: 'none',
                borderBottom: `2px solid ${active ? 'var(--ai-500)' : 'transparent'}`,
                color: active ? 'var(--text)' : 'var(--text-muted)',
                fontSize: 13, fontWeight: active ? 600 : 500,
                fontFamily: 'var(--font-display)', letterSpacing: '.01em',
                padding: '10px 18px 12px',
                cursor: 'pointer', whiteSpace: 'nowrap',
                transition: 'color .15s, border-color .15s',
              }}>
                {b.name}
              </button>
            );
          })}
        </div>
      </div>

      {/* Scrollable body */}
      <div className="scrollbar" style={{ flex: 1, minHeight: 0, overflowY: 'auto', padding: '28px 40px 48px', background: 'var(--bg)', display: 'flex', flexDirection: 'column', gap: 18 }}>

        <ProducaoBanner kpi={kpi} campanhas={campanhas || []} />

        {/* 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' }}>
            <ProducaoDropdown label="Marca" value={filterMarca} onChange={setFilterMarca}
              options={[{ value: 'all', label: 'Todas' }, ...allBrands.map(b => ({ value: b.slug, label: b.name }))]}
            />
            <ProducaoDropdown label="Produto" value={filterProduto} onChange={setFilterProduto}
              options={[{ value: 'all', label: 'Todos' }, ...produtos]}
            />
            <ProducaoDropdown label="Criado por" value={filterCriador} onChange={setFilterCriador}
              options={[{ value: 'all', label: 'Todos' }, ...criadores]}
            />
            <ProducaoDropdown label="Estado" value={filterEstado} onChange={setFilterEstado}
              options={[
                { value: 'all',         label: 'Todos' },
                { value: 'em_producao', label: 'Em produção' },
                { value: 'review',      label: 'Para rever' },
                { value: 'aprovadas',   label: 'Aprovadas' },
                { value: 'falhas',      label: 'Com falhas' },
              ]}
            />
            {hasFilter && (
              <button onClick={() => { setFilterMarca('all'); setFilterEstado('all'); setFilterProduto('all'); setFilterCriador('all'); setFilterPeriodo('all'); }} style={{
                background: 'none', border: 'none', color: 'var(--text-muted)', fontSize: 11,
                cursor: 'pointer', padding: '4px 6px', fontFamily: 'var(--font-mono)',
              }}>✕ limpar</button>
            )}
            <span style={{ fontSize: 12, color: 'var(--text-muted)', paddingLeft: 4 }}>
              {filteredCampanhas.length} resultado{filteredCampanhas.length !== 1 ? 's' : ''}
            </span>
          </div>
          <ProducaoDateRangePicker value={filterPeriodo} onChange={setFilterPeriodo} />
        </div>

        {/* Barra de selecção — aparece quando há itens seleccionados */}
        {someSelected && (
          <div style={{
            display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8,
            padding: '8px 12px', borderRadius: 8, marginTop: 8,
            background: 'color-mix(in srgb, #3859D0 6%, #ffffff)',
            border: '1px solid color-mix(in srgb, #3859D0 20%, #dde3ef)',
          }}>
            <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
              <span style={{ fontSize: 12, fontWeight: 700, color: '#3859D0', fontFamily: 'var(--font-mono)', letterSpacing: '0.04em' }}>
                {selectedIds.size} seleccionad{selectedIds.size !== 1 ? 'as' : 'a'}
              </span>
              <div style={{ width: 1, height: 14, background: '#dde3ef' }} />
              <button onClick={handleBulkDelete}
                style={{ background: 'none', border: '1px solid #dde3ef', borderRadius: 5, padding: '4px 10px', fontSize: 12, cursor: 'pointer', color: '#dc2626', fontFamily: 'inherit' }}
                onMouseEnter={e => e.currentTarget.style.background = 'color-mix(in srgb, #dc2626 8%, transparent)'}
                onMouseLeave={e => e.currentTarget.style.background = 'none'}>
                Apagar
              </button>
            </div>
            <button onClick={clearSelection}
              style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 11, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)', padding: '4px 6px' }}>
              ✕ cancelar
            </button>
          </div>
        )}

        {/* KPI strip */}
        <ProducaoKPIs kpi={kpi} filterEstado={filterEstado} onFilter={setFilterEstado} />

        {/* Lista de campanhas */}
        {campanhas === null ? (
          <div style={{ padding: 40, textAlign: 'center', color: 'var(--text-muted)', fontSize: 13 }}>A carregar...</div>
        ) : filteredCampanhas.length === 0 ? (
          <div style={{ padding: '48px 0', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}>
            <div style={{ fontSize: 13, color: 'var(--text-muted)', textAlign: 'center' }}>
              {filterMarca !== 'all' ? 'Nenhuma campanha em produção nesta marca.' : 'Nenhuma campanha em produção.'}
            </div>
            <div style={{ fontSize: 12, color: 'var(--text-dim)' }}>
              Aprova uma campanha no módulo Campanhas para iniciar a produção de assets.
            </div>
          </div>
        ) : (
          <CampanhasList
            campanhas={filteredCampanhas}
            onSelect={handleSelect}
            filterEstado={filterEstado}
            selectedIds={selectedIds}
            onToggle={handleToggle}
            onToggleAll={handleToggleAll}
            allSelected={allSelected}
          />
        )}
      </div>

      {/* Footer com custos */}
      {kpi.custo_mes_usd && parseFloat(kpi.custo_mes_usd) > 0 && (
        <div style={{
          flexShrink: 0, borderTop: '1px solid var(--border)',
          background: 'var(--bg-elev)', padding: '5px 24px',
          display: 'flex', alignItems: 'center', gap: 0,
          fontSize: 10.5, fontFamily: 'var(--font-mono)', color: 'var(--text-dim)',
        }}>
          <span style={{ fontWeight: 600, color: 'var(--text-muted)', marginRight: 10, fontSize: 10 }}>PRODUÇÃO</span>
          <span>Custo mês: <strong style={{ color: 'var(--text-muted)', fontWeight: 600 }}>{fmtEur(kpi.custo_mes_usd, 4)}</strong></span>
          <span style={{ margin: '0 5px', opacity: 0.35 }}>·</span>
          <span>Tempo médio/peça: <strong style={{ color: 'var(--text-muted)', fontWeight: 600 }}>{kpi.tempo_medio_seg ? `${parseFloat(kpi.tempo_medio_seg).toFixed(0)}s` : '—'}</strong></span>
        </div>
      )}
      {pendingDelete && (
        <ProdDeleteConfirmModal
          pendingDelete={pendingDelete}
          onConfirm={handleDeleteConfirm}
          onClose={() => { setPendingDelete(null); }}
        />
      )}
    </div>
  );
};

window.MktProducaoScreen = MktProducaoScreen;
