/* screen_marketing_briefings.jsx
   Submenu Briefings — lista, formulário 5 blocos, detalhe e aprovação.
   Blocos 1-3: AI Strategist. Blocos 4-5: Marketing Manager.
*/

// ─── Constantes ────────────────────────────────────────────────────────────────

const STATUS_CONFIG = {
  rascunho:     { label: 'Rascunho',      color: '#475569', bg: '#e2e8f0' },
  submetido:    { label: 'Submetido',     color: '#92400e', bg: '#fef3c7' },
  validado_ceo: { label: 'Validado CEO',  color: '#6b21a8', bg: '#f3e8ff' },
  aprovado:     { label: 'Aprovado',      color: '#14532d', bg: '#dcfce7' },
  rejeitado:    { label: 'Rejeitado',     color: '#991b1b', bg: '#fee2e2' },
  em_campanha:  { label: 'Em Campanha',   color: '#1e3a8a', bg: '#dbeafe' },
};

// ─── Configuração de aprovadores (espelho do servidor) ────────────────────────
// Fluxo sequencial: brand_dir (CEO) age em 'submetido', rl (Chairman) age em 'validado_ceo'
const APPROVER_CONFIG_FE = {
  'fabio.costa@digidelta.pt':   { roles: ['rl', 'brand_dir'], brands: 'all' },
  'rui.leitao@digidelta.pt':    { roles: ['rl', 'brand_dir'], brands: 'all' },
  'rui@digidelta.pt':           { roles: ['rl', 'brand_dir'], brands: 'all' },
  'joao.paulino@digidelta.pt':  { roles: ['rl', 'brand_dir'], brands: 'all' },
  'armando.mota@digidelta.pt':  { roles: ['brand_dir'],       brands: ['mimaki','biond','decal','alldecor','sensek','digidelta'] },
  'bruno.rosa@digidelta.pt':    { roles: ['brand_dir'],       brands: ['netscreen'] },
};
// Ordem dos roles por marca (brand_dir primeiro, rl depois)
const BRAND_REQUIRED_ROLES_FE = { _default: ['brand_dir', 'rl'] };
const BRAND_DIR_NAME_FE = {
  mimaki: 'Armando Mota', biond: 'Armando Mota', decal: 'Armando Mota',
  alldecor: 'Armando Mota', sensek: 'Armando Mota', netscreen: 'Bruno Rosa',
  digidelta: 'Armando Mota',
};

// Fase activa baseada no status do briefing
const getActiveRole = (briefingStatus, requiredRoles) => {
  if (briefingStatus === 'submetido') {
    // Se a marca exige CEO, CEO é o primeiro; se só Chairman, Chairman age aqui
    return requiredRoles.includes('brand_dir') ? 'brand_dir' : 'rl';
  }
  if (briefingStatus === 'validado_ceo') return 'rl';
  return null;
};

const getRequiredApprovers = (brandSlug) => {
  const roles = BRAND_REQUIRED_ROLES_FE[brandSlug] || BRAND_REQUIRED_ROLES_FE._default;
  return roles.map(role => ({
    role,
    name: role === 'rl' ? 'Rui Leitão' : (BRAND_DIR_NAME_FE[brandSlug] || 'Director de Marca'),
  }));
};

// Devolve os roles que este utilizador pode actuar AGORA (respeitando a fase activa)
const getMyPendingRoles = (email, brandSlug, approvals = [], briefingStatus = 'submetido') => {
  const cfg = APPROVER_CONFIG_FE[email?.toLowerCase()];
  if (!cfg) return [];
  const required = BRAND_REQUIRED_ROLES_FE[brandSlug] || BRAND_REQUIRED_ROLES_FE._default;
  const eligible = cfg.brands === 'all' || (cfg.brands || []).includes(brandSlug)
    ? cfg.roles : cfg.roles.filter(r => r === 'rl');
  const decided  = approvals.map(a => a.role);
  const activeRole = getActiveRole(briefingStatus, required);
  return eligible.filter(r => required.includes(r) && !decided.includes(r) && r === activeRole);
};
// ──────────────────────────────────────────────────────────────────────────────────────────────

const BLOCK_LABELS = [
  'Produto',
  'Cliente-alvo',
  'Mercado',
  'Campanha',
  'Restrições',
  'Resumo',
];

const EMPTY_FORM = {
  product_id: '',
  block1: { commercial_name: '', product_family: '', elevator_pitch: '', usps: [''], certifications: [], applications: [], geo_markets: [] },
  block2: { decision_maker: '', end_user: '', pain_points: [''], motivators: [''], objections: [''], purchase_trigger: '', funnel_entry_stage: '' },
  block3: { competitors: [''], differentiation_args: [''], market_trends: [''], positioning_narrative: '', sensitive_info: '' },
  block4: { objective: '', kpis: [], channels: [], tone: '', key_message: '', timeline_start: '', timeline_end: '', previous_campaigns: [] },
  block5: { prohibited_claims: [''], off_brand_messages: [''], competitors_not_name: [''], out_of_scope_markets: [''], other_restrictions: [] },
};

// ─── Placeholders dinâmicos por marca ─────────────────────────────────────────
const BRAND_PLACEHOLDERS = {
  mimaki: {
    commercial_name:       'ex: UCJV330-160',
    elevator_pitch:        'ex: A impressora UV híbrida que imprime em rolos e rígidos até 1600mm — substitui duas máquinas, duplica o portfólio de serviços',
    usps:                  'ex: UV LED de nova geração com 30% menos odor — prima Enter',
    certifications:        'ex: CE, IQ Certification Mimaki, RoHS — prima Enter',
    applications:          'ex: Sinalética exterior, Floor graphics, Janelas decorativas — prima Enter',
    geo_markets:           'ex: Portugal, Espanha, Itália — prima Enter',
    decision_maker:        'ex: Director de Produção, Proprietário de sign shop',
    end_user:              'ex: Operador de impressão, Técnico UV',
    pain_points:           'ex: Dois trabalhos em duas máquinas diferentes, baixa produtividade — prima Enter',
    motivators:            'ex: ROI em 18 meses, portfólio de aplicações mais alargado — prima Enter',
    objections:            'ex: Preço inicial elevado vs Roland — prima Enter',
    purchase_trigger:      'ex: Fim de vida da máquina actual, novo contrato de sinalética',
    competitors:           'ex: Roland DG — prima Enter',
    differentiation_args:  'ex: Única UV híbrida com IQ Certification e tinta SVHC-free — prima Enter',
    market_trends:         'ex: Crescimento floor graphics +22% pós-pandemia — prima Enter',
    positioning_narrative: 'ex: A marca japonesa que democratizou a impressão UV de grande formato em Portugal',
    sensitive_info:        'ex: Roland perdeu distribuidor Ibérico em 2024',
    tone:                  'ex: Técnico e assertivo, focado em ROI, sem jargão excessivo',
    key_message:           'ex: Uma máquina, múltiplas aplicações — o retorno que os teus clientes esperam',
    previous_campaigns:    'ex: FESPA 2025 — 47 leads, CPL €12 — prima Enter',
    prohibited_claims:     'ex: Não afirmar velocidades máximas sem especificar modo de impressão — prima Enter',
    off_brand_messages:    'ex: Não comparar directamente por preço com Roland — prima Enter',
    competitors_not_name:  'ex: Roland DG — prima Enter',
    out_of_scope_markets:  'ex: Impressão offset, serigrafia industrial — prima Enter',
    other_restrictions:    'ex: Não usar imagens de equipamentos de concorrentes — prima Enter',
  },
  biond: {
    commercial_name:       'ex: Bio-Print Film 120µm',
    elevator_pitch:        'ex: O único filme de impressão com certificação TÜV Biobased 87% — qualidade profissional com 87% de origem biológica, sem compromisso',
    usps:                  'ex: Certificação TÜV Rheinland Biobased 87% — prima Enter',
    certifications:        'ex: TÜV Rheinland OK Biobased, SGS FSC — prima Enter',
    applications:          'ex: Wrapping veicular, Protection film, Decoração interiores — prima Enter',
    geo_markets:           'ex: Portugal, Espanha, UK, Alemanha — prima Enter',
    decision_maker:        'ex: Responsável de compras, Distribuidor de materiais',
    end_user:              'ex: Aplicador de vinil, Wrapper profissional',
    pain_points:           'ex: Clientes exigem alternativas eco sem perder qualidade — prima Enter',
    motivators:            'ex: Diferenciação ecológica com certificação auditável — prima Enter',
    objections:            'ex: Preço premium vs filme convencional — prima Enter',
    purchase_trigger:      'ex: Regulação ambiental, briefing eco de cliente final, novo contrato',
    competitors:           'ex: Avery Dennison, 3M — prima Enter',
    differentiation_args:  'ex: Única certificação TÜV Biobased 87% em filmes de impressão — prima Enter',
    market_trends:         'ex: EU Plastics Directive 2025, crescimento eco-wrapping +34% — prima Enter',
    positioning_narrative: 'ex: A única alternativa sustentável que não exige compromisso na qualidade de impressão',
    sensitive_info:        'ex: Avery ainda sem certificação biobased equivalente no mercado PT/ES',
    tone:                  'ex: Ecológico e técnico, prova científica, sem greenwashing',
    key_message:           'ex: 87% biobased, 100% profissional — o futuro dos filmes de impressão já chegou',
    previous_campaigns:    'ex: FESPA Barcelona 2024 — 23 amostras, 4 distribuidores — prima Enter',
    prohibited_claims:     'ex: Não afirmar "100% biodegradável" — certificação é biobased — prima Enter',
    off_brand_messages:    'ex: Não usar claims ambientais sem fonte TÜV citada — prima Enter',
    competitors_not_name:  'ex: Avery Dennison, 3M — prima Enter',
    out_of_scope_markets:  'ex: Embalagem alimentar, dispositivos médicos — prima Enter',
    other_restrictions:    'ex: Claims ambientais sempre com fonte certificada visível — prima Enter',
  },
  decal: {
    commercial_name:       'ex: Bubble-Free Sign Graphics',
    elevator_pitch:        'ex: O vinil com canais de ar que elimina bolhas na aplicação — instalação perfeita em segundos, sem espátula, sem retrabalho',
    usps:                  'ex: Tecnologia Bubble-Free — aplicação sem bolhas — prima Enter',
    certifications:        'ex: REACH compliant, RoHS — prima Enter',
    applications:          'ex: Comunicação visual, Sinalética, Veículos, Point-of-sale — prima Enter',
    geo_markets:           'ex: Portugal, Espanha — prima Enter',
    decision_maker:        'ex: Responsável de compras em sign shop, Distribuidor de materiais',
    end_user:              'ex: Instalador de vinil, Signmaker',
    pain_points:           'ex: Bolhas e rugas que obrigam a refazer a aplicação — prima Enter',
    motivators:            'ex: Menos retrabalho, instalações mais rápidas, clientes satisfeitos — prima Enter',
    objections:            'ex: Preço por metro ligeiramente acima do standard — prima Enter',
    purchase_trigger:      'ex: Reclamação de cliente por bolhas, expansão de portfólio de serviços',
    competitors:           'ex: Orafol, 3M, Avery Dennison — prima Enter',
    differentiation_args:  'ex: Tecnologia Bubble-Free exclusiva — única sem necessidade de espátula — prima Enter',
    market_trends:         'ex: Crescimento aplicações removíveis +15% para sinalética temporária — prima Enter',
    positioning_narrative: 'ex: O material que torna qualquer instalador num profissional de excelência',
    sensitive_info:        'ex: Orafol com atrasos de entrega recorrentes em Q1 2025',
    tone:                  'ex: Prático e demonstrativo, foco na experiência de aplicação',
    key_message:           'ex: Sem bolhas, sem ferramentas, sem erros — Bubble-Free muda tudo',
    previous_campaigns:    'ex: Open Day Decal 2024 — 12 sign shops, 8 amostras pedidas — prima Enter',
    prohibited_claims:     'ex: Não afirmar "invisível" — vinil tem sempre espessura — prima Enter',
    off_brand_messages:    'ex: Não entrar em guerra de preços com 3M — prima Enter',
    competitors_not_name:  'ex: 3M, Orafol — prima Enter',
    out_of_scope_markets:  'ex: Embalagem industrial, uso exterior prolongado sem especificação — prima Enter',
    other_restrictions:    'ex: Durabilidade exterior sempre com especificação de superfície — prima Enter',
  },
  sensek: {
    commercial_name:       'ex: TS500-1800 AMF',
    elevator_pitch:        'ex: A impressora de sublimação de alta velocidade para produção têxtil — 105m²/h com qualidade fotográfica e custo por metro imbatível',
    usps:                  'ex: 105m²/h em modo produção, menor TCO do segmento — prima Enter',
    certifications:        'ex: Oeko-Tex Standard 100, CE — prima Enter',
    applications:          'ex: Moda, Sportswear, Home textile, Flags — prima Enter',
    geo_markets:           'ex: Portugal, Espanha, Turquia — prima Enter',
    decision_maker:        'ex: Director de operações, Responsável de compras industrial',
    end_user:              'ex: Operador de máquina têxtil, Técnico de sublimação',
    pain_points:           'ex: Custo por metro elevado, gargalos nos picos de produção — prima Enter',
    motivators:            'ex: Redução de custo por metro, internalização de produção têxtil — prima Enter',
    objections:            'ex: Investimento inicial elevado vs outsourcing — prima Enter',
    purchase_trigger:      'ex: Expansão de capacidade, internalização de produção têxtil',
    competitors:           'ex: Epson, MS Printing, Durst — prima Enter',
    differentiation_args:  'ex: Melhor ratio custo/velocidade no segmento mid-market têxtil — prima Enter',
    market_trends:         'ex: Nearshoring têxtil Europa +28%, crescimento sportswear personalizado — prima Enter',
    positioning_narrative: 'ex: A máquina que devolve a produção têxtil à Península Ibérica',
    sensitive_info:        'ex: Epson com problemas de cabeças em climas húmidos reportados em PT',
    tone:                  'ex: Industrial e preciso, foco em produtividade e TCO',
    key_message:           'ex: Mais metros por hora, menos custo por peça — a equação que muda a tua produção',
    previous_campaigns:    'ex: Texprocess 2024 — 8 leads qualificados, 2 demos agendadas — prima Enter',
    prohibited_claims:     'ex: Não garantir velocidade máxima sem especificar modo e substrato — prima Enter',
    off_brand_messages:    'ex: Não posicionar como substituto de impressão offset têxtil — prima Enter',
    competitors_not_name:  'ex: Epson, MS Printing — prima Enter',
    out_of_scope_markets:  'ex: Impressão de embalagem, papel industrial — prima Enter',
    other_restrictions:    'ex: Velocidade sempre com especificação de modo de impressão e qualidade — prima Enter',
  },
  alldecor: {
    commercial_name:       'ex: AllDecor Premium Walls',
    elevator_pitch:        'ex: A solução completa de decoração de interiores em impressão digital — do projecto à instalação, com materiais BIOND certificados e impressão Mimaki',
    usps:                  'ex: Serviço turnkey — projecto, impressão e instalação incluídos — prima Enter',
    certifications:        'ex: Materiais BIOND Biobased, Mimaki UV certified — prima Enter',
    applications:          'ex: Revestimentos de parede, Pavimentos decorativos, Tectos, Mobiliário — prima Enter',
    geo_markets:           'ex: Portugal, Espanha — prima Enter',
    decision_maker:        'ex: Arquitecto, Designer de interiores, Promotor imobiliário',
    end_user:              'ex: Cliente final residencial ou corporativo',
    pain_points:           'ex: Soluções de decoração personalizada demoram semanas e são inflexíveis — prima Enter',
    motivators:            'ex: Diferenciação do espaço, projecto único personalizado — prima Enter',
    objections:            'ex: Custo vs papel de parede standard ou vinílico — prima Enter',
    purchase_trigger:      'ex: Obra nova, renovação de escritório, projecto de arquitectura com cliente exigente',
    competitors:           'ex: HP Latex (decoração), Roland (impressão), fornecedores tradicionais — prima Enter',
    differentiation_args:  'ex: Único serviço end-to-end com materiais biobased em decoração digital — prima Enter',
    market_trends:         'ex: Crescimento personalização de espaços +40%, sustentabilidade em interiores — prima Enter',
    positioning_narrative: 'ex: Decoração digital personalizada que transforma qualquer espaço em experiência única',
    sensitive_info:        'ex: HP Latex sem parceiro de instalação com cobertura nacional em PT',
    tone:                  'ex: Premium e inspiracional, foco na experiência e personalização',
    key_message:           'ex: O teu espaço, o teu design — produzido em Portugal, entregue em 5 dias',
    previous_campaigns:    'ex: Architect@Work 2024 — 15 projectos iniciados — prima Enter',
    prohibited_claims:     'ex: Não garantir durabilidade sem especificar tipo de substrato e uso — prima Enter',
    off_brand_messages:    'ex: Não comparar com papéis de parede standard — produtos completamente diferentes — prima Enter',
    competitors_not_name:  'ex: HP Latex instaladores — prima Enter',
    out_of_scope_markets:  'ex: Impressão exterior UV directa sem laminação protectora — prima Enter',
    other_restrictions:    'ex: Materiais sempre apresentados com ficha técnica disponível — prima Enter',
  },
  netscreen: {
    commercial_name:       'ex: NetScreen P4 Indoor',
    elevator_pitch:        'ex: O display LED de alta densidade de pixels para ambientes interiores — 800cd/m², gestão cloud remota, instalação em 4 horas',
    usps:                  'ex: Gestão de conteúdo cloud sem software adicional — prima Enter',
    certifications:        'ex: CE, RoHS, UL — prima Enter',
    applications:          'ex: Publicidade retail, Eventos, Showrooms, Centros comerciais — prima Enter',
    geo_markets:           'ex: Portugal, Espanha — prima Enter',
    decision_maker:        'ex: Director de Marketing, Gestor de ponto de venda, Empresa de publicidade exterior',
    end_user:              'ex: Responsável de comunicação visual, Marketing manager',
    pain_points:           'ex: Conteúdos impressos desactualizados, custo de troca de materiais — prima Enter',
    motivators:            'ex: Actualização de conteúdo em tempo real, redução de custos de impressão — prima Enter',
    objections:            'ex: Investimento inicial elevado vs vinil/lona — prima Enter',
    purchase_trigger:      'ex: Renovação de ponto de venda, novo espaço comercial, substituição de totem',
    competitors:           'ex: Unilumin, Absen, Samsung Commercial — prima Enter',
    differentiation_args:  'ex: Suporte técnico local em Portugal com SLA 24h garantido — prima Enter',
    market_trends:         'ex: DOOH crescimento +18% anual, digitalização de retail acelerada — prima Enter',
    positioning_narrative: 'ex: A tecnologia LED que transforma pontos de venda em experiências digitais',
    sensitive_info:        'ex: Unilumin com atrasos logísticos recorrentes desde 2024',
    tone:                  'ex: Moderno e pragmático, foco em ROI e facilidade de gestão',
    key_message:           'ex: Actualiza a tua mensagem em segundos, não em semanas',
    previous_campaigns:    'ex: EuroShop 2024 — 6 projectos piloto, 3 instalações concluídas — prima Enter',
    prohibited_claims:     'ex: Não garantir brilho exterior sem especificar IP rating — prima Enter',
    off_brand_messages:    'ex: Não posicionar como substituto de toda a comunicação impressa — prima Enter',
    competitors_not_name:  'ex: Unilumin, Samsung Commercial — prima Enter',
    out_of_scope_markets:  'ex: Televisão consumer, sinalização de trânsito regulamentada — prima Enter',
    other_restrictions:    'ex: Sempre distinguir indoor vs outdoor nas especificações técnicas — prima Enter',
  },
  digidelta: {
    commercial_name:       'ex: DigiRent — Solução de Financiamento',
    elevator_pitch:        'ex: A solução financeira da Digidelta que permite aceder a equipamento profissional sem imobilizar capital — renda fixa, upgrades facilitados',
    usps:                  'ex: Aprovação em 48h, sem entrada inicial — prima Enter',
    certifications:        'ex: Parceria bancária certificada — prima Enter',
    applications:          'ex: Financiamento de equipamento, Locação operacional, Upgrade de máquina — prima Enter',
    geo_markets:           'ex: Portugal — prima Enter',
    decision_maker:        'ex: Director Financeiro, CEO, Sócio-gerente PME',
    end_user:              'ex: Director de produção, utilizador final do equipamento',
    pain_points:           'ex: Capital imobilizado em equipamento, aprovação bancária demorada — prima Enter',
    motivators:            'ex: Preservar liquidez, aceder a equipamento de topo imediatamente — prima Enter',
    objections:            'ex: Custo total de locação vs compra directa — prima Enter',
    purchase_trigger:      'ex: Necessidade urgente de equipamento, fim de contrato anterior, crescimento',
    competitors:           'ex: Financiamento bancário directo, leasing de terceiros — prima Enter',
    differentiation_args:  'ex: Conhecimento técnico do equipamento que nenhum banco tem — assessoria incluída — prima Enter',
    market_trends:         'ex: PMEs preferem OPEX a CAPEX, crescimento leasing operacional +12% — prima Enter',
    positioning_narrative: 'ex: A Digidelta não vende apenas equipamento — dá acesso ao equipamento certo, no momento certo',
    sensitive_info:        'ex: Banco X reduziu linha de crédito para PMEs de impressão em Q4 2024',
    tone:                  'ex: Confiante e consultivo, focado em benefício financeiro concreto',
    key_message:           'ex: O equipamento que precisas, a renda que consegues pagar — começa hoje',
    previous_campaigns:    'ex: DigiRent Launch 2024 — 18 contratos, €2.1M financiado — prima Enter',
    prohibited_claims:     'ex: Não garantir aprovação a todos os clientes — sujeito a análise — prima Enter',
    off_brand_messages:    'ex: Não posicionar DigiRent como crédito fácil — é locação operacional — prima Enter',
    competitors_not_name:  'ex: Leasing Millennium, BPI Empresas — prima Enter',
    out_of_scope_markets:  'ex: Particulares B2C, equipamento de terceiros — prima Enter',
    other_restrictions:    'ex: Sempre mencionar "sujeito a aprovação financeira" — prima Enter',
  },
};

const _PH_DEFAULT = {
  commercial_name:       'ex: Nome comercial do produto',
  elevator_pitch:        'ex: A única solução que combina X e Y numa passagem, reduzindo o tempo de produção em 40%',
  usps:                  'ex: Característica única que nenhum concorrente tem — prima Enter',
  certifications:        'ex: Certificação técnica relevante para o mercado — prima Enter',
  applications:          'ex: Aplicação principal, Aplicação secundária — prima Enter',
  geo_markets:           'ex: Portugal, Espanha, França — prima Enter',
  decision_maker:        'ex: Director de operações, Responsável de compras',
  end_user:              'ex: Operador técnico, utilizador final do produto',
  pain_points:           'ex: Problema que o produto resolve directamente — prima Enter',
  motivators:            'ex: Benefício concreto que motiva a decisão de compra — prima Enter',
  objections:            'ex: Objecção típica vs alternativa do mercado — prima Enter',
  purchase_trigger:      'ex: Evento que precipita a decisão de compra',
  competitors:           'ex: Concorrente principal do segmento — prima Enter',
  differentiation_args:  'ex: O que torna este produto único vs concorrência — prima Enter',
  market_trends:         'ex: Tendência de crescimento relevante para o produto — prima Enter',
  positioning_narrative: 'ex: A marca que define o padrão no seu segmento na Península Ibérica',
  sensitive_info:        'ex: Informação de concorrência para uso interno',
  tone:                  'ex: Tom de comunicação desejado — técnico, inspiracional, assertivo',
  key_message:           'ex: A mensagem central que todos os conteúdos devem transmitir',
  previous_campaigns:    'ex: Evento ou campanha anterior — N leads, CPL €X — prima Enter',
  prohibited_claims:     'ex: Afirmação que não pode ser usada por razões legais ou técnicas — prima Enter',
  off_brand_messages:    'ex: Mensagem que contradiz o posicionamento da marca — prima Enter',
  competitors_not_name:  'ex: Concorrente que não deve ser nomeado directamente — prima Enter',
  out_of_scope_markets:  'ex: Segmento ou mercado fora do âmbito desta campanha — prima Enter',
  other_restrictions:    'ex: Restrição adicional de conteúdo ou imagem — prima Enter',
};

function ph(field, brand) {
  return (BRAND_PLACEHOLDERS[brand] || {})[field] || _PH_DEFAULT[field] || '';
}

function calcProgress(form) {
  const hasText  = (v) => typeof v === 'string' && v.trim().length > 0;
  const hasItems = (v) => Array.isArray(v) && v.some(i => (typeof i === 'string' ? i.trim() : i));
  const score    = (arr) => arr.filter(Boolean).length / arr.length;

  const b1 = form.block1 || {};
  const b2 = form.block2 || {};
  const b3 = form.block3 || {};
  const b4 = form.block4 || {};
  const b5 = form.block5 || {};

  const s1 = score([!!form.product_id, hasText(b1.commercial_name), hasText(b1.elevator_pitch), hasItems(b1.usps), hasItems(b1.geo_markets)]);
  const s2 = score([hasText(b2.decision_maker), hasText(b2.end_user), hasItems(b2.pain_points), hasText(b2.purchase_trigger)]);
  const s3 = score([hasItems(b3.competitors), hasText(b3.positioning_narrative), hasItems(b3.differentiation_args)]);
  const s4 = score([hasText(b4.objective), hasItems(b4.channels), hasText(b4.tone), hasText(b4.key_message)]);
  // B5 pode legitimamente estar vazio — damos 0.5 base se tudo vazio
  const anyB5 = hasItems(b5.prohibited_claims) || hasItems(b5.off_brand_messages) || hasItems(b5.competitors_not_name) || hasItems(b5.out_of_scope_markets) || hasItems(b5.other_restrictions);
  const s5 = anyB5 ? 1 : 0.5;

  return Math.round(((s1 + s2 + s3 + s4 + s5) / 5) * 100);
}

const ProgressPill = ({ pct }) => {
  const color = pct >= 80 ? 'var(--success)' : pct >= 40 ? 'var(--warning)' : 'var(--text-muted)';
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
      <div style={{ width: 80, height: 4, borderRadius: 2, background: 'var(--border)', overflow: 'hidden' }}>
        <div style={{ width: `${pct}%`, height: '100%', background: color, borderRadius: 2, transition: 'width 0.3s' }} />
      </div>
      <span style={{ fontSize: 11, fontFamily: 'var(--font-mono)', color, fontWeight: 600 }}>{pct}%</span>
    </div>
  );
};

const CHANNELS_OPTS = [
  { id: 'linkedin',    label: 'LinkedIn' },
  { id: 'instagram',   label: 'Instagram' },
  { id: 'facebook',    label: 'Facebook' },
  { id: 'google_ads',  label: 'Google Ads' },
  { id: 'youtube',     label: 'YouTube' },
  { id: 'website',     label: 'Website' },
  { id: 'email',       label: 'Email' },
  { id: 'whatsapp',    label: 'WhatsApp' },
  { id: 'muppi_led',   label: 'Muppi LED' },
];

// ─── Helpers de UI ─────────────────────────────────────────────────────────────

const StatusBadge = ({ status }) => {
  const cfg = STATUS_CONFIG[status] || STATUS_CONFIG.rascunho;
  return (
    <span style={{
      fontSize: 11, fontFamily: 'monospace', letterSpacing: '0.06em',
      padding: '3px 10px', borderRadius: 4, display: 'inline-block',
      fontWeight: 700,
      color: cfg.color, background: cfg.bg,
      border: '1px solid currentColor',
    }}>
      {cfg.label.toUpperCase()}
    </span>
  );
};

const Field = ({ label, hint, children, required }) => (
  <div style={{ marginBottom: 20 }}>
    <label style={{
      display: 'block', fontSize: 11, fontWeight: 600,
      color: 'var(--text-dim)', textTransform: 'uppercase',
      letterSpacing: '0.06em', marginBottom: 6,
    }}>
      {label}
      {required && <span style={{ color: 'var(--ai-500)', marginLeft: 4, fontSize: 13, lineHeight: 1 }}>·</span>}
    </label>
    {children}
    {hint && <div style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 4 }}>{hint}</div>}
  </div>
);

const TInput = ({ value, onChange, placeholder, multiline, rows = 3 }) => {
  const [focused, setFocused] = React.useState(false);
  const base = {
    width: '100%', padding: '8px 12px', boxSizing: 'border-box',
    background: 'var(--bg-sunken)',
    border: `1px solid ${focused ? 'var(--ai-500)' : 'var(--border)'}`,
    borderRadius: 6, color: 'var(--text)', fontSize: 13,
    fontFamily: 'var(--font-body)', outline: 'none',
    boxShadow: focused ? '0 0 0 3px color-mix(in oklch, var(--ai-500) 12%, transparent)' : 'none',
    transition: 'border-color 0.15s, box-shadow 0.15s',
  };
  const events = { onFocus: () => setFocused(true), onBlur: () => setFocused(false) };
  return multiline
    ? <textarea value={value} onChange={e => onChange(e.target.value)}
        placeholder={placeholder} rows={rows} {...events}
        style={{ ...base, resize: 'vertical' }} />
    : <input type="text" value={value} onChange={e => onChange(e.target.value)}
        placeholder={placeholder} {...events} style={base} />;
};

const TSelect = ({ value, onChange, options, placeholder }) => {
  const [focused, setFocused] = React.useState(false);
  return (
    <select value={value} onChange={e => onChange(e.target.value)}
      onFocus={() => setFocused(true)} onBlur={() => setFocused(false)}
      style={{
        width: '100%', padding: '8px 12px', boxSizing: 'border-box',
        background: 'var(--bg-sunken)',
        border: `1px solid ${focused ? 'var(--ai-500)' : 'var(--border)'}`,
        borderRadius: 6, color: value ? 'var(--text)' : 'var(--text-muted)', fontSize: 13,
        outline: 'none',
        boxShadow: focused ? '0 0 0 3px color-mix(in oklch, var(--ai-500) 12%, transparent)' : 'none',
        transition: 'border-color 0.15s, box-shadow 0.15s',
      }}>
      <option value="">{placeholder || 'Seleccionar...'}</option>
      {options.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
    </select>
  );
};

const ChipInput = ({ items, onChange, placeholder, maxItems }) => {
  const [inputVal, setInputVal] = React.useState('');
  const [focused, setFocused] = React.useState(false);
  const filled = (items || []).filter(i => typeof i === 'string' ? i.trim() : i);
  const canAdd = !maxItems || filled.length < maxItems;

  const addChip = () => {
    const v = inputVal.trim();
    if (!v || !canAdd) return;
    onChange([...filled, v]);
    setInputVal('');
  };
  const del = (i) => onChange(filled.filter((_, idx) => idx !== i));

  return (
    <div>
      {filled.length > 0 && (
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 8 }}>
          {filled.map((item, i) => (
            <div key={i} style={{
              display: 'inline-flex', alignItems: 'center', gap: 5,
              padding: '4px 10px 4px 10px', borderRadius: 999,
              background: 'color-mix(in oklch, var(--ai-500) 10%, transparent)',
              border: '1px solid color-mix(in oklch, var(--ai-500) 22%, transparent)',
              fontSize: 12, color: 'var(--text)',
            }}>
              <span>{item}</span>
              <button onClick={() => del(i)} style={{
                background: 'none', border: 'none', padding: '0 0 0 2px',
                color: 'var(--text-dim)', cursor: 'pointer', fontSize: 15, lineHeight: 1,
                display: 'flex', alignItems: 'center',
              }}>×</button>
            </div>
          ))}
        </div>
      )}
      {canAdd && (
        <div style={{ display: 'flex', gap: 6 }}>
          <input type="text" value={inputVal}
            onChange={e => setInputVal(e.target.value)}
            onFocus={() => setFocused(true)} onBlur={() => setFocused(false)}
            onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); addChip(); } }}
            placeholder={placeholder}
            style={{
              flex: 1, padding: '7px 10px',
              background: 'var(--bg-sunken)',
              border: `1px solid ${focused ? 'var(--ai-500)' : 'var(--border)'}`,
              borderRadius: 6, color: 'var(--text)', fontSize: 13,
              fontFamily: 'var(--font-body)', outline: 'none',
              boxShadow: focused ? '0 0 0 3px color-mix(in oklch, var(--ai-500) 12%, transparent)' : 'none',
              transition: 'border-color 0.15s, box-shadow 0.15s',
            }} />
          <button onClick={addChip} disabled={!inputVal.trim()}
            style={{
              padding: '0 12px', height: 36, borderRadius: 6,
              background: inputVal.trim() ? 'var(--ai-500)' : 'var(--bg-elev)',
              border: '1px solid var(--border)', color: inputVal.trim() ? '#fff' : 'var(--text-dim)',
              cursor: inputVal.trim() ? 'pointer' : 'default', fontSize: 12,
              transition: 'all 0.15s',
            }}>+ Add</button>
        </div>
      )}
      {maxItems && (
        <div style={{ fontSize: 10.5, color: 'var(--text-dim)', marginTop: 5, fontFamily: 'var(--font-mono)' }}>
          {filled.length}/{maxItems}
        </div>
      )}
    </div>
  );
};

const ChannelToggle = ({ selected, onChange }) => (
  <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
    {CHANNELS_OPTS.map(ch => {
      const active = selected.includes(ch.id);
      return (
        <button key={ch.id} onClick={() => onChange(
          active ? selected.filter(c => c !== ch.id) : [...selected, ch.id]
        )} style={{
          padding: '5px 12px', borderRadius: 999, fontSize: 12, cursor: 'pointer',
          border: '1px solid var(--border)',
          background: active ? 'var(--ai-500)' : 'var(--bg-sunken)',
          color: active ? '#fff' : 'var(--text-muted)',
          transition: 'all .15s',
        }}>{ch.label}</button>
      );
    })}
  </div>
);

// ─── Blocos do formulário ──────────────────────────────────────────────────────

const Block1 = ({ data, upd, isAll, allBrands, selectedBrand, onBrandChange, products, selectedSegment, onSegmentChange, selectedFamily, onFamilyChange, productId, onProductChange }) => {
  const hasSeg = (products || []).some(p => p.segment);

  const segments = hasSeg
    ? [...new Set((products || []).map(p => p.segment))].filter(Boolean).sort()
    : [];

  const familiesAll = hasSeg
    ? [...new Set((products || []).filter(p => !selectedSegment || p.segment === selectedSegment).map(p => p.category))].filter(Boolean).sort()
    : [...new Set((products || []).map(p => p.category))].filter(Boolean).sort();

  const filteredProducts = (products || []).filter(p =>
    (!hasSeg || !selectedSegment || p.segment === selectedSegment) &&
    (!selectedFamily || p.category === selectedFamily)
  );

  const noMarca = isAll && !selectedBrand;

  return (
  <div>
    <div style={{ marginBottom: 28 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
        <div style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', letterSpacing: '0.08em' }}>BLOCO 1 DE 5</div>
        <span style={{ fontSize: 9, fontFamily: 'var(--font-mono)', letterSpacing: '0.08em', padding: '1px 6px', borderRadius: 3, background: 'color-mix(in oklch, var(--ai-500) 10%, transparent)', color: 'var(--ai-500)', border: '1px solid color-mix(in oklch, var(--ai-500) 25%, transparent)' }}>AI STRATEGIST</span>
      </div>
      <div style={{ fontSize: 16, fontWeight: 600, color: 'var(--text)', marginBottom: 3 }}>Produto</div>
      <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>Identifica o produto, as suas características e os mercados-alvo. Orienta todos os conteúdos gerados.</div>
    </div>

    {/* Cascading selectors */}
    <div style={{
      padding: 16, background: 'var(--bg-sunken)', borderRadius: 8,
      marginBottom: 24, display: 'flex', flexDirection: 'column', gap: 12,
      border: '1px solid var(--border)',
    }}>
      <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--ai-500)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
        Identificação do produto
      </div>
      {isAll && (
        <Field label="Marca">
          <TSelect value={selectedBrand} onChange={onBrandChange}
            options={(allBrands || []).map(b => ({ value: b.slug, label: b.name }))}
            placeholder="Seleccionar marca..." />
        </Field>
      )}
      <div style={{ display: 'grid', gridTemplateColumns: hasSeg ? '1fr 1fr 1fr' : '1fr 1fr', gap: 12 }}>
        {hasSeg && (
          <Field label="Categoria">
            <TSelect value={selectedSegment} onChange={onSegmentChange}
              options={segments.map(s => ({ value: s, label: s }))}
              placeholder={noMarca ? 'Selecciona a marca primeiro' : 'Seleccionar categoria...'} />
          </Field>
        )}
        <Field label={hasSeg ? 'Sub-categoria' : 'Categoria'}>
          <TSelect value={selectedFamily} onChange={onFamilyChange}
            options={familiesAll.map(f => ({ value: f, label: f }))}
            placeholder={noMarca ? 'Selecciona a marca primeiro' : hasSeg && !selectedSegment ? 'Selecciona a categoria primeiro' : 'Seleccionar...'} />
        </Field>
        <Field label="Produto / Modelo">
          <TSelect value={productId ? String(productId) : ''} onChange={onProductChange}
            options={filteredProducts.map(p => ({ value: String(p.id), label: p.name }))}
            placeholder={!selectedFamily ? 'Selecciona a ' + (hasSeg ? 'sub-categoria' : 'categoria') + ' primeiro' : 'Seleccionar...'} />
        </Field>
      </div>
    </div>



    <Field label="Nome comercial" required>
      <TInput value={data.commercial_name} onChange={v => upd('commercial_name', v)} placeholder={ph('commercial_name', selectedBrand)} />
    </Field>
    <Field label="Elevator pitch" hint="1-2 frases que definem o produto de forma clara e diferenciadora" required>
      <TInput value={data.elevator_pitch} onChange={v => upd('elevator_pitch', v)}
        placeholder={ph('elevator_pitch', selectedBrand)} multiline />
    </Field>
    <Field label="USPs — Pontos de valor únicos" hint="Máximo 5" required>
      <ChipInput items={data.usps} onChange={v => upd('usps', v)}
        placeholder={ph('usps', selectedBrand)} maxItems={5} />
    </Field>
    <Field label="Certificações técnicas">
      <ChipInput items={data.certifications} onChange={v => upd('certifications', v)}
        placeholder={ph('certifications', selectedBrand)} />
    </Field>
    <Field label="Aplicações primárias">
      <ChipInput items={data.applications} onChange={v => upd('applications', v)}
        placeholder={ph('applications', selectedBrand)} />
    </Field>
    <Field label="Mercados geográficos alvo" required>
      <ChipInput items={data.geo_markets} onChange={v => upd('geo_markets', v)}
        placeholder={ph('geo_markets', selectedBrand)} />
    </Field>
  </div>
  );
};

const Block2 = ({ data, upd, brand }) => (
  <div>
    <div style={{ marginBottom: 28 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
        <div style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', letterSpacing: '0.08em' }}>BLOCO 2 DE 5</div>
        <span style={{ fontSize: 9, fontFamily: 'var(--font-mono)', letterSpacing: '0.08em', padding: '1px 6px', borderRadius: 3, background: 'color-mix(in oklch, var(--ai-500) 10%, transparent)', color: 'var(--ai-500)', border: '1px solid color-mix(in oklch, var(--ai-500) 25%, transparent)' }}>AI STRATEGIST</span>
      </div>
      <div style={{ fontSize: 16, fontWeight: 600, color: 'var(--text)', marginBottom: 3 }}>Cliente-alvo</div>
      <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>Define o perfil do cliente, as suas dores e o que motiva a decisão de compra.</div>
    </div>
    <Field label="Decisor de compra" required>
      <TInput value={data.decision_maker} onChange={v => upd('decision_maker', v)} placeholder={ph('decision_maker', brand)} />
    </Field>
    <Field label="Utilizador final" required>
      <TInput value={data.end_user} onChange={v => upd('end_user', v)} placeholder={ph('end_user', brand)} />
    </Field>
    <Field label="Dores principais que o produto resolve" required>
      <ChipInput items={data.pain_points} onChange={v => upd('pain_points', v)}
        placeholder={ph('pain_points', brand)} />
    </Field>
    <Field label="Motivadores de compra">
      <ChipInput items={data.motivators} onChange={v => upd('motivators', v)}
        placeholder={ph('motivators', brand)} />
    </Field>
    <Field label="Objecções comuns">
      <ChipInput items={data.objections} onChange={v => upd('objections', v)}
        placeholder={ph('objections', brand)} />
    </Field>
    <Field label="Trigger de compra" hint="O que precipita a decisão de compra" required>
      <TInput value={data.purchase_trigger} onChange={v => upd('purchase_trigger', v)}
        placeholder={ph('purchase_trigger', brand)} />
    </Field>
    <Field label="Fase do funil onde o contacto entra tipicamente">
      <TSelect value={data.funnel_entry_stage} onChange={v => upd('funnel_entry_stage', v)}
        options={[
          { value: 'awareness',      label: 'Awareness' },
          { value: 'consideration',  label: 'Consideração' },
          { value: 'decision',       label: 'Decisão' },
          { value: 'retention',      label: 'Retenção' },
        ]} />
    </Field>
  </div>
);

const Block3 = ({ data, upd, brand }) => (
  <div>
    <div style={{ marginBottom: 28 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
        <div style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', letterSpacing: '0.08em' }}>BLOCO 3 DE 5</div>
        <span style={{ fontSize: 9, fontFamily: 'var(--font-mono)', letterSpacing: '0.08em', padding: '1px 6px', borderRadius: 3, background: 'color-mix(in oklch, var(--ai-500) 10%, transparent)', color: 'var(--ai-500)', border: '1px solid color-mix(in oklch, var(--ai-500) 25%, transparent)' }}>AI STRATEGIST</span>
      </div>
      <div style={{ fontSize: 16, fontWeight: 600, color: 'var(--text)', marginBottom: 3 }}>Mercado</div>
      <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>Mapeia o mercado, os concorrentes e o posicionamento desejado para a marca.</div>
    </div>
    <Field label="Concorrentes directos" required>
      <ChipInput items={data.competitors} onChange={v => upd('competitors', v)}
        placeholder={ph('competitors', brand)} />
    </Field>
    <Field label="Argumentos de diferenciação vs concorrência" required>
      <ChipInput items={data.differentiation_args} onChange={v => upd('differentiation_args', v)}
        placeholder={ph('differentiation_args', brand)} />
    </Field>
    <Field label="Tendências de mercado relevantes">
      <ChipInput items={data.market_trends} onChange={v => upd('market_trends', v)}
        placeholder={ph('market_trends', brand)} />
    </Field>
    <Field label="Narrativa de posicionamento desejada">
      <TInput value={data.positioning_narrative} onChange={v => upd('positioning_narrative', v)}
        placeholder={ph('positioning_narrative', brand)} multiline />
    </Field>
    <Field label="Informação sensível sobre concorrência" hint="Uso interno — não exposto em conteúdos gerados por IA">
      <TInput value={data.sensitive_info} onChange={v => upd('sensitive_info', v)}
        placeholder={ph('sensitive_info', brand)} multiline />
    </Field>
  </div>
);

const Block4 = ({ data, upd, brand, aiSuggested, onSuggestBudget, suggestingBudget, budgetSuggested, budgetRationale, budgetError }) => (
  <div>
    <div style={{ marginBottom: 28 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4, flexWrap: 'wrap' }}>
        <div style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', letterSpacing: '0.08em' }}>BLOCO 4 DE 5</div>
        <span style={{ fontSize: 9, fontFamily: 'var(--font-mono)', letterSpacing: '0.08em', padding: '1px 6px', borderRadius: 3, background: 'color-mix(in oklch, var(--text-muted) 10%, transparent)', color: 'var(--text-muted)', border: '1px solid color-mix(in oklch, var(--text-muted) 25%, transparent)' }}>MARKETING</span>
        {aiSuggested && <span style={{ fontSize: 9, fontFamily: 'var(--font-mono)', letterSpacing: '0.07em', padding: '1px 6px', borderRadius: 3, background: 'color-mix(in oklch, var(--ai-500) 10%, transparent)', color: 'var(--ai-500)', border: '1px solid color-mix(in oklch, var(--ai-500) 25%, transparent)' }}>✦ EXTRAÍDO POR AI — REVÊ E CONFIRMA</span>}
        {budgetSuggested && !aiSuggested && <span style={{ fontSize: 9, fontFamily: 'var(--font-mono)', letterSpacing: '0.07em', padding: '1px 6px', borderRadius: 3, background: 'color-mix(in oklch, var(--success) 10%, transparent)', color: 'var(--success)', border: '1px solid color-mix(in oklch, var(--success) 25%, transparent)' }}>✦ SUGERIDO COM BASE NOS OBJECTIVOS</span>}
        {onSuggestBudget && (
          <button className="btn btn-xs btn-ai" onClick={onSuggestBudget} disabled={suggestingBudget}
            style={{ marginLeft: 'auto', fontSize: 10, display: 'flex', alignItems: 'center', gap: 4 }}>
            {suggestingBudget ? '…' : '✦'} {suggestingBudget ? 'A sugerir…' : 'Sugerir com Objectivos'}
          </button>
        )}
      </div>
      <div style={{ fontSize: 16, fontWeight: 600, color: 'var(--text)', marginBottom: 3 }}>Campanha</div>
      <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>Define os objectivos, canais, tom e mensagem central que guiam a campanha.</div>
      {budgetError && (
        <div style={{ marginTop: 8, padding: '8px 12px', borderRadius: 6, fontSize: 11.5, color: 'var(--danger)', lineHeight: 1.55,
          background: 'color-mix(in oklch, var(--danger) 6%, transparent)', border: '1px solid color-mix(in oklch, var(--danger) 20%, transparent)' }}>
          ⚠ {budgetError}
        </div>
      )}
    </div>
    <Field label="Objectivo primário" required>
      <TSelect value={data.objective} onChange={v => upd('objective', v)}
        options={[
          { value: 'awareness',  label: 'Awareness' },
          { value: 'lead_gen',   label: 'Geração de leads' },
          { value: 'conversion', label: 'Conversão' },
          { value: 'retention',  label: 'Retenção / Upsell' },
        ]} />
    </Field>
    <Field label="KPIs de sucesso" hint="Métricas concretas que definem o sucesso desta campanha">
      <ChipInput items={Array.isArray(data.kpis) ? data.kpis : []} onChange={v => upd('kpis', v)}
        placeholder="ex: 50 leads qualificados · CTR > 2% · €80k em pipeline" />
    </Field>
    <Field label="Canais prioritários" required>
      <ChannelToggle selected={data.channels} onChange={v => upd('channels', v)} />
    </Field>
    <Field label="Tom e linguagem" required>
      <TInput value={data.tone} onChange={v => upd('tone', v)}
        placeholder={ph('tone', brand)} />
    </Field>
    <Field label="Mensagem-chave" hint="A mensagem central que todos os conteúdos devem transmitir" required>
      <TInput value={data.key_message} onChange={v => upd('key_message', v)}
        placeholder={ph('key_message', brand)} multiline />
    </Field>
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
      <Field label="Início da campanha">
        <input type="date" value={data.timeline_start || ''} onChange={e => upd('timeline_start', e.target.value)}
          style={{ width: '100%', padding: '8px 12px', boxSizing: 'border-box', background: 'var(--bg-sunken)', border: '1px solid var(--border)', borderRadius: 6, color: 'var(--text)', fontSize: 13 }} />
      </Field>
      <Field label="Fim da campanha">
        <input type="date" value={data.timeline_end || ''} onChange={e => upd('timeline_end', e.target.value)}
          style={{ width: '100%', padding: '8px 12px', boxSizing: 'border-box', background: 'var(--bg-sunken)', border: '1px solid var(--border)', borderRadius: 6, color: 'var(--text)', fontSize: 13 }} />
      </Field>
    </div>
    <Field label="Campanhas anteriores relevantes">
      <ChipInput items={data.previous_campaigns} onChange={v => upd('previous_campaigns', v)}
        placeholder={ph('previous_campaigns', brand)} />
    </Field>
  </div>
);

const Block5 = ({ data, upd, brand, aiSuggested }) => (
  <div>
    <div style={{ marginBottom: 28 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
        <div style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', letterSpacing: '0.08em' }}>BLOCO 5 DE 5</div>
        <span style={{ fontSize: 9, fontFamily: 'var(--font-mono)', letterSpacing: '0.08em', padding: '1px 6px', borderRadius: 3, background: 'color-mix(in oklch, var(--text-muted) 10%, transparent)', color: 'var(--text-muted)', border: '1px solid color-mix(in oklch, var(--text-muted) 25%, transparent)' }}>MARKETING</span>
        {aiSuggested && <span style={{ fontSize: 9, fontFamily: 'var(--font-mono)', letterSpacing: '0.07em', padding: '1px 6px', borderRadius: 3, background: 'color-mix(in oklch, var(--ai-500) 10%, transparent)', color: 'var(--ai-500)', border: '1px solid color-mix(in oklch, var(--ai-500) 25%, transparent)' }}>✦ EXTRAÍDO POR AI — REVÊ E CONFIRMA</span>}
      </div>
      <div style={{ fontSize: 16, fontWeight: 600, color: 'var(--text)', marginBottom: 3 }}>Restrições</div>
      <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>Estabelece os limites legais, de posicionamento e de mercado para todos os conteúdos gerados.</div>
    </div>
    <Field label="Claims proibidos" hint="Afirmações que não podem ser usadas por razões legais ou de certificação pendente">
      <ChipInput items={data.prohibited_claims} onChange={v => upd('prohibited_claims', v)}
        placeholder={ph('prohibited_claims', brand)} />
    </Field>
    <Field label="Mensagens fora do posicionamento">
      <ChipInput items={data.off_brand_messages} onChange={v => upd('off_brand_messages', v)}
        placeholder={ph('off_brand_messages', brand)} />
    </Field>
    <Field label="Concorrentes que não devem ser nomeados">
      <ChipInput items={data.competitors_not_name} onChange={v => upd('competitors_not_name', v)}
        placeholder={ph('competitors_not_name', brand)} />
    </Field>
    <Field label="Mercados / segmentos fora de âmbito">
      <ChipInput items={data.out_of_scope_markets} onChange={v => upd('out_of_scope_markets', v)}
        placeholder={ph('out_of_scope_markets', brand)} />
    </Field>
    <Field label="Outras restrições">
      <ChipInput items={data.other_restrictions} onChange={v => upd('other_restrictions', v)}
        placeholder={ph('other_restrictions', brand)} />
    </Field>
  </div>
);

// ─── Block 6 — Apreciação AI ──────────────────────────────────────────────────


const Block6 = ({ briefingId, apreciation: initialApreciation, userRole, calcProgressValue, onUpdate, importOrigin, previewData, autoGenerate }) => {
  const [apreciation, setApreciation] = React.useState(initialApreciation || null);
  const [phase, setPhase] = React.useState('idle'); // idle | loading | error
  const [messages, setMessages] = React.useState([]);
  const [currentStep, setCurrentStep] = React.useState(0);
  const [errorMsg, setErrorMsg] = React.useState('');

  const canGenerate = (userRole === 'strategist' || userRole === 'admin') && (!!briefingId || !!previewData);
  const hasContent  = calcProgressValue >= 40;

  const scoreColor = (s) => s >= 90 ? 'var(--success)' : s >= 70 ? 'var(--ai-500)' : s >= 40 ? 'var(--warning)' : 'var(--danger)';
  const scoreLabel = (s) => s >= 90 ? 'Excelente' : s >= 70 ? 'Bom' : s >= 40 ? 'Suficiente' : 'Insuficiente';

  const processLines = (lines) => {
    for (const line of lines) {
      if (!line.startsWith('data: ')) continue;
      let evt; try { evt = JSON.parse(line.slice(6)); } catch { continue; }
      if (evt.type === 'status') { setCurrentStep(evt.step); setMessages(m => [...m, evt.message]); }
      else if (evt.type === 'done') { setApreciation(evt.data); onUpdate?.(evt.data); setPhase('idle'); }
      else if (evt.type === 'error') { setErrorMsg(evt.message); setPhase('error'); }
    }
  };

  const runGenerate = async () => {
    if (!canGenerate) return;
    setPhase('loading'); setMessages([]); setCurrentStep(0); setErrorMsg('');
    try {
      // Modo preview — sem briefingId, usa dados do formulário directamente
      if (!briefingId && previewData) {
        setCurrentStep(2);
        setMessages(['A analisar briefing...']);
        const res = await fetch('/api/marketing/briefings/apreciation-preview', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(previewData),
        });
        if (!res.ok) {
          const j = await res.json().catch(() => ({}));
          throw new Error(j.error || `HTTP ${res.status}`);
        }
        const data = await res.json();
        setApreciation(data);
        onUpdate?.(data);
        setPhase('idle');
        return;
      }
      const url = window.MktBriefingsAPI.apreciationUrl(briefingId);
      const res = await fetch(url, { method: 'POST' });
      if (!res.ok) {
        let msg = `HTTP ${res.status}`;
        try { const j = await res.json(); msg = j.error || msg; } catch {}
        setErrorMsg(msg); setPhase('error'); return;
      }
      const reader = res.body.getReader();
      const decoder = new TextDecoder();
      let buf = '';
      while (true) {
        const { done, value } = await reader.read();
        // Process value even when done=true — last chunk may carry the final SSE event
        if (value) {
          buf += decoder.decode(value, { stream: !done });
          const lines = buf.split('\n');
          buf = done ? '' : (lines.pop() ?? '');
          processLines(lines);
        }
        if (done) {
          // Flush any remaining incomplete line in buf
          if (buf.startsWith('data: ')) processLines([buf]);
          break;
        }
      }
    } catch (e) { setErrorMsg(e.message); setPhase('error'); }
  };

  // Auto-gerar — só quando autoGenerate=true (BriefingForm), nunca em BriefingDetail
  const _autoTriggered = React.useRef(false);
  React.useEffect(() => {
    if (!autoGenerate) return;
    if (_autoTriggered.current) return;
    const alreadyGood = (initialApreciation?.quality_score ?? 0) >= 80;
    if (!alreadyGood && !apreciation && canGenerate && hasContent) {
      _autoTriggered.current = true;
      runGenerate();
    }
  }, [hasContent]);

  const STEPS = [
    { step: 1, label: 'A carregar briefing' },
    { step: 2, label: 'A enviar para Claude AI' },
    { step: 3, label: 'A analisar o briefing' },
    { step: 4, label: 'A estruturar o resumo' },
  ];

  const sources = importOrigin?.sources || [];

  return (
    <div>
      {/* Header */}
      <div style={{ marginBottom: 24 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
          <div style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', letterSpacing: '0.08em' }}>BLOCO 6 DE 6</div>
          <span style={{ fontSize: 9, fontFamily: 'var(--font-mono)', letterSpacing: '0.08em', padding: '1px 6px', borderRadius: 3, background: 'color-mix(in oklch, var(--ai-500) 10%, transparent)', color: 'var(--ai-500)', border: '1px solid color-mix(in oklch, var(--ai-500) 25%, transparent)' }}>AI STRATEGIST</span>
        </div>
        <div style={{ fontSize: 16, fontWeight: 600, color: 'var(--text)', marginBottom: 3 }}>Resumo</div>
        <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>Qualificação do briefing e diagnóstico de pontos fortes, lacunas e alertas.</div>
      </div>

      {/* Fontes importadas */}
      {sources.length > 0 && (
        <div style={{ marginBottom: 20, padding: '10px 14px', borderRadius: 8, background: 'color-mix(in oklch, var(--ai-500) 6%, transparent)', border: '1px solid color-mix(in oklch, var(--ai-500) 20%, transparent)' }}>
          <div style={{ fontSize: 10, fontWeight: 700, color: 'var(--ai-500)', textTransform: 'uppercase', letterSpacing: '0.08em', fontFamily: 'var(--font-mono)', marginBottom: 6 }}>✦ Fontes importadas</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
            {sources.map((s, i) => (
              <div key={i} style={{ fontSize: 12, color: 'var(--text-muted)', display: 'flex', alignItems: 'center', gap: 6 }}>
                <span style={{ fontSize: 10, color: 'var(--ai-500)', flexShrink: 0 }}>{s.type === 'file' ? '📄' : '🔗'}</span>
                <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{s.name || s.url}</span>
              </div>
            ))}
          </div>
        </div>
      )}


      {/* Loading */}
      {phase === 'loading' && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          {STEPS.map(s => {
            const done = currentStep > s.step, active = currentStep === s.step, pending = currentStep < s.step;
            return (
              <div key={s.step} style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                <div style={{
                  width: 28, height: 28, borderRadius: '50%', flexShrink: 0,
                  display: 'grid', placeItems: 'center', fontSize: 12, fontWeight: 600,
                  background: done ? 'color-mix(in oklch, var(--success) 20%, transparent)' : active ? 'color-mix(in oklch, var(--ai-500) 20%, transparent)' : 'var(--bg-sunken)',
                  border: `2px solid ${done ? 'var(--success)' : active ? 'var(--ai-500)' : 'var(--border)'}`,
                  color: done ? 'var(--success)' : active ? 'var(--ai-500)' : 'var(--text-dim)',
                  transition: 'all 0.3s',
                }}>{done ? '✓' : s.step}</div>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 13, fontWeight: active ? 600 : 400, color: pending ? 'var(--text-dim)' : 'var(--text)', transition: 'all 0.3s' }}>{s.label}</div>
                  {active && messages.length > 0 && (
                    <div style={{ fontSize: 11, color: 'var(--ai-500)', marginTop: 2, fontFamily: 'var(--font-mono)' }}>{messages[messages.length - 1]}</div>
                  )}
                </div>
                {active && <div style={{ width: 16, height: 16, borderRadius: '50%', border: '2px solid var(--ai-500)', borderTopColor: 'transparent', animation: 'spin 0.8s linear infinite', flexShrink: 0 }} />}
              </div>
            );
          })}
          <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
        </div>
      )}

      {/* Error */}
      {phase === 'error' && (
        <div style={{ marginBottom: 16, padding: '10px 14px', borderRadius: 8, background: 'color-mix(in oklch, var(--danger) 8%, transparent)', border: '1px solid color-mix(in oklch, var(--danger) 25%, transparent)', color: 'var(--danger)', fontSize: 13 }}>
          {errorMsg}
        </div>
      )}

      {/* Empty state */}
      {phase !== 'loading' && !apreciation && (
        <div style={{ textAlign: 'center', padding: '40px 24px', borderRadius: 10, background: 'var(--bg-sunken)', border: '1px solid var(--border)' }}>
          <div style={{ fontSize: 32, marginBottom: 12 }}>✦</div>
          <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--text)', marginBottom: 6 }}>Apreciação não gerada</div>
          {!hasContent ? (
            <div style={{ fontSize: 12, color: 'var(--text-muted)', marginBottom: 16 }}>Preenche pelo menos 40% dos blocos 1-3 para gerar a apreciação.</div>
          ) : (
            <div style={{ fontSize: 12, color: 'var(--text-muted)', marginBottom: 16 }}>
              A apreciação é <strong>obrigatória</strong> antes de submeter — o score deve atingir ≥ 80 para aprovar a submissão.
            </div>
          )}
          {canGenerate && (
            <button className="btn btn-ai" onClick={runGenerate} disabled={!hasContent} style={{ margin: '0 auto', display: 'inline-flex' }}>
              ✦ Gerar Apreciação
            </button>
          )}
        </div>
      )}

      {/* Generated content */}
      {apreciation && phase !== 'loading' && (() => {
        const score   = apreciation.quality_score || 0;
        const diag    = apreciation.diagnostico || {};
        const bk      = apreciation.breakdown || {};
        const insights = apreciation.insights_melhoria || [];
        const BLOCK_NAMES = { b1: 'Produto', b2: 'Cliente', b3: 'Mercado', b4: 'Campanha', b5: 'Restrições' };
        const scoreReady = score >= 80;
        return (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>

            {/* Score global + estado de submissão */}
            <div style={{
              display: 'flex', alignItems: 'center', gap: 16, padding: '16px 18px', borderRadius: 10,
              background: 'var(--bg-sunken)', border: `1px solid ${scoreReady ? 'color-mix(in oklch, var(--success) 30%, transparent)' : 'var(--border)'}`,
            }}>
              <div style={{ textAlign: 'center', flexShrink: 0 }}>
                <div className="font-display" style={{ fontSize: 38, fontWeight: 700, color: scoreColor(score), lineHeight: 1 }}>{score}</div>
                <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', color: scoreColor(score), letterSpacing: '0.06em', marginTop: 2 }}>{scoreLabel(score)}</div>
              </div>
              <div style={{ flex: 1 }}>
                <div style={{ height: 8, borderRadius: 4, background: 'var(--border)', overflow: 'hidden', marginBottom: 8 }}>
                  <div style={{ width: `${score}%`, height: '100%', background: scoreColor(score), borderRadius: 4, transition: 'width 0.6s ease' }} />
                </div>
                {/* threshold indicator */}
                <div style={{ fontSize: 11, color: scoreReady ? 'var(--success)' : 'var(--warning)', fontWeight: 600 }}>
                  {scoreReady ? '✓ Pronto para submeter' : `Necessário ≥ 80 para submeter · faltam ${80 - score} pontos`}
                </div>
                <div style={{ fontSize: 10, color: 'var(--text-dim)', marginTop: 3, fontFamily: 'var(--font-mono)' }}>
                  Gerado {apreciation.generated_at ? new Date(apreciation.generated_at).toLocaleDateString('pt-PT', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' }) : ''}
                </div>
              </div>
              {canGenerate && (
                <button className="btn btn-sm" onClick={runGenerate} style={{ flexShrink: 0, fontSize: 11 }}>↻ Regenerar</button>
              )}
            </div>

            {/* Resumo narrativo */}
            {apreciation.resumo && (
              <div style={{ padding: '14px 16px', borderRadius: 8, background: 'var(--bg-sunken)', border: '1px solid var(--border)' }}>
                <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', color: 'var(--text-dim)', letterSpacing: '0.07em', marginBottom: 8, textTransform: 'uppercase' }}>Resumo</div>
                <p style={{ margin: 0, fontSize: 13, color: 'var(--text)', lineHeight: 1.65 }}>{apreciation.resumo}</p>
              </div>
            )}

            {/* Breakdown por bloco */}
            {Object.keys(bk).length > 0 && (
              <div style={{ padding: '14px 16px', borderRadius: 8, background: 'var(--bg-sunken)', border: '1px solid var(--border)' }}>
                <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', color: 'var(--text-dim)', letterSpacing: '0.07em', marginBottom: 12, textTransform: 'uppercase' }}>Score por bloco</div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                  {Object.entries(bk).map(([key, val]) => {
                    const s = val?.score ?? 0;
                    return (
                      <div key={key} style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                        <div style={{ width: 72, fontSize: 11, fontWeight: 600, color: 'var(--text-muted)', flexShrink: 0 }}>{BLOCK_NAMES[key] || key}</div>
                        <div style={{ flex: 1, height: 5, borderRadius: 3, background: 'var(--border)', overflow: 'hidden' }}>
                          <div style={{ width: `${s}%`, height: '100%', background: scoreColor(s), borderRadius: 3, transition: 'width 0.5s ease' }} />
                        </div>
                        <div style={{ width: 30, fontSize: 11, fontFamily: 'var(--font-mono)', color: scoreColor(s), fontWeight: 700, textAlign: 'right', flexShrink: 0 }}>{s}</div>
                        {val?.notas && <div style={{ fontSize: 11, color: 'var(--text-muted)', flex: 1 }}>{val.notas}</div>}
                      </div>
                    );
                  })}
                </div>
              </div>
            )}

            {/* Insights de melhoria */}
            {insights.length > 0 && (
              <div style={{ borderRadius: 8, padding: '12px 16px', background: 'color-mix(in oklch, var(--ai-500) 5%, transparent)', border: '1px solid color-mix(in oklch, var(--ai-500) 20%, transparent)' }}>
                <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ai-500)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 10, fontFamily: 'var(--font-mono)' }}>✦ Insights de melhoria</div>
                {insights.map((p, i) => (
                  <div key={i} style={{ fontSize: 12, color: 'var(--text)', marginBottom: 6, paddingLeft: 10, borderLeft: '2px solid color-mix(in oklch, var(--ai-500) 40%, transparent)', lineHeight: 1.55 }}>{p}</div>
                ))}
              </div>
            )}

            {/* Diagnóstico */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {diag.pontos_fortes?.length > 0 && (
                <div style={{ borderRadius: 8, padding: '12px 16px', background: 'color-mix(in oklch, var(--success) 6%, transparent)', border: '1px solid color-mix(in oklch, var(--success) 20%, transparent)' }}>
                  <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--success)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 8, fontFamily: 'var(--font-mono)' }}>✓ Pontos fortes</div>
                  {diag.pontos_fortes.map((p, i) => <div key={i} style={{ fontSize: 12, color: 'var(--text)', marginBottom: 4, paddingLeft: 8, borderLeft: '2px solid color-mix(in oklch, var(--success) 40%, transparent)' }}>{p}</div>)}
                </div>
              )}
              {diag.lacunas?.length > 0 && (
                <div style={{ borderRadius: 8, padding: '12px 16px', background: 'color-mix(in oklch, var(--warning) 6%, transparent)', border: '1px solid color-mix(in oklch, var(--warning) 20%, transparent)' }}>
                  <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--warning)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 8, fontFamily: 'var(--font-mono)' }}>⚠ Lacunas</div>
                  {diag.lacunas.map((p, i) => <div key={i} style={{ fontSize: 12, color: 'var(--text)', marginBottom: 4, paddingLeft: 8, borderLeft: '2px solid color-mix(in oklch, var(--warning) 40%, transparent)' }}>{p}</div>)}
                </div>
              )}
              {diag.alertas?.length > 0 && (
                <div style={{ borderRadius: 8, padding: '12px 16px', background: 'color-mix(in oklch, var(--danger) 6%, transparent)', border: '1px solid color-mix(in oklch, var(--danger) 20%, transparent)' }}>
                  <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--danger)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 8, fontFamily: 'var(--font-mono)' }}>✗ Alertas</div>
                  {diag.alertas.map((p, i) => <div key={i} style={{ fontSize: 12, color: 'var(--text)', marginBottom: 4, paddingLeft: 8, borderLeft: '2px solid color-mix(in oklch, var(--danger) 40%, transparent)' }}>{p}</div>)}
                </div>
              )}
            </div>

          </div>
        );
      })()}
    </div>
  );
};

// ─── BriefingDropdown + BriefingDateRangePicker — nomes únicos para evitar conflito com screen_objectivos.jsx ──────────

const BriefingDropdown = ({ 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 className="animate-in" 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>
  );
};

const BriefingDateRangePicker = ({ value, onChange }) => {
  const ref = React.useRef(null);
  const [open, setOpen] = React.useState(false);
  const [draftStart, setDraftStart] = React.useState(value.start);
  const [draftEnd, setDraftEnd] = React.useState(value.end);
  const [draftPreset, setDraftPreset] = React.useState(value.preset);
  const [anchor, setAnchor] = React.useState(() => {
    const d = new Date(value.end); d.setDate(1); d.setMonth(d.getMonth() - 1); return d;
  });
  const [pickStart, setPickStart] = React.useState(true);

  React.useEffect(() => {
    if (!open) return;
    const onClick = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onClick);
    return () => document.removeEventListener('mousedown', onClick);
  }, [open]);

  const stripTime = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
  const addDays = (d, n) => { const x = new Date(d); x.setDate(x.getDate() + n); return x; };
  const fmt = (d) => d.toLocaleDateString('pt-PT', { day: 'numeric', month: 'short', year: 'numeric' }).replace('.', '');
  const fmtShort = (d) => d.toLocaleDateString('pt-PT', { day: 'numeric', month: 'short' }).replace('.', '');

  const presets = [
    { id: 'all',        label: 'Todo o período' },
    { id: 'custom',     label: 'Personalizado' },
    { id: 'today',      label: 'Hoje' },
    { id: 'yesterday',  label: 'Ontem' },
    { id: 'last_7d',    label: 'Últimos 7 dias' },
    { id: 'last_28d',   label: 'Últimos 28 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: 'qtd',        label: 'Trimestre actual' },
    { id: 'this_year',  label: 'Este ano (Jan – Hoje)' },
    { id: 'last_year',  label: 'Ano passado' },
  ];

  const presetLabels = Object.fromEntries(presets.map(p => [p.id, p.label]));

  const applyPreset = (id) => {
    if (id === 'all') { setDraftPreset('all'); return; }
    const today = stripTime(new Date());
    let s = today, e = today;
    switch (id) {
      case 'today':      break;
      case 'yesterday':  s = e = addDays(today, -1); break;
      case 'last_7d':    s = addDays(today, -6); break;
      case 'last_28d':   s = addDays(today, -27); break;
      case 'last_30d':   s = addDays(today, -29); break;
      case 'last_90d':   s = addDays(today, -89); break;
      case 'this_month': s = new Date(today.getFullYear(), today.getMonth(), 1); break;
      case 'last_month':
        s = new Date(today.getFullYear(), today.getMonth() - 1, 1);
        e = new Date(today.getFullYear(), today.getMonth(), 0);
        break;
      case 'qtd':
        s = new Date(today.getFullYear(), Math.floor(today.getMonth() / 3) * 3, 1);
        break;
      case 'this_year': s = new Date(today.getFullYear(), 0, 1); break;
      case 'last_year':
        s = new Date(today.getFullYear() - 1, 0, 1);
        e = new Date(today.getFullYear() - 1, 11, 31);
        break;
      case 'custom': return;
      default: return;
    }
    setDraftPreset(id);
    setDraftStart(s);
    setDraftEnd(e);
    setAnchor(new Date(e.getFullYear(), e.getMonth() - 1, 1));
    setPickStart(true);
  };

  const handleDayClick = (d) => {
    if (pickStart || !draftStart || (draftStart && draftEnd && d < draftStart)) {
      setDraftStart(d); setDraftEnd(d); setPickStart(false); setDraftPreset('custom');
    } else {
      if (d < draftStart) { setDraftStart(d); }
      else { setDraftEnd(d); setPickStart(true); }
      setDraftPreset('custom');
    }
  };

  const apply = () => {
    onChange({ preset: draftPreset, start: draftStart, end: draftEnd });
    setOpen(false);
  };
  const cancel = () => {
    setDraftStart(value.start); setDraftEnd(value.end); setDraftPreset(value.preset);
    setOpen(false);
  };

  const triggerLabel = presetLabels[value.preset] || 'Personalizado';
  const triggerRange = value.preset === 'all' ? '' : `${fmtShort(value.start)} – ${fmt(value.end)}`;

  const Month = ({ date }) => {
    const y = date.getFullYear(), m = date.getMonth();
    const firstDay = new Date(y, m, 1).getDay();
    const daysInMonth = new Date(y, m + 1, 0).getDate();
    const monthLabel = date.toLocaleDateString('pt-PT', { month: 'long', year: 'numeric' }).toUpperCase();
    const cells = [];
    for (let i = 0; i < firstDay; i++) cells.push(null);
    for (let d = 1; d <= daysInMonth; d++) cells.push(new Date(y, m, d));
    const inRange = (d) => draftStart && draftEnd && d >= draftStart && d <= draftEnd;
    const isStart = (d) => draftStart && d.getTime() === stripTime(draftStart).getTime();
    const isEnd   = (d) => draftEnd   && d.getTime() === stripTime(draftEnd).getTime();
    return (
      <div style={{ marginBottom: 10 }}>
        <div style={{ fontSize: 10.5, fontFamily: 'var(--font-mono)', color: 'var(--text-dim)', letterSpacing: '.08em', padding: '4px 6px' }}>{monthLabel}</div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 1, fontSize: 11 }}>
          {cells.map((d, i) => {
            if (!d) return <div key={i} />;
            const r = inRange(d), s = isStart(d), e = isEnd(d);
            const isEdge = s || e;
            return (
              <button key={i} onClick={() => handleDayClick(d)} style={{
                width: 30, height: 30, lineHeight: '30px', textAlign: 'center', padding: 0,
                border: 'none', cursor: 'pointer', fontFamily: 'inherit',
                background: isEdge ? 'var(--ai-500)' : (r ? 'color-mix(in oklch, var(--ai-500) 18%, transparent)' : 'transparent'),
                color: isEdge ? '#fff' : (r ? 'var(--ai-500)' : 'var(--text)'),
                borderRadius: isEdge ? '50%' : (r ? 0 : 4),
                fontWeight: isEdge ? 600 : 500, fontSize: 11.5,
                transition: 'background .12s',
              }}
              onMouseEnter={(ev) => { if (!isEdge && !r) ev.currentTarget.style.background = 'var(--bg-hover)'; }}
              onMouseLeave={(ev) => { if (!isEdge && !r) ev.currentTarget.style.background = 'transparent'; }}>
                {d.getDate()}
              </button>
            );
          })}
        </div>
      </div>
    );
  };

  const nextAnchor = new Date(anchor.getFullYear(), anchor.getMonth() + 1, 1);

  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button onClick={() => setOpen(v => !v)} style={{
        background: 'var(--bg-elev, #ffffff)', color: '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: 'pointer', fontFamily: 'inherit',
      }}>
        <span style={{ color: 'var(--text-dim, #475569)', fontSize: 10, textTransform: 'uppercase', letterSpacing: '.06em', fontFamily: 'var(--font-mono, monospace)' }}>Período</span>
        <span style={{ fontWeight: 600, color: 'var(--ai-500, #3859D0)', background: 'color-mix(in oklch, var(--ai-500, #3859D0) 12%, transparent)', padding: '1px 6px', borderRadius: 3 }}>{triggerLabel}</span>
        {triggerRange && <span style={{ color: 'var(--text-muted, #64748b)', fontFamily: 'var(--font-mono, monospace)', fontSize: 11 }}>{triggerRange}</span>}
        <span style={{ color: 'var(--text-muted, #64748b)', fontSize: 9 }}>▾</span>
      </button>

      {open && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 6px)', right: 0, zIndex: 60,
          background: 'var(--bg-elev, #ffffff)', border: '1px solid var(--border, #dde3ef)', borderRadius: 8,
          boxShadow: '0 12px 32px rgba(0,0,0,0.18)',
          display: 'flex', minWidth: 580, maxWidth: 640,
        }}>
          <div style={{ width: 200, borderRight: '1px solid var(--border, #dde3ef)', padding: '6px 0', maxHeight: 460, overflowY: 'auto' }}>
            {presets.map(p => (
              <button key={p.id} onClick={() => applyPreset(p.id)} style={{
                display: 'block', width: '100%', textAlign: 'left',
                background: draftPreset === p.id ? 'color-mix(in oklch, var(--ai-500, #3859D0) 12%, transparent)' : 'transparent',
                color: draftPreset === p.id ? 'var(--ai-500, #3859D0)' : 'var(--text, #112954)',
                border: 'none', padding: '8px 14px',
                fontSize: 12.5, fontWeight: draftPreset === p.id ? 600 : 500,
                cursor: 'pointer', fontFamily: 'inherit',
              }}>{p.label}</button>
            ))}
          </div>
          <div style={{ flex: 1, padding: 14, display: 'flex', flexDirection: 'column' }}>
            <div style={{ display: 'flex', gap: 8, marginBottom: 10 }}>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 9.5, color: 'var(--text-dim, #475569)', textTransform: 'uppercase', letterSpacing: '.08em', fontFamily: 'var(--font-mono, monospace)', marginBottom: 3 }}>Data início</div>
                <div style={{ border: `1px solid ${pickStart ? 'var(--ai-500, #3859D0)' : 'var(--border, #dde3ef)'}`, borderRadius: 4, padding: '5px 8px', fontSize: 12, background: 'var(--bg, #f5f7fa)' }}>
                  {fmt(draftStart)}
                </div>
              </div>
              <div style={{ alignSelf: 'flex-end', padding: '6px 0', color: 'var(--text-muted, #64748b)' }}>—</div>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 9.5, color: 'var(--text-dim, #475569)', textTransform: 'uppercase', letterSpacing: '.08em', fontFamily: 'var(--font-mono, monospace)', marginBottom: 3 }}>Data fim</div>
                <div style={{ border: `1px solid ${!pickStart ? 'var(--ai-500, #3859D0)' : 'var(--border, #dde3ef)'}`, borderRadius: 4, padding: '5px 8px', fontSize: 12, background: 'var(--bg, #f5f7fa)' }}>
                  {fmt(draftEnd)}
                </div>
              </div>
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }}>
              <button onClick={() => setAnchor(new Date(anchor.getFullYear(), anchor.getMonth() - 1, 1))} style={{ background: 'none', border: '1px solid var(--border, #dde3ef)', borderRadius: 4, width: 24, height: 24, color: 'var(--text-muted, #64748b)', cursor: 'pointer' }}>‹</button>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 1, flex: 1, padding: '0 8px' }}>
                {['D','S','T','Q','Q','S','S'].map((d, i) => (
                  <div key={i} style={{ textAlign: 'center', fontSize: 10, color: 'var(--text-dim, #475569)', fontFamily: 'var(--font-mono, monospace)', textTransform: 'uppercase' }}>{d}</div>
                ))}
              </div>
              <button onClick={() => setAnchor(new Date(anchor.getFullYear(), anchor.getMonth() + 1, 1))} style={{ background: 'none', border: '1px solid var(--border, #dde3ef)', borderRadius: 4, width: 24, height: 24, color: 'var(--text-muted, #64748b)', cursor: 'pointer' }}>›</button>
            </div>
            <div style={{ flex: 1, overflowY: 'auto', maxHeight: 360, padding: '0 30px' }}>
              <Month date={anchor} />
              <Month date={nextAnchor} />
            </div>
            <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 10, paddingTop: 10, borderTop: '1px solid var(--border, #dde3ef)' }}>
              <button onClick={cancel} className="btn btn-xs">Cancelar</button>
              <button onClick={apply} className="btn btn-xs btn-ai">Aplicar</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

// ─── Lista de briefings ────────────────────────────────────────────────────────

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 CHANNEL_CHIPS = {
  email:      { abbr: 'EML' },
  linkedin:   { abbr: 'LIN' },
  google_ads: { abbr: 'GGL' },
  facebook:   { abbr: 'FB'  },
  instagram:  { abbr: 'IG'  },
  youtube:    { abbr: 'YT'  },
  social:     { abbr: 'SOC' },
  site:       { abbr: 'WEB' },
  whatsapp:   { abbr: 'WA'  },
  led:        { abbr: 'LED' },
  ads:        { abbr: 'ADS', color: '#ef4444', bg: '#7f1d1d15' },
};

const SortHeader = ({ label, sortKey, sortBy, onSort, width, style: extraStyle }) => {
  const active = sortBy.key === sortKey;
  return (
    <th style={{ ...thSt, width, cursor: sortKey ? 'pointer' : 'default', userSelect: 'none', ...extraStyle }}
      onClick={() => sortKey && onSort(sortKey)}>
      <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4 }}>
        {label}
        {active && sortKey && window.Icon &&
          <window.Icon name={sortBy.dir === 'asc' ? 'arrowUp' : 'arrowDown'} size={9} style={{ color: 'var(--ai-500)' }} />}
      </span>
    </th>
  );
};

const BriefingRow = ({ b, isAll, onSelect, onEdit, onDelete, onSubmit, onGenerateStrategy, onCriarCampanha, attentionColor, selected, onToggle, rowIndex }) => {
  const [menuOpen, setMenuOpen] = React.useState(false);
  const [confirming, setConfirming] = React.useState(false);
  const [hovered, setHovered] = React.useState(false);
  const menuRef = React.useRef(null);
  const pct = parseInt(b.completion_pct) || 0;
  const pctColor = pct >= 80 ? 'var(--success)' : pct >= 40 ? 'var(--warning)' : 'var(--text-muted)';
  const defaultBg = attentionColor ? `color-mix(in oklch, ${attentionColor} 6%, transparent)` : 'transparent';
  const stickyBg = hovered ? 'var(--bg-hover)' : attentionColor ? `color-mix(in oklch, ${attentionColor} 6%, var(--bg))` : 'var(--bg)';

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

  return (
    <tr
      onClick={onSelect}
      onMouseEnter={() => setHovered(true)}
      onMouseLeave={() => setHovered(false)}
      style={{ background: hovered ? 'var(--bg-hover)' : defaultBg, boxShadow: attentionColor ? `inset 3px 0 0 ${attentionColor}` : 'none', cursor: 'pointer', borderBottom: '1px solid var(--border)', transition: 'background 150ms ease', animation: 'slide-up 0.2s ease-out both', animationDelay: `${Math.min(rowIndex || 0, 9) * 25}ms` }}>
      {/* ── Checkbox ── */}
      <td style={{ padding: '4px 4px', textAlign: 'center', width: 36 }} onClick={e => e.stopPropagation()}>
        <input type="checkbox" checked={!!selected} onChange={onToggle}
          style={{ width: 14, height: 14, cursor: 'pointer', accentColor: 'var(--ai-500, #3859D0)' }} />
      </td>
      {/* ── Nome Comercial — sticky ── */}
      <td style={{ padding: '10px 12px', overflow: 'hidden' }}>
        <div style={{ fontWeight: 500, color: 'var(--text)', fontSize: 13, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
          {b.commercial_name || b.product_name || <span style={{ color: 'var(--text-dim)', fontStyle: 'italic', fontWeight: 400 }}>Sem produto</span>}
        </div>
      </td>
      {/* ── Marca ── */}
      {isAll && (
        <td style={{ padding: '10px 12px', overflow: 'hidden' }}>
          <span style={{ fontSize: 11, fontFamily: 'var(--font-display)', fontWeight: 700, letterSpacing: '0.06em', color: b.brand_color || 'var(--text-muted)', display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
            {(b.brand_name || '').toUpperCase()}
          </span>
        </td>
      )}
      {/* ── Segmento ── */}
      <td style={{ padding: '10px 12px', overflow: 'hidden' }}>
        {b.product_segment
          ? <span style={{ fontSize: 12, color: 'var(--text-muted)', display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{b.product_segment}</span>
          : <span style={{ color: 'var(--text-dim)', fontSize: 12 }}>—</span>
        }
      </td>
      {/* ── Estado ── */}
      <td style={{ padding: '10px 12px' }}>
        {(() => {
          const m = {
            rascunho:     ['Rascunho',     '#475569', '#e2e8f0'],
            submetido:    ['Submetido',    '#92400e', '#fef3c7'],
            validado_ceo: ['Validado CEO', '#6b21a8', '#f3e8ff'],
            aprovado:     ['Aprovado',     '#14532d', '#dcfce7'],
            rejeitado:    ['Rejeitado',    '#991b1b', '#fee2e2'],
            em_campanha:  ['Em Campanha',  '#1e3a8a', '#dbeafe'],
          };
          const [l, c, bg] = m[b.status] || m.rascunho;
          return <span style={{ fontSize: 11, fontFamily: 'var(--font-mono)', fontWeight: 600, padding: '2px 8px', borderRadius: 4, display: 'inline-block', color: c, background: bg }}>{l.toUpperCase()}</span>;
        })()}
      </td>
      {/* ── Qualidade ── */}
      <td style={{ padding: '10px 12px' }}>
        {b.quality_score != null ? (
          <span style={{ fontSize: 12, fontFamily: 'var(--font-mono)', fontWeight: 700, color: b.quality_score >= 80 ? '#15803d' : b.quality_score >= 60 ? '#b45309' : '#dc2626' }}>
            {b.quality_score}<span style={{ fontSize: 10, fontWeight: 400, color: 'var(--text-dim)' }}>/100</span>
          </span>
        ) : (
          <div style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
            <div style={{ flex: 1, height: 4, borderRadius: 2, background: 'var(--border)', overflow: 'hidden', minWidth: 36 }}>
              <div style={{ height: '100%', borderRadius: 2, width: `${pct}%`, background: pct >= 80 ? 'var(--success)' : pct >= 40 ? 'var(--warning)' : 'var(--danger)' }} />
            </div>
            <span style={{ fontSize: 10.5, fontFamily: 'var(--font-mono)', color: pctColor, whiteSpace: 'nowrap' }}>{pct}%</span>
          </div>
        )}
      </td>
      {/* ── Canais ── */}
      <td style={{ padding: '10px 12px' }}>
        {(() => {
          const channels = Array.isArray(b.campaign_channels) ? b.campaign_channels : [];
          if (!channels.length) return <span style={{ color: 'var(--text-dim)', fontSize: 12 }}>—</span>;
          return (
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 3 }}>
              {channels.map((ch, idx) => {
                const abbr = (CHANNEL_CHIPS[ch] || { abbr: ch.slice(0,3).toUpperCase() }).abbr;
                return <React.Fragment key={ch}><span style={{ fontSize: 11, fontFamily: 'var(--font-mono)', color: 'var(--text-muted)' }}>{abbr}</span>{idx < channels.length - 1 && <span style={{ color: 'var(--text-dim)', margin: '0 3px' }}>·</span>}</React.Fragment>;
              })}
            </div>
          );
        })()}
      </td>
      {/* ── Actualizado ── */}
      <td style={{ padding: '10px 12px' }}>
        <span style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-muted)' }}>
          {b.updated_at ? new Date(b.updated_at).toLocaleDateString('pt-PT') : '—'}
        </span>
      </td>
      {/* ── Criado por ── */}
      <td style={{ padding: '10px 12px', overflow: 'hidden' }}>
        <span style={{ fontSize: 12, color: 'var(--text)', fontWeight: 400, display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
          {b.created_by_name || <span style={{ color: 'var(--text-dim)' }}>—</span>}
        </span>
      </td>
      {/* ── Aprovado por ── */}
      <td style={{ padding: '10px 12px', overflow: 'hidden' }}>
        {b.approved_by_name
          ? <span style={{ fontSize: 12, color: 'var(--success)', fontWeight: 500, display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{b.approved_by_name}</span>
          : <span style={{ color: 'var(--text-dim)', fontSize: 12 }}>—</span>
        }
      </td>

      {/* ── Kebab menu ── */}
      <td style={{ padding: '4px 6px', textAlign: 'right' }} onClick={e => e.stopPropagation()}>
        <div ref={menuRef} style={{ position: 'relative', display: 'inline-block' }}>
          <button
            onClick={() => { setMenuOpen(o => !o); if (menuOpen) setConfirming(false); }}
            style={{
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              width: 28, height: 28, borderRadius: 5, cursor: 'pointer',
              background: menuOpen ? 'var(--bg-elev)' : 'none',
              border: `1px solid ${menuOpen ? 'var(--border)' : 'transparent'}`,
              color: 'var(--text-muted)', transition: 'all 100ms',
            }}
            onMouseEnter={e => { if (!menuOpen) { e.currentTarget.style.background = 'var(--bg-elev)'; e.currentTarget.style.borderColor = 'var(--border)'; } }}
            onMouseLeave={e => { if (!menuOpen) { e.currentTarget.style.background = 'none'; e.currentTarget.style.borderColor = 'transparent'; } }}
          >
            {window.Icon ? <window.Icon name="more" size={14} /> : '⋮'}
          </button>

          {menuOpen && (
            <div className="animate-in" style={{
              position: 'absolute', top: 'calc(100% + 4px)', right: 0, zIndex: 60,
              background: 'var(--bg-elev, #ffffff)', border: '1px solid var(--border, #dde3ef)',
              borderRadius: 6, minWidth: 176, padding: 4,
              boxShadow: '0 8px 24px rgba(17,41,84,0.12)',
            }}>
              {confirming ? (
                <div style={{ padding: '10px 10px', display: 'flex', flexDirection: 'column', gap: 8 }}>
                  <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text, #112954)' }}>Eliminar briefing?</div>
                  <div style={{ fontSize: 11, color: 'var(--text-muted, #4a5e7a)', lineHeight: 1.4 }}>Esta acção não pode ser revertida.</div>
                  <div style={{ display: 'flex', gap: 5 }}>
                    <button onClick={() => setConfirming(false)} className="btn btn-xs" style={{ flex: 1, fontSize: 11 }}>Cancelar</button>
                    <button onClick={() => { setMenuOpen(false); setConfirming(false); onDelete(); }} className="btn btn-xs" style={{ flex: 1, fontSize: 11, background: 'var(--danger)', color: '#fff', borderColor: 'var(--danger)' }}>Eliminar</button>
                  </div>
                </div>
              ) : (
                <>
                  <button onClick={() => { setMenuOpen(false); onEdit(); }}
                    style={{ display: 'block', width: '100%', textAlign: 'left', background: 'transparent', border: 'none', padding: '7px 10px', borderRadius: 4, fontSize: 12, fontWeight: 500, color: 'var(--text, #112954)', cursor: 'pointer', fontFamily: 'inherit' }}
                    onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover, #f1f5f9)'}
                    onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
                    Editar
                  </button>
                  {(b.status === 'rascunho' || b.status === 'rejeitado') && onSubmit && (
                    <button onClick={() => { setMenuOpen(false); onSubmit(b); }}
                      style={{ display: 'block', width: '100%', textAlign: 'left', background: 'transparent', border: 'none', padding: '7px 10px', borderRadius: 4, fontSize: 12, fontWeight: 500, color: 'var(--text, #112954)', cursor: 'pointer', fontFamily: 'inherit', whiteSpace: 'nowrap' }}
                      onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover, #f1f5f9)'}
                      onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
                      {b.status === 'rejeitado' ? 'Submeter novamente' : 'Submeter'}
                    </button>
                  )}
                  {b.status === 'aprovado' && onCriarCampanha && (
                    <button onClick={() => { setMenuOpen(false); onCriarCampanha(b); }}
                      style={{ display: 'block', width: '100%', textAlign: 'left', background: 'transparent', border: 'none', padding: '7px 10px', borderRadius: 4, fontSize: 12, fontWeight: 500, color: 'var(--text, #112954)', cursor: 'pointer', fontFamily: 'inherit', whiteSpace: 'nowrap' }}
                      onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover, #f1f5f9)'}
                      onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
                      Criar Campanha
                    </button>
                  )}
                  <button onClick={() => setConfirming(true)}
                    style={{ display: 'block', width: '100%', textAlign: 'left', background: 'transparent', border: 'none', padding: '7px 10px', borderRadius: 4, fontSize: 12, fontWeight: 500, color: 'var(--danger, #dc2626)', cursor: 'pointer', fontFamily: 'inherit' }}
                    onMouseEnter={e => e.currentTarget.style.background = 'color-mix(in oklch, var(--danger, #dc2626) 8%, transparent)'}
                    onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
                    Apagar
                  </button>
                </>
              )}
            </div>
          )}
        </div>
      </td>

    </tr>
  );
};

// ─── Digi AI Banner ────────────────────────────────────────────────────────────
const DigiAIBanner = ({ kpi, userRole, userName, onFilter }) => {
  const hour = new Date().getHours();
  // pickSeed: random float fixed on mount → different variant each time user enters screen
  const [pickSeed] = React.useState(Math.random);

  const { heading, segments } = React.useMemo(() => {
    const isBoard = userRole === 'board' || userRole === 'admin';
    const rawName = userName || (window.currentUser && (window.currentUser.nome_apresentar || window.currentUser.nome)) || '';
    const firstName = rawName.split(' ')[0] || '';
    const pl = (n, s, p) => n === 1 ? s : p;

    const saudacao = hour < 12 ? 'Bom dia' : hour < 19 ? 'Boa tarde' : 'Boa noite';
    const heading  = firstName ? `${saudacao}, ${firstName}.` : `${saudacao}.`;
    const manha = hour < 12, tarde = hour >= 12 && hour < 19;
    const timeCtx = manha ? 'esta manhã' : tarde ? 'esta tarde' : 'hoje';
    const timeAct = manha ? 'Começa o dia' : tarde ? 'Aproveita a tarde' : 'Antes de terminar o dia';

    // cada segmento: { text, filter?, color? }
    // color: 'warning' | 'danger' | 'ai' | 'success'
    let pool;

    if (isBoard) {
      if (kpi.submitted > 0) {
        const n = kpi.submitted;
        pool = [
          [
            { text: `${n} ${pl(n,'briefing aguarda','briefings aguardam')} a tua aprovação`, filter: 'submetido', color: 'warning' },
            { text: ` ${timeCtx}. A Digi AI fica pronta para arrancar com a produção de conteúdo assim que deres o go.` },
          ],
          [
            { text: `Tens ` },
            { text: `${n} ${pl(n,'briefing','briefings')} pendente${n>1?'s':''} de aprovação`, filter: 'submetido', color: 'warning' },
            { text: `. Dá o go para a Digi AI começar a gerar conteúdo.` },
          ],
          [
            { text: `A equipa está à espera. ` },
            { text: `${n} ${pl(n,'briefing','briefings')}`, filter: 'submetido', color: 'warning' },
            { text: ` ${pl(n,'aguarda','aguardam')} o teu go. ${timeAct} com as aprovações.` },
          ],
        ];
      } else if (kpi.total === 0) {
        pool = [
          [{ text: `O pipeline de conteúdo ainda está vazio. A equipa de AI Strategist ainda não criou briefings. Podes criar o primeiro directamente aqui.` }],
          [{ text: `Ainda não há briefings criados. Quando a equipa começar a preencher, vais ver tudo aqui para aprovar.` }],
        ];
      } else if (kpi.approved > 0) {
        pool = [
          [
            { text: `Pipeline a funcionar. ` },
            { text: `${kpi.approved} ${pl(kpi.approved,'briefing aprovado','briefings aprovados')}`, filter: 'aprovado', color: 'ai' },
            { text: ` em produção de conteúdo pela Digi AI. Sem aprovações pendentes.` },
          ],
          [
            { text: `A Digi AI está a trabalhar. ` },
            { text: `${kpi.approved} ${pl(kpi.approved,'briefing','briefings')}`, filter: 'aprovado', color: 'ai' },
            { text: ` aprovado${kpi.approved>1?'s':''}. Nada pendente da tua parte.` },
          ],
        ];
      } else {
        pool = [[{ text: `Sem aprovações pendentes. Tudo em ordem.` }]];
      }
    } else {
      if (kpi.rejected > 0) {
        const n = kpi.rejected;
        pool = [
          [
            { text: `${n} ${pl(n,'briefing foi rejeitado','briefings foram rejeitados')} pelo board`, filter: 'rejeitado', color: 'danger' },
            { text: `. Revê o feedback, corrige os pontos assinalados e submete novamente. A Digi AI fica pronta assim que aprovado.` },
          ],
          [
            { text: `O board pediu revisão em ` },
            { text: `${n} ${pl(n,'briefing','briefings')}`, filter: 'rejeitado', color: 'danger' },
            { text: `. Corrige o que foi assinalado e volta a submeter.` },
          ],
        ];
      } else if (kpi.approved > 0) {
        const n = kpi.approved;
        pool = [
          [
            { text: `${timeAct}: ` },
            { text: `${n} ${pl(n,'briefing aprovado','briefings aprovados')}`, filter: 'aprovado', color: 'ai' },
            { text: ` ${pl(n,'está pronto','estão prontos')} para a Digi AI gerar conteúdo. Abre qualquer um e clica em Gerar Conteúdo.` },
          ],
          [
            { text: `A Digi AI está pronta. Tens ` },
            { text: `${n} ${pl(n,'briefing aprovado','briefings aprovados')}`, filter: 'aprovado', color: 'ai' },
            { text: ` para gerar conteúdo. É só abrir e clicar.` },
          ],
          [
            { text: `${n} ${pl(n,'briefing','briefings')} `, filter: 'aprovado', color: 'ai' },
            { text: `aprovado${n>1?'s':''} à espera. A Digi AI gera o conteúdo assim que abrires.` },
          ],
        ];
      } else if (kpi.submitted > 0 && kpi.draft === 0) {
        const n = kpi.submitted;
        pool = [
          [
            { text: `${n} ${pl(n,'briefing em revisão','briefings em revisão')} pelo board`, filter: 'submetido', color: 'warning' },
            { text: `. Aproveita para preparar o próximo. Quanto mais briefings completos tiveres, mais a Digi AI pode gerar.` },
          ],
          [
            { text: `O board está a rever ` },
            { text: `${n} ${pl(n,'briefing','briefings')}`, filter: 'submetido', color: 'warning' },
            { text: `. Enquanto aguardas, podes já preparar o próximo.` },
          ],
        ];
      } else if (kpi.draft > 0) {
        const n = kpi.draft;
        const v1 = [
          { text: `${timeAct}: ` },
          { text: `${n} ${pl(n,'rascunho por completar','rascunhos por completar')}`, filter: 'rascunho', color: 'warning' },
          { text: `. Quanto mais sólido o briefing, melhor o conteúdo que a Digi AI vai gerar.` },
        ];
        const v2 = [
          { text: `Tens ` },
          { text: `${n} ${pl(n,'rascunho incompleto','rascunhos incompletos')}`, filter: 'rascunho', color: 'warning' },
          { text: `. Completa-${n===1?'o':'os'} para a Digi AI ter o contexto todo.` },
        ];
        if (kpi.submitted > 0) {
          const extra = [
            { text: ` Tens também ` },
            { text: `${kpi.submitted} ${pl(kpi.submitted,'briefing','briefings')} em revisão`, filter: 'submetido', color: 'ai' },
            { text: `.` },
          ];
          pool = [[...v1, ...extra], [...v2, ...extra]];
        } else {
          pool = [v1, v2];
        }
      } else if (kpi.total === 0) {
        pool = [
          [
            { text: `O pipeline de conteúdo da Digi AI começa aqui. Ainda não há briefings. ` },
            { text: `Cria o primeiro`, color: 'warning' },
            { text: ` ou importa um documento existente para a IA preencher automaticamente.` },
          ],
          [
            { text: `Começa por criar o primeiro briefing. A Digi AI usa-o como base para gerar todo o conteúdo desta marca.` },
          ],
        ];
      } else {
        pool = [[{ text: `Tudo em ordem. A Digi AI está pronta para gerar conteúdo assim que tiveres um briefing aprovado.` }]];
      }
    }

    return { heading, segments: pool[Math.floor(pickSeed * pool.length)] };
  }, [kpi, userRole, userName, hour, pickSeed]);

  const segColor = c => c === 'danger' ? 'var(--danger)' : c === 'ai' ? 'var(--ai-500)' : c === 'success' ? 'var(--success)' : 'var(--warning)';

  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.65 }}>
        {segments.map((seg, i) => seg.color ? (
          <span key={i}
            onClick={seg.filter ? () => onFilter(seg.filter) : undefined}
            style={{
              color: segColor(seg.color),
              cursor: seg.filter ? 'pointer' : 'default',
              fontWeight: 500,
            }}>{seg.text}</span>
        ) : (
          <span key={i}>{seg.text}</span>
        ))}
      </div>
    </div>
  );
};

// ─── Painel de aprovações ──────────────────────────────────────────────────────
const ApprovalPanel = ({ briefing, approvals = [], userEmail, onNoteChange, noteValue, noteError, children }) => {
  const brandSlug   = briefing?.brand_slug;
  const required    = getRequiredApprovers(brandSlug);
  const curStatus   = briefing?.status;
  const activeRole  = getActiveRole(curStatus, required.map(r => r.role));

  const actionLabel = (action) => ({
    approved:          'aprovado',
    rejected:          'rejeitado',
    changes_requested: 'alterações pedidas',
  }[action] || action);

  return (
    <div style={{ marginBottom: 24, padding: 16, borderRadius: 8, background: 'rgba(217,119,6,.07)', border: '1px solid rgba(217,119,6,.3)' }}>
      <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text)', marginBottom: 14, letterSpacing: '0.03em' }}>
        Fluxo de aprovação
      </div>

      {/* Pipeline visual por fase */}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 18 }}>
        {required.map(({ role, name }, idx) => {
          const aprov      = approvals.find(a => a.role === role);
          const ok         = aprov?.action === 'approved';
          const nok        = aprov?.action === 'rejected' || aprov?.action === 'changes_requested';
          const isActive   = role === activeRole && !aprov;
          const isPending  = !aprov && role !== activeRole;
          const stageLabel = role === 'brand_dir' ? 'Fase 1 · CEO' : `Fase ${required.length > 1 ? '2' : '1'} · Chairman`;

          return (
            <div key={role} style={{
              display: 'flex', alignItems: 'flex-start', gap: 12,
              padding: '10px 12px', borderRadius: 7,
              border: `1px solid ${isActive ? 'rgba(217,119,6,.5)' : ok ? 'rgba(21,128,61,.25)' : nok ? 'rgba(220,38,38,.25)' : 'var(--border)'}`,
              background: isActive ? 'rgba(217,119,6,.05)' : ok ? 'rgba(21,128,61,.04)' : nok ? 'rgba(220,38,38,.04)' : 'transparent',
              opacity: isPending ? 0.5 : 1,
            }}>
              <span style={{ fontSize: 16, width: 20, textAlign: 'center', flexShrink: 0, marginTop: 1,
                color: ok ? '#15803d' : nok ? '#dc2626' : isActive ? '#d97706' : 'var(--text-dim)' }}>
                {ok ? '✓' : nok ? '✗' : isActive ? '◉' : '○'}
              </span>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', color: 'var(--text-dim)', letterSpacing: '0.06em', marginBottom: 2 }}>
                  {stageLabel}
                </div>
                <div style={{ fontSize: 13, fontWeight: 500, color: ok ? '#15803d' : nok ? '#dc2626' : 'var(--text)' }}>
                  {name}
                  {isActive && <span style={{ fontSize: 11, color: '#d97706', marginLeft: 8, fontWeight: 400 }}>· a aguardar decisão</span>}
                  {isPending && !aprov && <span style={{ fontSize: 11, color: 'var(--text-dim)', marginLeft: 8, fontWeight: 400 }}>· aguarda fase anterior</span>}
                </div>
                {aprov && (
                  <div style={{ fontSize: 11, color: 'var(--text-dim)', marginTop: 2 }}>
                    {actionLabel(aprov.action)} por {aprov.approver_name || name}
                    {aprov.notes ? ` · "${aprov.notes}"` : ''}
                  </div>
                )}
              </div>
            </div>
          );
        })}
      </div>

      <Field label="Notas (obrigatório se rejeitar ou pedir alterações, opcional se aprovar)">
        <TInput value={noteValue} onChange={onNoteChange}
          placeholder="Sugestões de melhoria, campos a corrigir, ajustes de posicionamento..." multiline />
      </Field>
      {noteError && <div style={{ marginTop: 8, fontSize: 12, color: 'var(--danger)', fontWeight: 500 }}>{noteError}</div>}

      {children}
    </div>
  );
};

const BriefingsList = ({ brandId, onSelect, onNew, onEdit, onImport, onGenerateStrategy, onCriarCampanha, userRole, userName }) => {
  const [briefings, setBriefings] = React.useState(null);
  const [filterEstado, setFilterEstado] = React.useState('all');
  const [filterMarca, setFilterMarca] = React.useState('all');
  const [filterProduto, setFilterProduto] = React.useState('all');
  const [filterCriador, setFilterCriador] = React.useState('all');
  const isAll = !brandId || brandId === 'todas';

  const today = React.useMemo(() => { const d = new Date(); d.setHours(23,59,59,999); return d; }, []);
  const [dateRange, setDateRange] = React.useState({ preset: 'all', start: new Date(today.getFullYear(), 0, 1), end: today });

  const load = React.useCallback(() => {
    setBriefings(null);
    window.MktBriefingsAPI.list(isAll ? 'todas' : brandId).then(d => setBriefings(d || []));
  }, [brandId]);

  React.useEffect(load, [load]);

  const handleDelete = async (id) => {
    await window.MktBriefingsAPI.remove(id);
    load();
  };

  const handleSubmitRow = async (b) => {
    if ((parseInt(b.completion_pct) || 0) < 80) {
      alert(`Briefing ${b.completion_pct || 0}% preenchido — mínimo 80% para submeter.`);
      return;
    }
    try {
      await window.MktBriefingsAPI.submit(b.id);
      load();
    } catch (e) {
      alert(e.message || 'Erro ao submeter briefing.');
    }
  };

  const kpi = React.useMemo(() => {
    if (!briefings) return { total: 0, draft: 0, submitted: 0, approved: 0, rejected: 0, em_campanha: 0, avgPct: 0 };
    const base = userRole === 'board'
      ? briefings.filter(b => ['submetido','validado_ceo','aprovado','em_campanha'].includes(b.status))
      : briefings;
    const total = base.length;
    const byStatus = s => base.filter(b => b.status === s).length;
    const avgPct = total ? Math.round(base.reduce((a, b) => a + (parseInt(b.completion_pct) || 0), 0) / total) : 0;
    return { total, draft: byStatus('rascunho'), submitted: byStatus('submetido'), validado_ceo: byStatus('validado_ceo'), approved: byStatus('aprovado'), rejected: byStatus('rejeitado'), em_campanha: byStatus('em_campanha'), avgPct };
  }, [briefings, userRole]);

  const BRAND_ORDER = ['mimaki', 'biond', 'decal', 'alldecor', 'sensek', 'netscreen', 'digidelta'];
  const BRAND_NAMES = { mimaki: 'Mimaki', biond: 'BIOND', decal: 'Decal', alldecor: 'AllDecor', sensek: 'Sensek', netscreen: 'NetScreen', digidelta: 'Digidelta' };
  const { setBrand: setActiveBrand } = window.useMktBrand ? window.useMktBrand() : { setBrand: () => {} };
  const [allBrands, setAllBrands] = React.useState([]);
  React.useEffect(() => {
    window.MktBriefingsAPI.getBrands().then(d => {
      const list = (d || []).filter(b => b.slug !== 'todas').map(b => ({
        ...b,
        name: BRAND_NAMES[b.slug] || b.name,
      }));
      list.sort((a, b) => {
        const ia = BRAND_ORDER.indexOf(a.slug), ib = BRAND_ORDER.indexOf(b.slug);
        return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
      });
      setAllBrands(list);
    });
  }, []);
  const brands = allBrands;

  const produtos = React.useMemo(() => {
    if (!briefings) return [];
    const seen = new Set();
    briefings.forEach(b => { const v = b.commercial_name || b.product_name; if (v) seen.add(v); });
    return [...seen].sort().map(name => ({ value: name, label: name }));
  }, [briefings]);

  const criadores = React.useMemo(() => {
    if (!briefings) return [];
    const seen = new Set();
    briefings.forEach(b => { if (b.created_by_name) seen.add(b.created_by_name); });
    return [...seen].sort().map(name => ({ value: name, label: name }));
  }, [briefings]);

  const [sortBy, setSortBy] = React.useState({ key: 'updated_at', dir: 'desc' });
  const onSort = (key) => setSortBy(s => ({ key, dir: s.key === key && s.dir === 'asc' ? 'desc' : 'asc' }));
  const [filterAttention, setFilterAttention] = React.useState(false);
  const [selectedIds, setSelectedIds] = React.useState(new Set());
  const [bulkStatusOpen, setBulkStatusOpen] = React.useState(false);
  const bulkStatusRef = React.useRef(null);

  // Atenção: estes três memos têm de vir ANTES de `filtered` (que os usa)
  const myDecisions = React.useMemo(() => {
    if (userRole !== 'strategist' || !userName || !briefings) return [];
    return briefings.filter(b =>
      b.created_by_name === userName &&
      (b.status === 'aprovado' || b.status === 'rejeitado')
    );
  }, [briefings, userRole, userName]);

  const pendingApprovals = React.useMemo(() => {
    if (userRole !== 'board' && userRole !== 'admin' || !briefings) return [];
    return briefings.filter(b => b.status === 'submetido');
  }, [briefings, userRole]);

  const attentionIds = React.useMemo(() => {
    const items = (userRole === 'board' || userRole === 'admin') ? pendingApprovals : myDecisions;
    return new Set(items.map(b => b.id));
  }, [pendingApprovals, myDecisions, userRole]);

  const filtered = React.useMemo(() => {
    if (!briefings) return [];
    const dateFrom = dateRange.preset !== 'all' ? new Date(dateRange.start.getFullYear(), dateRange.start.getMonth(), dateRange.start.getDate()) : null;
    const dateTo   = dateRange.preset !== 'all' ? new Date(dateRange.end.getFullYear(),   dateRange.end.getMonth(),   dateRange.end.getDate() + 1) : null;
    const list = briefings.filter(b =>
      (filterEstado === 'all' || (filterEstado === 'pendentes' ? ['submetido','validado_ceo'].includes(b.status) : b.status === filterEstado)) &&
      (filterMarca   === 'all' || b.brand_slug === filterMarca) &&
      (filterProduto === 'all' || (b.commercial_name || b.product_name) === filterProduto) &&
      (filterCriador === 'all' || b.created_by_name === filterCriador) &&
      (!dateFrom || !b.created_at || new Date(b.created_at) >= dateFrom) &&
      (!dateTo   || !b.created_at || new Date(b.created_at) <  dateTo) &&
      (userRole !== 'board' || ['submetido','validado_ceo','aprovado','em_campanha'].includes(b.status) || b.created_by_name === userName) &&
      (!filterAttention || attentionIds.has(b.id))
    );
    return list.sort((a, b) => {
      let av, bv;
      switch (sortBy.key) {
        case 'commercial_name': av = (a.commercial_name || a.product_name || '').toLowerCase(); bv = (b.commercial_name || b.product_name || '').toLowerCase(); break;
        case 'brand_name':    av = (a.brand_name || '').toLowerCase();      bv = (b.brand_name || '').toLowerCase();      break;
        case 'status':        av = a.status || '';                          bv = b.status || '';                          break;
        case 'completion':    av = parseInt(a.completion_pct) || 0;        bv = parseInt(b.completion_pct) || 0;        break;
        case 'quality_score': av = a.quality_score != null ? a.quality_score : (parseInt(a.completion_pct)||0)/100; bv = b.quality_score != null ? b.quality_score : (parseInt(b.completion_pct)||0)/100; break;
        case 'updated_at':    av = a.updated_at || '';                      bv = b.updated_at || '';                      break;
        case 'created_by':    av = (a.created_by_name || '').toLowerCase(); bv = (b.created_by_name || '').toLowerCase(); break;
        case 'approved_by':   av = (a.approved_by_name || '').toLowerCase();bv = (b.approved_by_name || '').toLowerCase();break;
        default:              av = a[sortBy.key] || '';                     bv = b[sortBy.key] || '';
      }
      if (av < bv) return sortBy.dir === 'asc' ? -1 : 1;
      if (av > bv) return sortBy.dir === 'asc' ? 1 : -1;
      return 0;
    });
  }, [briefings, filterEstado, filterMarca, filterProduto, filterCriador, dateRange, sortBy, filterAttention, attentionIds]);

  const hasActiveFilter = filterEstado !== 'all' || filterMarca !== 'all' || filterProduto !== 'all' || filterCriador !== 'all' || dateRange.preset !== 'all' || filterAttention;

  const clearFilters = () => {
    setFilterEstado('all'); setFilterMarca('all');
    setFilterProduto('all'); setFilterCriador('all');
    setFilterAttention(false);
    setSelectedIds(new Set());
    setDateRange({ preset: 'all', start: new Date(today.getFullYear(), 0, 1), end: today });
  };

  // ── Bulk selection ──────────────────────────────────────────────────────────
  const allSelected  = filtered.length > 0 && filtered.every(b => selectedIds.has(b.id));
  const someSelected = selectedIds.size > 0;

  const toggleSelect    = (id) => setSelectedIds(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; });
  const toggleSelectAll = () => allSelected ? setSelectedIds(new Set()) : setSelectedIds(new Set(filtered.map(b => b.id)));
  const clearSelection  = () => { setSelectedIds(new Set()); setBulkStatusOpen(false); };

  const handleBulkDelete = async () => {
    if (!window.confirm(`Apagar ${selectedIds.size} briefing${selectedIds.size !== 1 ? 's' : ''}? Esta acção não pode ser revertida.`)) return;
    await Promise.all([...selectedIds].map(id => window.MktBriefingsAPI.remove(id)));
    clearSelection(); load();
  };

  const handleBulkStatus = async (status) => {
    setBulkStatusOpen(false);
    try {
      await Promise.all([...selectedIds].map(id =>
        status === 'submetido' ? window.MktBriefingsAPI.submit(id) : window.MktBriefingsAPI.update(id, { status })
      ));
    } catch (e) { alert(e.message || 'Erro ao actualizar estado.'); }
    clearSelection(); load();
  };

  const handleBulkExport = () => {
    const rows = (briefings || []).filter(b => selectedIds.has(b.id)).map(b => ({
      id: b.id, produto: b.commercial_name || b.product_name, marca: b.brand_name,
      estado: b.status, criado_por: b.created_by_name, actualizado: b.updated_at,
    }));
    const blob = new Blob([JSON.stringify(rows, null, 2)], { type: 'application/json' });
    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = `briefings-${new Date().toISOString().slice(0, 10)}.json`;
    a.click();
  };
  // ────────────────────────────────────────────────────────────────────────────

  const kpiPendentes = kpi.submitted + kpi.validado_ceo;
  const kpiCards = [
    { label: 'Pendentes',   value: kpiPendentes,    accent: 'var(--warning, #f59e0b)', fill: kpi.total ? kpiPendentes    / kpi.total : 0, filter: 'pendentes'   },
    { label: 'Aprovados',   value: kpi.approved,    accent: 'var(--success, #22c55e)', fill: kpi.total ? kpi.approved    / kpi.total : 0, filter: 'aprovado'    },
    { label: 'Em Campanha', value: kpi.em_campanha, accent: 'var(--ai-500, #3859D0)',  fill: kpi.total ? kpi.em_campanha / kpi.total : 0, filter: 'em_campanha' },
    { label: 'Total',       value: kpi.total,       accent: 'var(--ai-500, #3859D0)',  fill: 1,                                           filter: 'all'         },
  ];

  const attnCount  = attentionIds.size;
  const attnColor  = (userRole === 'board' || userRole === 'admin') ? 'var(--warning)' : 'var(--ai-500)';
  const attnLabel  = (userRole === 'board' || userRole === 'admin')
    ? `${attnCount} briefing${attnCount !== 1 ? 's' : ''} pendente${attnCount !== 1 ? 's' : ''} de aprovação`
    : `${attnCount} decisão${attnCount !== 1 ? 'ões' : ''} recente${attnCount !== 1 ? 's' : ''} sobre os teus briefings`;

  return (
    <div className="scrollbar" data-theme="light" style={{ flex: 1, minHeight: 0, overflowY: 'auto', overflowX: 'clip', '--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' }}>

      {/* ── Page header ── */}
      <div style={{ padding: '28px 32px 0', display: 'flex', flexDirection: 'column', gap: 18 }}>
        <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 16, flexWrap: 'wrap' }}>
          <div>
            <div style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', letterSpacing: '0.08em' }}>
              MARKETING · AI STRATEGIST · BRIEFINGS
            </div>
            <h1 className="font-display" style={{ margin: '6px 0 4px', fontSize: 26, fontWeight: 600, letterSpacing: '-0.015em' }}>
              AI Strategist · Briefings
            </h1>
            <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>
              Tabela briefings · {kpi.total} briefing{kpi.total !== 1 ? 's' : ''}{isAll ? ' · todas as marcas' : (briefings && briefings[0] ? ` · ${briefings[0].brand_name}` : '')}
            </div>
          </div>
          <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
            <button className="btn" style={{ height: 30, width: 96, padding: '0', fontSize: 12, whiteSpace: 'nowrap', justifyContent: 'center' }} onClick={onImport} title="Importar PDF ou DOCX para preencher o briefing automaticamente com IA">Importar</button>
            <button className="btn btn-ai" style={{ height: 30, width: 96, padding: '0', fontSize: 12, whiteSpace: 'nowrap', justifyContent: 'center' }} onClick={onNew}>Criar</button>
          </div>
        </div>

        {/* ── Digi AI Banner ── */}
        <DigiAIBanner kpi={kpi} userRole={userRole} userName={userName} onFilter={setFilterEstado} />

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

        {/* ── Filter row / Bulk bar ── */}
        {someSelected ? (
          <div style={{
            display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8,
            padding: '8px 12px', borderRadius: 8,
            background: 'color-mix(in srgb, var(--ai-500, #3859D0) 6%, var(--bg-elev, #ffffff))',
            border: '1px solid color-mix(in srgb, var(--ai-500, #3859D0) 20%, var(--border, #dde3ef))',
            animation: 'slide-up 0.2s ease-out both',
          }}>
            <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
              <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--ai-500, #3859D0)', fontFamily: 'var(--font-mono)', letterSpacing: '0.04em' }}>
                {selectedIds.size} seleccionado{selectedIds.size !== 1 ? 's' : ''}
              </span>
              <div style={{ width: 1, height: 14, background: 'var(--border, #dde3ef)' }} />
              <button onClick={handleBulkDelete}
                style={{ background: 'none', border: '1px solid var(--border, #dde3ef)', borderRadius: 5, padding: '4px 10px', fontSize: 12, cursor: 'pointer', color: 'var(--danger, #dc2626)', fontFamily: 'inherit' }}
                onMouseEnter={e => e.currentTarget.style.background = 'color-mix(in srgb, var(--danger, #dc2626) 8%, transparent)'}
                onMouseLeave={e => e.currentTarget.style.background = 'none'}>
                Apagar
              </button>
              <div style={{ position: 'relative' }} ref={bulkStatusRef}>
                <button onClick={() => setBulkStatusOpen(o => !o)}
                  style={{ background: 'none', border: '1px solid var(--border, #dde3ef)', borderRadius: 5, padding: '4px 10px', fontSize: 12, cursor: 'pointer', color: 'var(--text, #112954)', fontFamily: 'inherit', display: 'flex', alignItems: 'center', gap: 4 }}
                  onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover, #f1f5f9)'}
                  onMouseLeave={e => e.currentTarget.style.background = 'none'}>
                  Mudar estado <span style={{ fontSize: 9 }}>▾</span>
                </button>
                {bulkStatusOpen && (
                  <div className="animate-in" style={{ position: 'absolute', top: 'calc(100% + 4px)', left: 0, zIndex: 60, background: 'var(--bg-elev, #ffffff)', border: '1px solid var(--border, #dde3ef)', borderRadius: 6, minWidth: 160, boxShadow: '0 8px 24px rgba(17,41,84,0.12)', padding: 4 }}>
                    {[
                      { value: 'submetido',   label: 'Submeter' },
                      { value: 'aprovado',    label: 'Aprovado' },
                      { value: 'rejeitado',   label: 'Rejeitado' },
                      { value: 'em_campanha', label: 'Em Campanha' },
                    ].map(opt => (
                      <button key={opt.value} onClick={() => handleBulkStatus(opt.value)}
                        style={{ display: 'block', width: '100%', padding: '9px 12px', background: 'none', border: 'none', cursor: 'pointer', fontSize: 13, color: 'var(--text, #112954)', textAlign: 'left', fontFamily: 'inherit' }}
                        onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover, #f1f5f9)'}
                        onMouseLeave={e => e.currentTarget.style.background = 'none'}>
                        {opt.label}
                      </button>
                    ))}
                  </div>
                )}
              </div>
              <button onClick={handleBulkExport}
                style={{ background: 'none', border: '1px solid var(--border, #dde3ef)', borderRadius: 5, padding: '4px 10px', fontSize: 12, cursor: 'pointer', color: 'var(--text, #112954)', fontFamily: 'inherit' }}
                onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover, #f1f5f9)'}
                onMouseLeave={e => e.currentTarget.style.background = 'none'}>
                Exportar JSON
              </button>
            </div>
            <button onClick={clearSelection}
              style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 11, color: 'var(--text-muted, #4a5e7a)', fontFamily: 'var(--font-mono)', padding: '4px 6px' }}>
              ✕ cancelar
            </button>
          </div>
        ) : (
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8 }}>
            <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', alignItems: 'center' }}>
              {isAll && (
                <BriefingDropdown label="Marca" value={filterMarca} onChange={setFilterMarca}
                  options={[{ value: 'all', label: 'Todas' }, ...brands.map(b => ({ value: b.slug, label: b.name }))]}
                />
              )}
              <BriefingDropdown label="Produto" value={filterProduto} onChange={setFilterProduto}
                options={[{ value: 'all', label: 'Todos' }, ...produtos]}
              />
              <BriefingDropdown label="Criado por" value={filterCriador} onChange={setFilterCriador}
                options={[{ value: 'all', label: 'Todos' }, ...criadores]}
              />
              <BriefingDropdown label="Estado" value={filterEstado} onChange={setFilterEstado}
                options={(userRole === 'board' || userRole === 'ceo') ? [
                  { value: 'all',          label: 'Todos' },
                  { value: 'submetido',    label: 'Para CEO' },
                  { value: 'validado_ceo', label: 'Para Chairman' },
                  { value: 'aprovado',     label: 'Aprovado' },
                  { value: 'em_campanha',  label: 'Em Campanha' },
                ] : userRole === 'admin' ? [
                  { value: 'all',          label: 'Todos' },
                  { value: 'rascunho',     label: 'Rascunho' },
                  { value: 'submetido',    label: 'Para CEO' },
                  { value: 'validado_ceo', label: 'Para Chairman' },
                  { value: 'aprovado',     label: 'Aprovado' },
                  { value: 'em_campanha',  label: 'Em Campanha' },
                  { value: 'rejeitado',    label: 'Rejeitado' },
                ] : [
                  { value: 'all',          label: 'Todos' },
                  { value: 'rascunho',     label: 'Rascunho' },
                  { value: 'submetido',    label: 'Em Revisão' },
                  { value: 'validado_ceo', label: 'Validado CEO' },
                  { value: 'aprovado',     label: 'Aprovado' },
                  { value: 'em_campanha',  label: 'Em Campanha' },
                  { value: 'rejeitado',    label: 'Rejeitado' },
                ]}
              />
              {hasActiveFilter && (
                <button onClick={clearFilters} 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 }}>
                {filtered.length} resultado{filtered.length !== 1 ? 's' : ''}
              </span>
            </div>
            <BriefingDateRangePicker value={dateRange} onChange={setDateRange} />
          </div>
        )}

        {/* ── KPI strip ── */}
        <div style={{ display: 'flex', gap: 12 }}>
          {kpiCards.map((k, i) => {
            const isActive = k.filter !== 'all' && filterEstado === k.filter;
            return (
              <div key={i} onClick={() => setFilterEstado(isActive ? 'all' : k.filter)}
                style={{
                  flex: '1 1 0', 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: 'pointer',
                  transition: 'border-color 150ms ease, background 150ms ease',
                  animation: 'slide-up 0.25s ease-out both',
                  animationDelay: `${i * 60}ms`,
                }}>
                <div style={{ fontSize: 9.5, fontWeight: 600, letterSpacing: '0.10em', textTransform: 'uppercase', color: isActive ? k.accent : 'var(--text-muted, #4a5e7a)', fontFamily: 'var(--font-mono, monospace)', whiteSpace: 'nowrap', transition: 'color 150ms ease' }}>{k.label}</div>
                <div style={{ fontSize: 22, fontWeight: 700, fontFamily: 'var(--font-display, Montserrat, sans-serif)', color: k.accent, lineHeight: 1, paddingBottom: 10 }}>{k.value}</div>
                <div style={{ height: 3, background: 'var(--bg-sunken, #eef1f6)', overflow: 'hidden' }}>
                  <div style={{ height: '100%', width: `${(k.fill || 0) * 100}%`, background: k.accent, transition: 'width 400ms ease' }} />
                </div>
              </div>
            );
          })}
        </div>

      </div>


      {/* ── Table ── */}
      {briefings === null ? (
        <div style={{ padding: '18px 32px' }}>
          {[1,2,3,4].map(i => (
            <div key={i} style={{ height: 44, borderRadius: 6, background: 'var(--bg-sunken, #eef1f6)', marginBottom: 6, opacity: 1 - i * 0.2 }} />
          ))}
        </div>
      ) : briefings.length === 0 ? (
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12, color: 'var(--text-muted)', marginTop: 18, padding: '0 32px' }}>
          <div style={{ fontSize: 14 }}>{isAll ? 'Ainda não há briefings.' : 'Ainda não há briefings para esta marca.'}</div>
          <div style={{ display: 'flex', gap: 8 }}>
            <button className="btn" style={{ height: 30, width: 96, padding: '0', fontSize: 12, whiteSpace: 'nowrap', justifyContent: 'center' }} onClick={onImport}>Importar</button>
            <button className="btn btn-ai" style={{ height: 30, width: 96, padding: '0', fontSize: 12, whiteSpace: 'nowrap', justifyContent: 'center' }} onClick={onNew}>Criar</button>
          </div>
        </div>
      ) : filtered.length === 0 ? (
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 8, color: 'var(--text-muted)', marginTop: 18, padding: '0 32px' }}>
          <div style={{ fontSize: 14 }}>Sem resultados para os filtros actuais.</div>
          <button className="btn" style={{ height: 30, width: 96, padding: '0', fontSize: 12, whiteSpace: 'nowrap', justifyContent: 'center' }} onClick={clearFilters}>Limpar filtros</button>
        </div>
      ) : (
        <div style={{ marginTop: 18, padding: '0 32px 32px' }}>
          <div style={{ overflowX: 'auto', width: '100%' }}>
          <table style={{ width: isAll ? 1920 : 1740, tableLayout: 'fixed', borderCollapse: 'collapse', fontSize: 12.5, fontFamily: 'var(--font-sans)' }}>
            <colgroup>
              <col style={{ width: 36 }} />{/* checkbox */}
              <col style={{ width: 400 }} />{/* Nome Comercial */}
              {isAll && <col style={{ width: 180 }} />}{/* Marca */}
              <col style={{ width: 180 }} />{/* Segmento */}
              <col style={{ width: 180 }} />{/* Estado */}
              <col style={{ width: 180 }} />{/* Qualidade */}
              <col style={{ width: 180 }} />{/* Canais */}
              <col style={{ width: 180 }} />{/* Actualizado */}
              <col style={{ width: 180 }} />{/* Criado por */}
              <col style={{ width: 180 }} />{/* Aprovado por */}
              <col style={{ width: 44 }} />{/* kebab */}
            </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', padding: '4px 4px' }}>
                  <input type="checkbox" checked={allSelected}
                    ref={el => { if (el) el.indeterminate = someSelected && !allSelected; }}
                    onChange={toggleSelectAll}
                    style={{ width: 14, height: 14, cursor: 'pointer', accentColor: 'var(--ai-500, #3859D0)' }} />
                </th>
                <SortHeader label="Nome Comercial" sortKey="commercial_name" sortBy={sortBy} onSort={onSort} />
                {isAll && <SortHeader label="Marca"       sortKey="brand_name"  sortBy={sortBy} onSort={onSort} />}
                <SortHeader label="Segmento"     sortKey={null}         sortBy={sortBy} onSort={onSort} />
                <SortHeader label="Estado"       sortKey="status"       sortBy={sortBy} onSort={onSort} />
                <SortHeader label="Qualidade"    sortKey="quality_score" sortBy={sortBy} onSort={onSort} />
                <SortHeader label="Canais"       sortKey={null}         sortBy={sortBy} onSort={onSort} />
                <SortHeader label="Actualizado"  sortKey="updated_at"   sortBy={sortBy} onSort={onSort} />
                <SortHeader label="Criado por"   sortKey="created_by"   sortBy={sortBy} onSort={onSort} />
                <SortHeader label="Aprovado por" sortKey="approved_by"  sortBy={sortBy} onSort={onSort} />
                <th style={thSt}></th>
              </tr>
            </thead>
            <tbody>
              {filtered.map((b, i) => {
                let rowAttention = null;
                if (attentionIds.has(b.id)) {
                  if (userRole === 'board') rowAttention = 'var(--warning)';
                  else rowAttention = b.status === 'aprovado' ? 'var(--success)' : 'var(--danger)';
                }
                return (
                  <BriefingRow key={b.id} b={b} isAll={isAll}
                    onSelect={() => onSelect(b)}
                    onEdit={() => onEdit(b)}
                    onDelete={() => handleDelete(b.id)}
                    onSubmit={handleSubmitRow}
                    onGenerateStrategy={onGenerateStrategy}
                    onCriarCampanha={onCriarCampanha}
                    attentionColor={rowAttention}
                    selected={selectedIds.has(b.id)}
                    onToggle={() => toggleSelect(b.id)}
                    rowIndex={i}
                  />
                );
              })}
            </tbody>
          </table>
          </div>
        </div>
      )}
    </div>
  );
};

// ─── Formulário multi-bloco ────────────────────────────────────────────────────

const BriefingForm = ({ briefing, brandId, onSave, onSubmit, onCancel, onApproved, onRejected, onGenerateStrategy, onCriarCampanha, userRole, userName, userEmail }) => {
  const isMobile = window.useMktMobile();
  const isAll = !brandId || brandId === 'todas';
  const [activeBlock, setActiveBlock] = React.useState(1);
  const [allBrands, setAllBrands] = React.useState([]);
  const [selectedBrand, setSelectedBrand] = React.useState(
    briefing?.brand_slug || (isAll ? '' : brandId)
  );
  const [products, setProducts] = React.useState([]);
  const [selectedSegment, setSelectedSegment] = React.useState(briefing?.product_segment || briefing?.block1?.product_segment || '');
  const [selectedFamily, setSelectedFamily] = React.useState(briefing?.product_family || briefing?.block1?.product_family || '');
  const [saving, setSaving] = React.useState(false);
  const [saveError, setSaveError] = React.useState(null);
  const [approvalNotes, setApprovalNotes] = React.useState('');
  const [approvalNoteError, setApprovalNoteError] = React.useState('');
  const [acting, setActing] = React.useState(false);
  const [approvals, setApprovals] = React.useState(briefing?.approvals || []);
  const [apreciation, setApreciation] = React.useState(briefing?.ai_apreciation || null);
  const [importBannerDismissed, setImportBannerDismissed] = React.useState(false);
  const [suggestingBudget, setSuggestingBudget] = React.useState(false);
  const [budgetSuggested, setBudgetSuggested] = React.useState(false);
  const [budgetRationale, setBudgetRationale] = React.useState('');
  const [budgetError, setBudgetError] = React.useState('');
  const toInputDate = (v) => (v && typeof v === 'string' && v.length > 10 ? v.slice(0, 10) : v || '');

  const buildForm = (src) => {
    const b4 = src.block4 || {};
    return {
      product_id: src.product_id || '',
      block1: { ...EMPTY_FORM.block1, ...(src.block1 || {}) },
      block2: { ...EMPTY_FORM.block2, ...(src.block2 || {}) },
      block3: { ...EMPTY_FORM.block3, ...(src.block3 || {}) },
      block4: { ...EMPTY_FORM.block4, ...b4, timeline_start: toInputDate(b4.timeline_start), timeline_end: toInputDate(b4.timeline_end) },
      block5: { ...EMPTY_FORM.block5, ...(src.block5 || {}) },
    };
  };

  const [form, setForm] = React.useState(
    briefing?.block1 ? buildForm(briefing) : { ...EMPTY_FORM }
  );
  const [formLoading, setFormLoading] = React.useState(!!(briefing?.id && !briefing?.block1));

  // Editar da lista — buscar blocos via API
  React.useEffect(() => {
    if (!briefing?.id || briefing?.block1) return;
    window.MktBriefingsAPI.get(briefing.id).then(full => {
      if (!full) return;
      setForm(buildForm(full));
      setApreciation(full.ai_apreciation || null);
      setApprovals(full.approvals || []);
      if (full.product_segment) setSelectedSegment(full.product_segment);
      if (full.product_family)  setSelectedFamily(full.product_family);
      setFormLoading(false);
    });
  }, []);

  // Carregar marcas quando modo "todas"
  const BRAND_DISPLAY = { mimaki: 'Mimaki', biond: 'BIOND', decal: 'Decal', sensek: 'Sensek', alldecor: 'AllDecor', digidelta: 'Digidelta', netscreen: 'NetScreen' };
  React.useEffect(() => {
    if (!isAll) return;
    window.MktBriefingsAPI.getBrands().then(d => {
      setAllBrands((d || []).filter(b => b.slug !== 'todas').map(b => ({ ...b, name: BRAND_DISPLAY[b.slug] || b.name })));
    });
  }, []);

  // Carregar produtos quando muda a marca
  React.useEffect(() => {
    const bid = selectedBrand || (!isAll ? brandId : '');
    if (!bid || bid === 'todas') { setProducts([]); return; }
    window.MktBriefingsAPI.getProducts(bid).then(d => {
      const prods = d || [];
      setProducts(prods);
      // Pré-selecionar produto se vier do briefing (editado ou importado)
      if (briefing?.product_id) {
        const existing = prods.find(p => String(p.id) === String(briefing.product_id));
        if (existing?.segment) setSelectedSegment(existing.segment);
        if (existing?.category) setSelectedFamily(existing.category);
      }
    });
  }, [selectedBrand, brandId]);

  // Auto-trigger suggest quando Block 4 é aberto e KPIs ou timeline estão vazios
  React.useEffect(() => {
    if (activeBlock !== 4) return;
    const b4 = form.block4 || {};
    const missingKpis     = !(b4.kpis?.length);
    const missingTimeline = !b4.timeline_start || !b4.timeline_end;
    const b1Ok = !!(form.block1?.commercial_name?.trim());
    if ((missingKpis || missingTimeline) && b1Ok && !suggestingBudget && !budgetSuggested) {
      handleSuggestB4FromBudget();
    }
  }, [activeBlock]);

  const handleBrandChange = (slug) => {
    setSelectedBrand(slug);
    setSelectedSegment('');
    setSelectedFamily('');
    setForm(f => ({ ...f, product_id: '' }));
  };

  const handleSegmentChange = (seg) => {
    setSelectedSegment(seg);
    setSelectedFamily('');
    setForm(f => ({ ...f, product_id: '' }));
  };

  const handleFamilyChange = (family) => {
    setSelectedFamily(family);
    setForm(f => ({ ...f, product_id: '' }));
  };

  const handleProductChange = (pid) => {
    const prod = products.find(p => String(p.id) === String(pid));
    setForm(f => ({
      ...f,
      product_id: pid,
      block1: { ...f.block1, product_family: prod?.category || f.block1.product_family },
    }));
  };

  const upd = (block, field, value) =>
    setForm(f => ({ ...f, [block]: { ...f[block], [field]: value } }));

  const handleSuggestB4FromBudget = async () => {
    const bid = isAll ? selectedBrand : brandId;
    if (!bid) {
      setBudgetError('Selecciona uma marca no Bloco 1 antes de sugerir a campanha.');
      return;
    }
    setSuggestingBudget(true);
    setBudgetError('');
    try {
      const res = await fetch('/api/marketing/briefings/suggest-b4-from-budget', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          brand_id: bid, product_id: form.product_id || null,
          brand_name: bid, block1: form.block1, block2: form.block2, block3: form.block3,
        }),
      });
      if (!res.ok) {
        const j = await res.json().catch(() => ({}));
        throw new Error(j.error || `HTTP ${res.status}`);
      }
      const s = await res.json();
      setForm(f => ({ ...f, block4: {
        ...f.block4,
        objective:      s.objective      || f.block4.objective,
        kpis:           s.kpis?.length   ? s.kpis : f.block4.kpis,
        channels:       s.channels?.length ? s.channels : f.block4.channels,
        tone:           s.tone           || f.block4.tone,
        key_message:    s.key_message    || f.block4.key_message,
        timeline_start: s.timeline_start || f.block4.timeline_start,
        timeline_end:   s.timeline_end   || f.block4.timeline_end,
      }}));
      setBudgetSuggested(true);
      setBudgetRationale(s.budget_rationale || '');
    } catch (e) {
      setBudgetError('Erro ao sugerir campanha: ' + e.message);
      console.error('[suggest-b4-budget]', e.message);
    } finally {
      setSuggestingBudget(false);
    }
  };

  const handleSave = async () => {
    setSaveError(null);
    if (isAll && !selectedBrand) { setSaveError('Selecciona uma marca antes de guardar.'); return; }
    setSaving(true);
    try {
      await onSave(form, isAll ? selectedBrand : undefined, apreciation || undefined);
    } catch (e) {
      setSaveError(e.message || 'Erro ao guardar. Tenta novamente.');
    } finally {
      setSaving(false);
    }
  };

  const blockDone = React.useMemo(() => {
    const hasT  = v => typeof v === 'string' && v.trim().length > 0;
    const hasIt = v => Array.isArray(v) && v.some(i => typeof i === 'string' ? i.trim() : i);
    const b1 = form.block1 || {}, b2 = form.block2 || {}, b3 = form.block3 || {}, b4 = form.block4 || {}, b5 = form.block5 || {};
    const score = arr => arr.filter(Boolean).length / arr.length;
    return [
      score([!!form.product_id, hasT(b1.commercial_name), hasT(b1.elevator_pitch), hasIt(b1.usps), hasIt(b1.geo_markets)]),
      score([hasT(b2.decision_maker), hasT(b2.end_user), hasIt(b2.pain_points), hasT(b2.purchase_trigger)]),
      score([hasIt(b3.competitors), hasT(b3.positioning_narrative), hasIt(b3.differentiation_args)]),
      score([hasT(b4.objective), hasT(b4.key_message), hasIt(b4.channels), hasT(b4.tone)]),
      score([(hasIt(b5.prohibited_claims) || hasIt(b5.off_brand_messages) || hasIt(b5.other_restrictions)) ? true : false]),
      apreciation ? 1 : 0,
    ];
  }, [form, apreciation]);

  const tabStyle = (n) => ({
    padding: isMobile ? '8px 10px' : '8px 18px',
    fontSize: isMobile ? 11 : 13,
    fontWeight: activeBlock === n ? 700 : 400,
    color: activeBlock === n ? 'var(--ai-500)' : 'var(--text-muted)',
    borderBottom: activeBlock === n ? '2px solid var(--ai-500)' : '2px solid transparent',
    background: 'none', border: 'none', borderRadius: 0,
    cursor: 'pointer', whiteSpace: 'nowrap',
    display: 'inline-flex', alignItems: 'center', gap: 6,
  });

  const isNew          = !briefing?.id;
  const isDraft        = !briefing?.status || briefing.status === 'rascunho';
  const isRejected     = briefing?.status === 'rejeitado';
  const isSubmitted    = briefing?.status === 'submetido';
  const isValidadoCeo  = briefing?.status === 'validado_ceo';
  const isInApproval   = isSubmitted || isValidadoCeo;
  const canSubmit      = (isDraft || isRejected) && calcProgress(form) >= 80 && (apreciation?.quality_score ?? 0) >= 80;
  const brandSlug      = briefing?.brand_slug;
  const pendingRoles   = isInApproval ? getMyPendingRoles(userEmail, brandSlug, approvals, briefing.status) : [];
  const canApprove     = pendingRoles.length > 0;
  // Aprovador activo pode editar o briefing
  const isReadOnly     = !canApprove && (isInApproval || briefing?.status === 'em_campanha');

  const handleApproval = async (action) => {
    const needsNotes = action === 'rejected' || action === 'changes_requested';
    if (needsNotes && !approvalNotes.trim()) {
      setApprovalNoteError(
        action === 'rejected'
          ? 'Indica o motivo da rejeição antes de rejeitar.'
          : 'Indica as alterações necessárias antes de enviar.'
      );
      return;
    }
    setApprovalNoteError('');
    setActing(true);
    try {
      const approver_name = userName || window.currentUser?.nome_apresentar || window.currentUser?.nome || null;
      for (const role of pendingRoles) {
        await window.MktBriefingsAPI.approve(briefing.id, {
          action,
          notes: approvalNotes,
          approver_name,
          approver_email: userEmail,
          role,
        });
      }
      if (action === 'approved') onApproved?.();
      else if (action === 'rejected') onRejected?.();
      else onRejected?.(); // changes_requested também volta para o autor
    } catch (e) {
      setSaveError(e.message || 'Erro ao processar. Tenta novamente.');
    } finally {
      setActing(false);
    }
  };

  if (formLoading) return (
    <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--text-muted)', fontSize: 13 }}>
      A carregar briefing...
    </div>
  );

  return (
    <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0 }}>
      {/* Header — mesmo padrão que BriefingsList */}
      <div style={{ padding: '28px 32px 18px', flexShrink: 0 }}>
        <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 16, flexWrap: 'wrap' }}>
          <div>
            <div style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', letterSpacing: '0.08em' }}>
              MARKETING · AI STRATEGIST · BRIEFINGS · {isNew ? 'NOVO' : 'EDITAR'}
            </div>
            <h1 className="font-display" style={{ margin: '6px 0 4px', fontSize: 26, fontWeight: 600, letterSpacing: '-0.015em' }}>
              AI Strategist · {isNew ? 'Novo Briefing' : (briefing?.commercial_name || briefing?.product_name || 'Briefing')}
            </h1>
            <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>
              {(STATUS_CONFIG[briefing?.status] || STATUS_CONFIG.rascunho).label}
              {!isNew && ` · ${calcProgress(form)}% preenchido`}
              {briefing?.brand_name && ` · ${briefing.brand_name}`}
            </div>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 6 }}>
            <div style={{ display: 'flex', gap: 6, alignItems: 'center', flexWrap: 'wrap' }}>
              <button className="btn btn-ghost" style={{ height: 30, minWidth: 80, padding: '0 12px', fontSize: 12, whiteSpace: 'nowrap' }} onClick={onCancel}>← Voltar</button>
              {!isReadOnly && (
                <button className="btn" style={{ height: 30, width: 96, padding: '0', fontSize: 12, whiteSpace: 'nowrap', justifyContent: 'center' }} onClick={handleSave} disabled={saving}>
                  {saving ? 'A guardar...' : 'Guardar'}
                </button>
              )}
              {(isDraft || isRejected) && !canSubmit && calcProgress(form) >= 80 && (
                apreciation
                  ? <span title={`Score de apreciação: ${apreciation.quality_score}/100 — necessário ≥ 80`} style={{ fontSize: 11, color: 'var(--warning)', display: 'flex', alignItems: 'center', gap: 4 }}>
                      ⚠ Score AI {apreciation.quality_score}/100 · mín. 80
                    </span>
                  : <span title="Gera a apreciação no bloco B6 antes de submeter" style={{ fontSize: 11, color: 'var(--warning)', display: 'flex', alignItems: 'center', gap: 4 }}>
                      ⚠ Apreciação AI obrigatória (B6)
                    </span>
              )}
              {canSubmit && (
                <button className="btn btn-ai" style={{ height: 30, padding: '0 14px', fontSize: 12, whiteSpace: 'nowrap' }} onClick={() => {
                  setSaveError(null);
                  if (isAll && !selectedBrand) { setSaveError('Selecciona uma marca antes de submeter.'); return; }
                  onSubmit(form, isAll ? selectedBrand : undefined, apreciation || undefined);
                }}>
                  {isRejected ? 'Submeter novamente' : 'Submeter →'}
                </button>
              )}
              {briefing?.status === 'aprovado' && (
                <>
                  <button className="btn" style={{ height: 30, padding: '0 14px', fontSize: 12 }}
                    onClick={() => onCriarCampanha?.(briefing)}>+ Criar Campanha</button>
                  <button className="btn btn-ai" style={{ height: 30, padding: '0 14px', fontSize: 12 }}
                    onClick={() => onGenerateStrategy?.(briefing)}>Gerar Conteúdo →</button>
                </>
              )}
              {canApprove && (
                <>
                  <button className="btn" style={{ height: 30, padding: '0 14px', fontSize: 12, color: 'var(--danger)', borderColor: 'var(--danger)' }}
                    onClick={() => handleApproval('rejected')} disabled={acting}>Rejeitar</button>
                  <button className="btn" style={{ height: 30, padding: '0 14px', fontSize: 12, color: 'var(--warning)', borderColor: 'var(--warning)' }}
                    onClick={() => handleApproval('changes_requested')} disabled={acting}>Pedir Alterações</button>
                  <button className="btn btn-ai" style={{ height: 30, padding: '0 14px', fontSize: 12 }}
                    onClick={() => handleApproval('approved')} disabled={acting}>{acting ? 'A processar...' : 'Aprovar'}</button>
                </>
              )}
            </div>
            {saveError && (
              <span style={{ fontSize: 11, color: 'var(--danger)', textAlign: 'right', maxWidth: 280 }}>{saveError}</span>
            )}
          </div>
        </div>
      </div>

      {/* Tabs dos blocos */}
      <div style={{
        display: 'flex', borderBottom: '1px solid var(--border)',
        overflowX: 'auto', flexShrink: 0, padding: '0 32px',
      }}>
        {BLOCK_LABELS.map((label, i) => (
          <button key={i} style={tabStyle(i + 1)} onClick={() => setActiveBlock(i + 1)}>
            {isMobile ? `B${i + 1}` : `${i + 1}. ${label}`}
            {i === 5
              ? <span style={{ fontSize: 10, lineHeight: 1, color: blockDone[5] >= 1 ? 'var(--ai-500)' : 'var(--text-dim)' }}>✦</span>
              : <span style={{ width: 6, height: 6, borderRadius: '50%', flexShrink: 0, background: blockDone[i] >= 1 ? 'var(--success)' : blockDone[i] > 0 ? 'var(--warning)' : 'var(--border)' }} />
            }
          </button>
        ))}
      </div>

      {/* Conteúdo do bloco */}
      <div className="scrollbar" style={{ flex: 1, minHeight: 0, overflowY: 'auto', padding: isMobile ? 12 : 28 }}>
        <div style={{ maxWidth: 960, margin: '0 auto' }}>

          {/* Painel de aprovações */}
          {isInApproval && (
            <ApprovalPanel
              briefing={briefing}
              approvals={approvals}
              userEmail={userEmail}
              noteValue={approvalNotes}
              onNoteChange={v => { setApprovalNotes(v); if (v.trim()) setApprovalNoteError(''); }}
              noteError={approvalNoteError}
            />
          )}

          {briefing?._import_origin && !importBannerDismissed && (
            <div style={{ marginBottom: 20, padding: '10px 14px', borderRadius: 8, fontSize: 12, display: 'flex', alignItems: 'center', gap: 8, background: 'color-mix(in oklch, var(--ai-500) 8%, transparent)', border: '1px solid color-mix(in oklch, var(--ai-500) 25%, transparent)', color: 'var(--ai-500)' }}>
              <span style={{ flexShrink: 0 }}>✦</span>
              <span style={{ flex: 1 }}>Conteúdo extraído por IA — revê e confirma os dados antes de guardar. O Bloco 6 gera o resumo automaticamente.</span>
              <button onClick={() => setImportBannerDismissed(true)} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--ai-500)', fontSize: 14, padding: '0 4px', flexShrink: 0, lineHeight: 1 }}>✕</button>
            </div>
          )}

          <div style={isReadOnly ? { pointerEvents: 'none', userSelect: 'text' } : {}}>
            {activeBlock === 1 && <Block1
              data={form.block1} upd={(f, v) => upd('block1', f, v)}
              isAll={isAll} allBrands={allBrands}
              selectedBrand={selectedBrand} onBrandChange={handleBrandChange}
              products={products}
              selectedSegment={selectedSegment} onSegmentChange={handleSegmentChange}
              selectedFamily={selectedFamily} onFamilyChange={handleFamilyChange}
              productId={form.product_id} onProductChange={handleProductChange}
            />}
            {activeBlock === 2 && <Block2 data={form.block2} upd={(f, v) => upd('block2', f, v)} brand={selectedBrand} />}
            {activeBlock === 3 && <Block3 data={form.block3} upd={(f, v) => upd('block3', f, v)} brand={selectedBrand} />}
            {activeBlock === 4 && <Block4 data={form.block4} upd={(f, v) => upd('block4', f, v)} brand={selectedBrand}
              aiSuggested={!!briefing?._import_origin && (form.block4.objective || (form.block4.channels || []).length > 0 || form.block4.key_message)}
              onSuggestBudget={handleSuggestB4FromBudget}
              suggestingBudget={suggestingBudget}
              budgetSuggested={budgetSuggested}
              budgetRationale={budgetRationale}
              budgetError={budgetError} />}
            {activeBlock === 5 && <Block5 data={form.block5} upd={(f, v) => upd('block5', f, v)} brand={selectedBrand} aiSuggested={!!briefing?._import_origin && ((form.block5.prohibited_claims || []).some(v => v?.trim()) || (form.block5.off_brand_messages || []).some(v => v?.trim()))} />}
          </div>
          {activeBlock === 6 && (
            <Block6
              briefingId={briefing?.id}
              apreciation={apreciation}
              previewData={!briefing?.id ? {
                block1: form.block1, block2: form.block2, block3: form.block3,
                block4: form.block4, block5: form.block5,
                brand_name: selectedBrand || briefing?.brand_slug || briefing?.brand_name || '',
              } : null}
              userRole={userRole}
              calcProgressValue={calcProgress(form)}
              onUpdate={(data) => {
                setApreciation(data);
                if (briefing?.id) window.MktBriefingsAPI.update(briefing.id, { ai_apreciation: data }).catch(() => {});
              }}
              importOrigin={briefing?._import_origin || briefing?.import_origin}
              autoGenerate={true}
            />
          )}
        </div>
      </div>

      {/* Navegação sticky entre blocos */}
      <div style={{
        flexShrink: 0, display: 'flex', justifyContent: 'space-between', alignItems: 'center',
        padding: '12px 32px', borderTop: '1px solid var(--border)', background: 'var(--bg)',
      }}>
        <button className="btn btn-ghost" style={{ height: 30, minWidth: 96, padding: '0 12px', fontSize: 12, whiteSpace: 'nowrap' }}
          onClick={() => setActiveBlock(b => Math.max(1, b - 1))}
          disabled={activeBlock === 1}>
          ← {activeBlock > 1 ? BLOCK_LABELS[activeBlock - 2] : ''}
        </button>
        <span style={{ fontSize: 12, color: 'var(--text-muted)' }}>
          {activeBlock} / {BLOCK_LABELS.length}
        </span>
        <button className="btn btn-ghost" style={{ height: 30, minWidth: 96, padding: '0 12px', fontSize: 12, whiteSpace: 'nowrap' }}
          onClick={() => setActiveBlock(b => Math.min(BLOCK_LABELS.length, b + 1))}
          disabled={activeBlock === BLOCK_LABELS.length}>
          {activeBlock < BLOCK_LABELS.length ? BLOCK_LABELS[activeBlock] : ''} →
        </button>
      </div>
    </div>
  );
};

// ─── Vista de detalhe + aprovação ─────────────────────────────────────────────

const BriefingDetail = ({ briefing, onEdit, onBack, onApproved, onRejected, onGenerateStrategy, onCriarCampanha, userRole, userName, userEmail }) => {
  const isMobile = window.useMktMobile();
  const [notes, setNotes] = React.useState('');
  const [rejectError, setRejectError] = React.useState('');
  const [acting, setActing] = React.useState(false);
  const [activeBlock, setActiveBlock] = React.useState(1);
  const [full, setFull] = React.useState(briefing.block1 ? briefing : null);

  React.useEffect(() => {
    if (briefing.block1) return;
    window.MktBriefingsAPI.get(briefing.id).then(d => { if (d) setFull(d); });
  }, [briefing.id]);

  const src          = full || briefing;
  const brandSlug    = src.brand_slug;
  const approvals    = src.approvals || [];
  const isInApproval = briefing.status === 'submetido' || briefing.status === 'validado_ceo';
  const pendingRoles = isInApproval ? getMyPendingRoles(userEmail, brandSlug, approvals, briefing.status) : [];
  const canApprove   = pendingRoles.length > 0;

  const handle = async (action) => {
    const needsNotes = action === 'rejected' || action === 'changes_requested';
    if (needsNotes && !notes.trim()) {
      setRejectError(action === 'rejected' ? 'Indica o motivo da rejeição.' : 'Indica as alterações necessárias.');
      return;
    }
    setRejectError('');
    setActing(true);
    try {
      const approver_name = userName || window.currentUser?.nome_apresentar || window.currentUser?.nome || null;
      for (const role of pendingRoles) {
        await window.MktBriefingsAPI.approve(briefing.id, {
          action,
          notes,
          approver_name,
          approver_email: userEmail,
          role,
        });
      }
      action === 'approved' ? onApproved() : onRejected();
    } catch (e) {
      setRejectError(e.message || 'Erro ao processar. Tenta novamente.');
    } finally {
      setActing(false);
    }
  };

  const form = {
    product_id: src.product_id || '',
    block1: { ...EMPTY_FORM.block1, ...(src.block1 || {}) },
    block2: { ...EMPTY_FORM.block2, ...(src.block2 || {}) },
    block3: { ...EMPTY_FORM.block3, ...(src.block3 || {}) },
    block4: { ...EMPTY_FORM.block4, ...(src.block4 || {}) },
    block5: { ...EMPTY_FORM.block5, ...(src.block5 || {}) },
  };

  // Dados sintéticos para o Block1 mostrar marca/produto seleccionados correctamente
  const syntheticBrands   = src.brand_slug ? [{ slug: src.brand_slug, name: src.brand_name || src.brand_slug }] : [];
  const syntheticProducts = src.product_id ? [{ id: src.product_id, name: src.product_name || '', slug: '', segment: '', category: '' }] : [];

  // Completion dots — idêntico ao BriefingForm
  const _hasT  = v => typeof v === 'string' && v.trim().length > 0;
  const _hasIt = v => Array.isArray(v) && v.some(i => typeof i === 'string' ? i.trim() : i);
  const _score = arr => arr.filter(Boolean).length / arr.length;
  const blockDone = [
    _score([!!form.product_id, _hasT(form.block1.commercial_name), _hasT(form.block1.elevator_pitch), _hasIt(form.block1.usps), _hasIt(form.block1.geo_markets)]),
    _score([_hasT(form.block2.decision_maker), _hasT(form.block2.end_user), _hasIt(form.block2.pain_points), _hasT(form.block2.purchase_trigger)]),
    _score([_hasIt(form.block3.competitors), _hasT(form.block3.positioning_narrative), _hasIt(form.block3.differentiation_args)]),
    _score([_hasT(form.block4.objective), _hasT(form.block4.key_message), _hasIt(form.block4.channels), _hasT(form.block4.tone)]),
    _score([((_hasIt(form.block5.prohibited_claims) || _hasIt(form.block5.off_brand_messages) || _hasIt(form.block5.other_restrictions)) ? true : false)]),
    src.ai_apreciation ? 1 : 0,
  ];

  const tabStyle = (n) => ({
    padding: isMobile ? '8px 10px' : '8px 18px',
    fontSize: isMobile ? 11 : 13,
    fontWeight: activeBlock === n ? 700 : 400,
    color: activeBlock === n ? 'var(--ai-500)' : 'var(--text-muted)',
    borderBottom: activeBlock === n ? '2px solid var(--ai-500)' : '2px solid transparent',
    background: 'none', border: 'none', borderRadius: 0,
    cursor: 'pointer', whiteSpace: 'nowrap',
    display: 'inline-flex', alignItems: 'center', gap: 6,
  });

  if (!full && briefing.id) return (
    <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--text-muted)', fontSize: 13 }}>
      A carregar briefing...
    </div>
  );

  return (
    <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0 }}>
      {/* Header — idêntico ao BriefingForm */}
      <div style={{ padding: '28px 32px 18px', flexShrink: 0 }}>
        <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 16, flexWrap: 'wrap' }}>
          <div>
            <div style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', letterSpacing: '0.08em' }}>
              MARKETING · AI STRATEGIST · BRIEFINGS · DETALHE
            </div>
            <h1 className="font-display" style={{ margin: '6px 0 4px', fontSize: 26, fontWeight: 600, letterSpacing: '-0.015em' }}>
              AI Strategist · {src.commercial_name || src.product_name || 'Briefing'}
            </h1>
            <div style={{ fontSize: 12, color: 'var(--text-muted)', display: 'flex', alignItems: 'center', gap: 8 }}>
              {(() => { const m={rascunho:['Rascunho','#475569','#e2e8f0'],submetido:['Submetido','#92400e','#fef3c7'],aprovado:['Aprovado','#14532d','#dcfce7'],rejeitado:['Rejeitado','#991b1b','#fee2e2'],em_campanha:['Em Campanha','#1e3a8a','#dbeafe']}; const [l,c,bg]=m[briefing.status]||m.rascunho; return <span style={{fontSize:11,fontFamily:'monospace',fontWeight:600,padding:'2px 8px',borderRadius:4,display:'inline-block',color:c,background:bg}}>{l.toUpperCase()}</span>; })()}
              {src.brand_name && <span>· {src.brand_name}</span>}
              <span>· v{briefing.version_number || 1}</span>
              <span>· {calcProgress(form)}% preenchido</span>
            </div>
          </div>
          <div style={{ display: 'flex', gap: 6, alignItems: 'center', flexWrap: 'wrap' }}>
            <button className="btn btn-ghost" style={{ height: 30, minWidth: 80, padding: '0 12px', fontSize: 12 }} onClick={onBack}>← Voltar</button>
            {(briefing.status === 'rascunho' || briefing.status === 'rejeitado') && (
              <button className="btn" style={{ height: 30, minWidth: 80, padding: '0 12px', fontSize: 12 }} onClick={() => onEdit(src)}>Editar</button>
            )}
            {briefing.status === 'aprovado' && (
              <>
                <button className="btn" style={{ height: 30, padding: '0 14px', fontSize: 12 }}
                  onClick={() => onCriarCampanha?.(briefing)}>+ Criar Campanha</button>
                <button className="btn btn-ai" style={{ height: 30, padding: '0 14px', fontSize: 12 }}
                  onClick={() => onGenerateStrategy?.(briefing)}>Gerar Conteúdo →</button>
              </>
            )}
            {canApprove && (
              <>
                <button className="btn" style={{ height: 30, padding: '0 14px', fontSize: 12, color: 'var(--danger)', borderColor: 'var(--danger)' }}
                  onClick={() => handle('rejected')} disabled={acting}>Rejeitar</button>
                <button className="btn" style={{ height: 30, padding: '0 14px', fontSize: 12, color: 'var(--warning)', borderColor: 'var(--warning)' }}
                  onClick={() => handle('changes_requested')} disabled={acting}>Pedir Alterações</button>
                <button className="btn btn-ai" style={{ height: 30, padding: '0 14px', fontSize: 12 }}
                  onClick={() => handle('approved')} disabled={acting}>{acting ? 'A processar...' : 'Aprovar'}</button>
              </>
            )}
          </div>
        </div>
      </div>

      {/* Tabs dos blocos — idêntico ao BriefingForm */}
      <div style={{ display: 'flex', borderBottom: '1px solid var(--border)', overflowX: 'auto', flexShrink: 0, padding: '0 32px' }}>
        {BLOCK_LABELS.map((label, i) => (
          <button key={i} style={tabStyle(i + 1)} onClick={() => setActiveBlock(i + 1)}>
            {isMobile ? `B${i + 1}` : `${i + 1}. ${label}`}
            {i === 5
              ? <span style={{ fontSize: 10, lineHeight: 1, color: blockDone[5] >= 1 ? 'var(--ai-500)' : 'var(--text-dim)' }}>✦</span>
              : <span style={{ width: 6, height: 6, borderRadius: '50%', flexShrink: 0, background: blockDone[i] >= 1 ? 'var(--success)' : blockDone[i] > 0 ? 'var(--warning)' : 'var(--border)' }} />
            }
          </button>
        ))}
      </div>

      {/* Conteúdo */}
      <div className="scrollbar" style={{ flex: 1, minHeight: 0, overflowY: 'auto', padding: isMobile ? 12 : 28 }}>
        <div style={{ maxWidth: 960, margin: '0 auto' }}>

          {/* Banner rejeitado */}
          {briefing.status === 'rejeitado' && (
            <div style={{ marginBottom: 24, padding: 16, borderRadius: 8, background: 'color-mix(in oklch, var(--danger) 8%, transparent)', border: '1px solid color-mix(in oklch, var(--danger) 30%, transparent)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>
              <div>
                <div style={{ fontWeight: 600, color: 'var(--danger)', fontSize: 13, marginBottom: 2 }}>Briefing rejeitado</div>
                <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>Edita o briefing e volta a submeter para aprovação.</div>
              </div>
              <button className="btn btn-sm btn-ai" onClick={() => onEdit(src)}>Editar e corrigir</button>
            </div>
          )}

          {/* Painel de aprovações */}
          {(briefing.status === 'submetido' || briefing.status === 'validado_ceo') && (
            <ApprovalPanel
              briefing={src}
              approvals={approvals}
              userEmail={userEmail}
              noteValue={notes}
              onNoteChange={v => { setNotes(v); if (v.trim()) setRejectError(''); }}
              noteError={rejectError}
            />
          )}

          {/* Blocos em leitura — pointer-events desligados, visual idêntico ao form */}
          <div style={{ pointerEvents: 'none', userSelect: 'text' }}>
            {activeBlock === 1 && <Block1
              data={form.block1} upd={() => {}}
              isAll={false} allBrands={syntheticBrands}
              selectedBrand={src.brand_slug} onBrandChange={() => {}}
              products={syntheticProducts}
              selectedSegment={''} onSegmentChange={() => {}}
              selectedFamily={''} onFamilyChange={() => {}}
              productId={form.product_id} onProductChange={() => {}}
            />}
            {activeBlock === 2 && <Block2 data={form.block2} upd={() => {}} brand={src.brand_slug} />}
            {activeBlock === 3 && <Block3 data={form.block3} upd={() => {}} brand={src.brand_slug} />}
            {activeBlock === 4 && <Block4 data={form.block4} upd={() => {}} brand={src.brand_slug} />}
            {activeBlock === 5 && <Block5 data={form.block5} upd={() => {}} brand={src.brand_slug} />}
          </div>
          {activeBlock === 6 && (
            <Block6
              briefingId={src.id}
              apreciation={src.ai_apreciation}
              userRole={userRole}
              calcProgressValue={calcProgress(form)}
              importOrigin={src.import_origin}
            />
          )}
        </div>
      </div>

      {/* Navegação entre blocos — idêntico ao BriefingForm */}
      <div style={{ flexShrink: 0, display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '12px 32px', borderTop: '1px solid var(--border)', background: 'var(--bg)' }}>
        <button className="btn btn-ghost" style={{ height: 30, minWidth: 96, padding: '0 12px', fontSize: 12, whiteSpace: 'nowrap' }}
          onClick={() => setActiveBlock(b => Math.max(1, b - 1))} disabled={activeBlock === 1}>
          ← {activeBlock > 1 ? BLOCK_LABELS[activeBlock - 2] : ''}
        </button>
        <span style={{ fontSize: 12, color: 'var(--text-muted)' }}>{activeBlock} / {BLOCK_LABELS.length}</span>
        <button className="btn btn-ghost" style={{ height: 30, minWidth: 96, padding: '0 12px', fontSize: 12, whiteSpace: 'nowrap' }}
          onClick={() => setActiveBlock(b => Math.min(BLOCK_LABELS.length, b + 1))} disabled={activeBlock === BLOCK_LABELS.length}>
          {activeBlock < BLOCK_LABELS.length ? BLOCK_LABELS[activeBlock] : ''} →
        </button>
      </div>
    </div>
  );
};

const IMPORT_STEPS = [
  { step: 1, label: 'Fontes recebidas' },
  { step: 2, label: 'Extracção de texto' },
  { step: 3, label: 'Envio para Claude AI' },
  { step: 4, label: 'Análise do conteúdo' },
  { step: 5, label: 'Estruturação da informação' },
];

// ─── Completeness helpers ─────────────────────────────────────────────────────

const COMPLETENESS_BLOCKS = [
  { id: 1, label: 'Produto', key: 'block1', fields: [
    { key: 'commercial_name', label: 'Nome comercial' },
    { key: 'elevator_pitch',  label: 'Elevator pitch' },
    { key: 'usps',            label: 'USPs' },
    { key: 'certifications',  label: 'Certificações' },
    { key: 'applications',    label: 'Aplicações' },
    { key: 'geo_markets',     label: 'Mercados geográficos' },
  ]},
  { id: 2, label: 'Cliente', key: 'block2', fields: [
    { key: 'decision_maker',     label: 'Decisor' },
    { key: 'end_user',           label: 'Utilizador final' },
    { key: 'pain_points',        label: 'Pain points' },
    { key: 'motivators',         label: 'Motivadores' },
    { key: 'objections',         label: 'Objecções' },
    { key: 'purchase_trigger',   label: 'Trigger de compra' },
    { key: 'funnel_entry_stage', label: 'Fase de funil' },
  ]},
  { id: 3, label: 'Mercado', key: 'block3', fields: [
    { key: 'competitors',           label: 'Concorrentes' },
    { key: 'differentiation_args',  label: 'Diferenciação' },
    { key: 'market_trends',         label: 'Tendências de mercado' },
    { key: 'positioning_narrative', label: 'Narrativa posicionamento' },
    { key: 'sensitive_info',        label: 'Info sensível' },
  ]},
];

const isEmpty = v => {
  if (v === null || v === undefined) return true;
  if (Array.isArray(v)) return v.length === 0 || v.every(x => !String(x || '').trim());
  if (typeof v === 'string') return !v.trim();
  return false;
};

const calcCompleteness = (data) =>
  COMPLETENESS_BLOCKS.map(b => {
    const block = data?.[b.key] || {};
    const missing = b.fields.filter(f => isEmpty(block[f.key])).map(f => f.label);
    return { ...b, filled: b.fields.length - missing.length, total: b.fields.length, missing };
  });

// ─── Modal de importação com progresso SSE ────────────────────────────────────

const ImportModal = ({ onDone, onClose }) => {
  const [phase, setPhase]       = React.useState('pick');   // pick | loading | done | error
  const [messages, setMessages] = React.useState([]);
  const [currentStep, setCurrentStep] = React.useState(0);
  const [errorMsg, setErrorMsg] = React.useState('');
  const [dragging, setDragging] = React.useState(false);
  const [doneData, setDoneData] = React.useState(null);
  const [doneSources, setDoneSources] = React.useState([]);
  const [urlInput, setUrlInput] = React.useState('');
  const [fileList, setFileList] = React.useState([]);   // File objects
  const [urlList, setUrlList]   = React.useState([]);   // string[]
  // suggest-b4b5 skill state
  const [suggestPhase, setSuggestPhase]   = React.useState('idle'); // idle | loading | done | error
  const [suggestMsgs,  setSuggestMsgs]    = React.useState([]);
  const [suggestError, setSuggestError]   = React.useState('');
  // add-product sub-step
  const [addStep, setAddStep]       = React.useState(false);
  const [newProd, setNewProd]       = React.useState({ brand_slug: '', segment: '', category: '', name: '' });
  const [prodCatalog, setProdCatalog] = React.useState([]);
  const [savingProd, setSavingProd] = React.useState(false);
  const [openingForm, setOpeningForm] = React.useState(false);
  const [extractProgress, setExtractProgress] = React.useState(0);
  const dropRef      = React.useRef(null);
  const fileRef      = React.useRef(null);
  const aprecPromise = React.useRef(null);  // silent background apreciation

  const totalSources = fileList.length + urlList.length;

  // Simulated progress for step 4 (Claude API wait — no real % available)
  React.useEffect(() => {
    if (currentStep !== 4) {
      if (currentStep > 4) setExtractProgress(100);
      return;
    }
    let pct = 1;
    setExtractProgress(1);
    const interval = setInterval(() => {
      const inc = pct < 60 ? 3 : pct < 82 ? 1 : 0.3;
      pct = Math.min(95, pct + inc);
      setExtractProgress(Math.round(pct));
      if (pct >= 95) clearInterval(interval);
    }, 800);
    return () => clearInterval(interval);
  }, [currentStep]);

  const resetPick = () => {
    setPhase('pick'); setMessages([]); setCurrentStep(0);
    setErrorMsg(''); setDoneData(null); setDoneSources([]); setAddStep(false);
    aprecPromise.current = null; setOpeningForm(false); setExtractProgress(0);
  };

  const addFiles = (incoming) => {
    const valid = Array.from(incoming).filter(f => f.name.endsWith('.pdf') || f.name.endsWith('.docx'));
    if (!valid.length) { setErrorMsg('Formato não suportado. Usa PDF ou DOCX.'); return; }
    setErrorMsg('');
    setFileList(prev => {
      const names = new Set(prev.map(f => f.name));
      return [...prev, ...valid.filter(f => !names.has(f.name))];
    });
  };

  const addUrl = () => {
    const u = urlInput.trim();
    if (!u.startsWith('http')) { setErrorMsg('URL inválido — deve começar por http:// ou https://'); return; }
    if (urlList.includes(u)) { setUrlInput(''); return; }
    setErrorMsg('');
    setUrlList(prev => [...prev, u]);
    setUrlInput('');
  };

  const consumeSSE = async (res) => {
    const reader = res.body.getReader();
    const decoder = new TextDecoder();
    let buf = '';
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      buf += decoder.decode(value, { stream: true });
      const lines = buf.split('\n');
      buf = lines.pop();
      for (const line of lines) {
        if (!line.startsWith('data: ')) continue;
        let evt;
        try { evt = JSON.parse(line.slice(6)); } catch { continue; }
        if (evt.type === 'status') {
          setCurrentStep(evt.step);
          setMessages(m => [...m, evt.message]);
        } else if (evt.type === 'done') {
          setCurrentStep(6);
          setDoneData(evt.data);
          setDoneSources(evt.sources || []);
          setPhase('done');
          // Disparar apreciation preview em background (silencioso)
          aprecPromise.current = fetch('/api/marketing/briefings/apreciation-preview', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
              block1: evt.data.block1 || {}, block2: evt.data.block2 || {},
              block3: evt.data.block3 || {}, block4: evt.data.block4 || {},
              block5: evt.data.block5 || {}, brand_name: evt.data.brand_name || evt.data.brand_slug || '',
            }),
          }).then(r => r.ok ? r.json() : null).catch(() => null);
        } else if (evt.type === 'progress') {
          setExtractProgress(evt.percent);
        } else if (evt.type === 'error') {
          setErrorMsg(evt.message);
          setPhase('error');
        }
      }
    }
  };

  const runExtract = async () => {
    if (totalSources === 0) return;
    setPhase('loading'); setMessages([]); setCurrentStep(0);
    try {
      const fd = new FormData();
      fileList.forEach(f => fd.append('files', f));
      fd.append('urls', JSON.stringify(urlList));
      const res = await fetch('/api/marketing/briefings/extract-multi', { method: 'POST', body: fd });
      await consumeSSE(res);
    } catch (e) { setErrorMsg(e.message); setPhase('error'); }
  };

  const overlayStyle = {
    position: 'fixed', inset: 0, zIndex: 9999,
    background: 'color-mix(in oklch, var(--bg) 60%, transparent)',
    backdropFilter: 'blur(4px)',
    display: 'flex', alignItems: 'center', justifyContent: 'center',
  };
  const cardStyle = {
    background: 'var(--bg-elev)', border: '1px solid var(--border)',
    borderRadius: 12, width: 480, padding: 28,
    boxShadow: '0 8px 32px color-mix(in oklch, var(--bg-base) 40%, transparent)',
    display: 'flex', flexDirection: 'column', gap: 20,
  };

  const tabBtn = (t, label) => (
    <button
      onClick={() => { setMode(t); resetPick(); }}
      style={{
        flex: 1, padding: '7px 0', fontSize: 12, fontWeight: 600, borderRadius: 6, border: 'none', cursor: 'pointer',
        background: mode === t ? 'var(--ai-500)' : 'transparent',
        color: mode === t ? '#fff' : 'var(--text-muted)',
        transition: 'all 0.15s',
      }}
    >{label}</button>
  );

  return (
    <div style={overlayStyle} onClick={e => e.target === e.currentTarget && phase !== 'loading' && onClose()}>
      <div style={cardStyle}>
        {/* Header */}
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <div>
            <div style={{ fontFamily: 'var(--font-display)', fontSize: 16, fontWeight: 600 }}>Importar com IA</div>
            <div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 2 }}>
              Adiciona ficheiros e/ou URLs — a IA analisa tudo de uma vez
            </div>
          </div>
          {phase !== 'loading' && (
            <button className="btn btn-xs" onClick={onClose} style={{ padding: '4px 8px' }}>✕</button>
          )}
        </div>

        {/* ── Pick phase ── */}
        {phase === 'pick' && (
          <>
            {/* Drop zone — múltiplos ficheiros */}
            <input
              ref={fileRef}
              type="file"
              accept=".pdf,.docx"
              multiple
              style={{ display: 'none' }}
              onChange={e => addFiles(e.target.files)}
            />
            <div
              ref={dropRef}
              onDragOver={e => { e.preventDefault(); setDragging(true); }}
              onDragLeave={() => setDragging(false)}
              onDrop={e => { e.preventDefault(); setDragging(false); addFiles(e.dataTransfer.files); }}
              onClick={() => fileRef.current?.click()}
              style={{
                border: `2px dashed ${dragging ? 'var(--ai-500)' : 'var(--border)'}`,
                borderRadius: 8, padding: '20px 24px', textAlign: 'center',
                background: dragging ? 'color-mix(in oklch, var(--ai-500) 6%, transparent)' : 'var(--bg-sunken)',
                transition: 'all 0.15s', cursor: 'pointer',
              }}
            >
              <div style={{ fontSize: 22, marginBottom: 6 }}>📄</div>
              <div style={{ fontSize: 13, fontWeight: 500, color: 'var(--text)', marginBottom: 2 }}>
                Arrasta ficheiros aqui ou clica para escolher
              </div>
              <div style={{ fontSize: 11, color: 'var(--text-muted)' }}>PDF ou DOCX · múltiplos ficheiros · máx. 60 MB cada</div>
            </div>

            {/* URL input */}
            <div style={{ display: 'flex', gap: 8 }}>
              <input
                type="url"
                placeholder="https://... (página de produto, ficha técnica)"
                value={urlInput}
                onChange={e => setUrlInput(e.target.value)}
                onKeyDown={e => e.key === 'Enter' && addUrl()}
                style={{
                  flex: 1, padding: '7px 10px', borderRadius: 6, fontSize: 12,
                  border: '1px solid var(--border)', background: 'var(--bg-elev)',
                  color: 'var(--text)', outline: 'none',
                }}
              />
              <button className="btn btn-sm" onClick={addUrl}
                disabled={!urlInput.trim().startsWith('http')}
                style={{ flexShrink: 0, fontSize: 12 }}>
                + URL
              </button>
            </div>

            {/* Lista de fontes acumuladas */}
            {totalSources > 0 && (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                <div style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', letterSpacing: '0.06em', textTransform: 'uppercase', marginBottom: 2 }}>
                  {totalSources} fonte{totalSources > 1 ? 's' : ''} para análise
                </div>
                {fileList.map((f, i) => (
                  <div key={f.name} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '6px 10px', borderRadius: 6, background: 'var(--bg-sunken)', border: '1px solid var(--border)' }}>
                    <span style={{ fontSize: 13 }}>📄</span>
                    <span style={{ flex: 1, fontSize: 12, color: 'var(--text)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{f.name}</span>
                    <span style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', flexShrink: 0 }}>{(f.size / 1024 / 1024).toFixed(1)} MB</span>
                    <button onClick={() => setFileList(l => l.filter((_, j) => j !== i))}
                      style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-dim)', fontSize: 14, padding: 0, lineHeight: 1 }}>✕</button>
                  </div>
                ))}
                {urlList.map((u, i) => (
                  <div key={u} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '6px 10px', borderRadius: 6, background: 'var(--bg-sunken)', border: '1px solid var(--border)' }}>
                    <span style={{ fontSize: 13 }}>🔗</span>
                    <span style={{ flex: 1, fontSize: 12, color: 'var(--text)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{u}</span>
                    <button onClick={() => setUrlList(l => l.filter((_, j) => j !== i))}
                      style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-dim)', fontSize: 14, padding: 0, lineHeight: 1 }}>✕</button>
                  </div>
                ))}
              </div>
            )}

            {errorMsg && (
              <div style={{ fontSize: 12, color: 'var(--danger)', padding: '6px 10px', borderRadius: 6, background: 'color-mix(in oklch, var(--danger) 8%, transparent)' }}>
                {errorMsg}
              </div>
            )}

            <button
              className="btn btn-ai"
              onClick={runExtract}
              disabled={totalSources === 0}
              style={{ width: '100%', justifyContent: 'center', fontSize: 13 }}
            >
              Analisar {totalSources > 0 ? `${totalSources} fonte${totalSources > 1 ? 's' : ''}` : ''} →
            </button>
          </>
        )}

        {/* Progress */}
        {phase === 'loading' && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
            {IMPORT_STEPS.map(s => {
              const done    = currentStep > s.step;
              const active  = currentStep === s.step;
              const pending = currentStep < s.step;
              return (
                <div key={s.step} style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                  <div style={{
                    width: 28, height: 28, borderRadius: '50%', flexShrink: 0,
                    display: 'grid', placeItems: 'center', fontSize: 12, fontWeight: 600,
                    background: done    ? 'color-mix(in oklch, var(--success) 20%, transparent)'
                               : active ? 'color-mix(in oklch, var(--ai-500) 20%, transparent)'
                               : 'var(--bg-sunken)',
                    border: `2px solid ${done ? 'var(--success)' : active ? 'var(--ai-500)' : 'var(--border)'}`,
                    color: done ? 'var(--success)' : active ? 'var(--ai-500)' : 'var(--text-dim)',
                    transition: 'all 0.3s',
                  }}>
                    {done ? '✓' : s.step}
                  </div>
                  <div style={{ flex: 1 }}>
                    <div style={{
                      fontSize: 13, fontWeight: active ? 600 : 400,
                      color: pending ? 'var(--text-dim)' : 'var(--text)',
                      transition: 'all 0.3s',
                    }}>{s.label}</div>
                    {active && messages.length > 0 && (
                      <div style={{ fontSize: 11, color: 'var(--ai-500)', marginTop: 2, fontFamily: 'var(--font-mono)' }}>
                        {messages[messages.length - 1]}
                        {s.step === 4 && extractProgress > 0 && extractProgress < 100 && (
                          <span style={{ marginLeft: 6, fontWeight: 700 }}>{extractProgress}%</span>
                        )}
                      </div>
                    )}
                    {done && messages[s.step - 1] && (
                      <div style={{ fontSize: 11, color: 'var(--text-dim)', marginTop: 2, fontFamily: 'var(--font-mono)' }}>
                        {messages[s.step - 1]}
                      </div>
                    )}
                  </div>
                  {active && (
                    <div style={{
                      width: 16, height: 16, borderRadius: '50%', flexShrink: 0,
                      border: '2px solid var(--ai-500)', borderTopColor: 'transparent',
                      animation: 'spin 0.8s linear infinite',
                    }} />
                  )}
                </div>
              );
            })}
            <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
          </div>
        )}

        {/* Done — resumo completude */}
        {phase === 'done' && doneData && !addStep && (() => {
          const completeness = calcCompleteness(doneData);
          const productMissing = !doneData.product_id;

          const handleAddProduct = async () => {
            const prods = await fetch(`/api/marketing/products?brand_id=${doneData.brand_slug || 'todas'}`).then(r => r.json()).catch(() => []);
            setProdCatalog(prods);
            setNewProd({ brand_slug: doneData.brand_slug || '', segment: '', category: '', name: doneData.block1?.commercial_name || '' });
            setAddStep(true);
          };

          return (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
              {/* Banner */}
              <div style={{ padding: '10px 14px', borderRadius: 8, fontSize: 13,
                background: 'color-mix(in oklch, var(--success) 10%, transparent)',
                border: '1px solid color-mix(in oklch, var(--success) 30%, transparent)',
                color: 'var(--success)', fontWeight: 500 }}>
                Extracção concluída — revê o resumo abaixo
              </div>

              {/* Completude blocos 1-3 */}
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                {completeness.map(b => {
                  const full    = b.missing.length === 0;
                  const empty   = b.filled === 0;
                  const color   = full ? 'var(--success)' : empty ? 'var(--danger)' : 'var(--warning, #f59e0b)';
                  const bg      = full ? 'color-mix(in oklch, var(--success) 8%, transparent)'
                                : empty ? 'color-mix(in oklch, var(--danger) 8%, transparent)'
                                : 'color-mix(in oklch, var(--warning, #f59e0b) 8%, transparent)';
                  const icon    = full ? '✓' : empty ? '✗' : '⚠';
                  return (
                    <div key={b.id} style={{ borderRadius: 7, padding: '8px 12px', background: bg, border: `1px solid color-mix(in oklch, ${color} 25%, transparent)` }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                        <span style={{ color, fontWeight: 700, fontSize: 13, width: 16 }}>{icon}</span>
                        <span style={{ fontSize: 12, fontWeight: 600, color: 'var(--text)' }}>Bloco {b.id} — {b.label}</span>
                        <span style={{ marginLeft: 'auto', fontSize: 11, fontFamily: 'var(--font-mono)', color }}>
                          {b.filled}/{b.total}
                        </span>
                      </div>
                      {b.missing.length > 0 && (
                        <div style={{ marginTop: 4, marginLeft: 24, fontSize: 11, color: 'var(--text-muted)', lineHeight: 1.5 }}>
                          Em falta: {b.missing.join(' · ')}
                        </div>
                      )}
                    </div>
                  );
                })}
                {/* Blocos 4+5 — skill ou extractado */}
                {(() => {
                  const isEmptyVal = (v) => !v || (Array.isArray(v) ? v.every(i => !i?.toString().trim()) : !String(v).trim());
                  const b4 = doneData?.block4 || {};
                  const b5 = doneData?.block5 || {};
                  const b4Empty = isEmptyVal(b4.objective) && isEmptyVal(b4.key_message) && isEmptyVal(b4.channels);
                  const b5Empty = isEmptyVal(b5.prohibited_claims) && isEmptyVal(b5.off_brand_messages) && isEmptyVal(b5.competitors_not_name);
                  const needsSkill = b4Empty || b5Empty;

                  const runSuggest = async () => {
                    setSuggestPhase('loading'); setSuggestMsgs([]); setSuggestError('');
                    try {
                      const res = await fetch('/api/marketing/briefings/suggest-b4b5', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({
                          block1: doneData?.block1 || {},
                          block2: doneData?.block2 || {},
                          block3: doneData?.block3 || {},
                          block4: doneData?.block4 || {},
                          block5: doneData?.block5 || {},
                          brand_name: doneData?.brand_slug || '',
                        }),
                      });
                      const reader = res.body.getReader();
                      const dec = new TextDecoder();
                      let buf = '';
                      while (true) {
                        const { done, value } = await reader.read();
                        if (done) break;
                        buf += dec.decode(value, { stream: true });
                        const lines = buf.split('\n');
                        buf = lines.pop();
                        for (const line of lines) {
                          if (!line.startsWith('data: ')) continue;
                          let evt; try { evt = JSON.parse(line.slice(6)); } catch { continue; }
                          if (evt.type === 'status') setSuggestMsgs(m => [...m, evt.message]);
                          else if (evt.type === 'done') {
                            setDoneData(prev => ({ ...prev, block4: evt.data.block4, block5: evt.data.block5, _b4_suggested: true, _b5_suggested: true }));
                            setSuggestPhase('done');
                          } else if (evt.type === 'error') { setSuggestError(evt.message); setSuggestPhase('error'); }
                        }
                      }
                    } catch (e) { setSuggestError(e.message); setSuggestPhase('error'); }
                  };

                  return (
                    <>
                      {[{ id: 4, label: 'Campanha', empty: b4Empty, suggested: doneData?._b4_suggested },
                        { id: 5, label: 'Restrições', empty: b5Empty, suggested: doneData?._b5_suggested }].map(b => (
                        <div key={b.id} style={{
                          borderRadius: 7, padding: '8px 12px',
                          background: b.suggested ? 'color-mix(in oklch, var(--ai-500) 7%, transparent)'
                                    : b.empty ? 'var(--bg-sunken)' : 'color-mix(in oklch, var(--success) 8%, transparent)',
                          border: `1px solid ${b.suggested ? 'color-mix(in oklch, var(--ai-500) 25%, transparent)'
                                             : b.empty ? 'var(--border)' : 'color-mix(in oklch, var(--success) 25%, transparent)'}`,
                        }}>
                          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                            <span style={{ fontSize: 13, width: 16 }}>
                              {b.suggested ? '✦' : b.empty ? '—' : '✓'}
                            </span>
                            <span style={{ fontSize: 12, fontWeight: 600, color: 'var(--text)' }}>Bloco {b.id} — {b.label}</span>
                            <span style={{ marginLeft: 'auto', fontSize: 11, color: b.suggested ? 'var(--ai-500)' : b.empty ? 'var(--text-dim)' : 'var(--success)', fontWeight: b.suggested ? 600 : 400 }}>
                              {b.suggested ? 'Sugerido por AI' : b.empty ? 'Não encontrado nas fontes' : 'Extraído das fontes'}
                            </span>
                          </div>
                        </div>
                      ))}

                      {/* Banner skill */}
                      {needsSkill && suggestPhase === 'idle' && (
                        <div style={{ padding: '12px 14px', borderRadius: 8, background: 'color-mix(in oklch, var(--ai-500) 6%, transparent)', border: '1px solid color-mix(in oklch, var(--ai-500) 22%, transparent)' }}>
                          <div style={{ fontSize: 12, color: 'var(--text)', marginBottom: 8, lineHeight: 1.5 }}>
                            <strong style={{ color: 'var(--ai-500)' }}>B{b4Empty && b5Empty ? '4 e B5' : b4Empty ? '4' : '5'} não {b4Empty && b5Empty ? 'foram encontrados' : 'foi encontrado'} nas fontes.</strong>{' '}
                            A Digi AI pode sugerir com base no produto, ICP e posicionamento já extraídos.
                          </div>
                          <button className="btn btn-sm btn-ai" onClick={runSuggest}>
                            ✦ Sugerir B{b4Empty && b5Empty ? '4 e B5' : b4Empty ? '4' : '5'} com AI
                          </button>
                        </div>
                      )}

                      {/* Progress da skill */}
                      {suggestPhase === 'loading' && (
                        <div style={{ padding: '12px 14px', borderRadius: 8, background: 'color-mix(in oklch, var(--ai-500) 6%, transparent)', border: '1px solid color-mix(in oklch, var(--ai-500) 22%, transparent)' }}>
                          <div style={{ fontSize: 11, color: 'var(--ai-500)', fontWeight: 600, marginBottom: 6 }}>✦ A gerar sugestões...</div>
                          {suggestMsgs.map((m, i) => (
                            <div key={i} style={{ fontSize: 11, color: 'var(--text-muted)', marginBottom: 2 }}>{m}</div>
                          ))}
                        </div>
                      )}

                      {/* Erro da skill */}
                      {suggestPhase === 'error' && (
                        <div style={{ padding: '10px 14px', borderRadius: 8, background: 'color-mix(in oklch, var(--danger) 6%, transparent)', border: '1px solid color-mix(in oklch, var(--danger) 22%, transparent)', fontSize: 12, color: 'var(--danger)' }}>
                          ✗ {suggestError}
                          <button className="btn btn-sm" style={{ marginLeft: 10 }} onClick={() => setSuggestPhase('idle')}>Tentar novamente</button>
                        </div>
                      )}
                    </>
                  );
                })()}
              </div>

              {/* Produto em falta */}
              {productMissing && (
                <div style={{ padding: '10px 12px', borderRadius: 8, fontSize: 12,
                  background: 'color-mix(in oklch, var(--warning, #f59e0b) 8%, transparent)',
                  border: '1px solid color-mix(in oklch, var(--warning, #f59e0b) 25%, transparent)' }}>
                  <div style={{ fontWeight: 600, color: 'var(--text)', marginBottom: 2 }}>
                    ⚠ Produto não encontrado na BD
                  </div>
                  <div style={{ color: 'var(--text-muted)' }}>
                    "{doneData.block1?.commercial_name || 'nome não detectado'}" — clica em Adicionar produto para o registar.
                  </div>
                </div>
              )}

              {/* Acções */}
              <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
                <button className="btn btn-sm" onClick={resetPick}>Extrair novamente</button>
                {productMissing && (
                  <button className="btn btn-sm" onClick={handleAddProduct}>+ Adicionar produto</button>
                )}
                <button className="btn btn-sm btn-ai" style={{ marginLeft: 'auto' }} disabled={openingForm}
                  onClick={async () => {
                    if (openingForm) return;
                    setOpeningForm(true);
                    const aprec = await Promise.race([
                      aprecPromise.current || Promise.resolve(null),
                      new Promise(r => setTimeout(() => r(null), 8000)),
                    ]);
                    onDone({ ...doneData, ai_apreciation: aprec || undefined }, doneSources);
                  }}>
                  {openingForm ? 'A preparar…' : 'Abrir formulário →'}
                </button>
              </div>
            </div>
          );
        })()}

        {/* Done — sub-step: adicionar produto */}
        {phase === 'done' && doneData && addStep && (() => {
          const segments    = [...new Set(prodCatalog.map(p => p.segment).filter(Boolean))];
          const hasSegments = segments.length > 0;
          const categories  = [...new Set(prodCatalog
            .filter(p => !hasSegments || !newProd.segment || p.segment === newProd.segment)
            .map(p => p.category).filter(Boolean))];

          const handleSave = async () => {
            if (!newProd.name.trim()) return;
            setSavingProd(true);
            try {
              const r = await fetch('/api/marketing/products', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(newProd),
              });
              const prod = await r.json();
              if (prod.id) {
                setDoneData(d => ({ ...d, product_id: String(prod.id), product_family: prod.category || '', product_segment: prod.segment || '' }));
                setAddStep(false);
              }
            } catch {}
            setSavingProd(false);
          };

          const inp = (placeholder, value, onChange, type = 'text') => (
            <input type={type} placeholder={placeholder} value={value} onChange={onChange} style={{
              width: '100%', boxSizing: 'border-box', padding: '7px 10px',
              borderRadius: 6, fontSize: 12, border: '1px solid var(--border)',
              background: 'var(--bg-elev)', color: 'var(--text)', outline: 'none',
            }} />
          );

          const sel = (value, onChange, opts, placeholder) => (
            <select value={value} onChange={onChange} style={{
              width: '100%', boxSizing: 'border-box', padding: '7px 10px',
              borderRadius: 6, fontSize: 12, border: '1px solid var(--border)',
              background: 'var(--bg-elev)', color: value ? 'var(--text)' : 'var(--text-dim)', outline: 'none',
            }}>
              <option value="">{placeholder}</option>
              {opts.map(o => <option key={o} value={o}>{o}</option>)}
            </select>
          );

          return (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
              <div style={{ fontWeight: 600, fontSize: 13 }}>Adicionar produto à BD</div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                <div>
                  <div style={{ fontSize: 11, color: 'var(--text-muted)', marginBottom: 4 }}>MARCA</div>
                  <input value={newProd.brand_slug} disabled style={{
                    width: '100%', boxSizing: 'border-box', padding: '7px 10px',
                    borderRadius: 6, fontSize: 12, border: '1px solid var(--border)',
                    background: 'var(--bg-sunken)', color: 'var(--text-muted)', outline: 'none',
                  }} />
                </div>
                {hasSegments && (
                  <div>
                    <div style={{ fontSize: 11, color: 'var(--text-muted)', marginBottom: 4 }}>SEGMENTO</div>
                    {sel(newProd.segment, e => setNewProd(p => ({ ...p, segment: e.target.value, category: '' })), segments, 'Escolher segmento...')}
                  </div>
                )}
                <div>
                  <div style={{ fontSize: 11, color: 'var(--text-muted)', marginBottom: 4 }}>CATEGORIA</div>
                  {sel(newProd.category, e => setNewProd(p => ({ ...p, category: e.target.value })),
                    categories,
                    hasSegments && !newProd.segment ? 'Escolhe o segmento primeiro' : 'Escolher categoria...')}
                </div>
                <div>
                  <div style={{ fontSize: 11, color: 'var(--text-muted)', marginBottom: 4 }}>NOME DO PRODUTO <span style={{ color: 'var(--ai-500)' }}>·</span></div>
                  {inp('Nome do produto', newProd.name, e => setNewProd(p => ({ ...p, name: e.target.value })))}
                </div>
              </div>
              <div style={{ display: 'flex', gap: 8 }}>
                <button className="btn btn-sm" onClick={() => setAddStep(false)}>← Voltar</button>
                <button className="btn btn-sm btn-ai" style={{ marginLeft: 'auto' }}
                  onClick={handleSave}
                  disabled={savingProd || !newProd.name.trim()}>
                  {savingProd ? 'A guardar...' : 'Adicionar produto'}
                </button>
              </div>
            </div>
          );
        })()}

        {/* Error */}
        {phase === 'error' && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
            <div style={{
              padding: '12px 14px', borderRadius: 8, fontSize: 13,
              background: 'color-mix(in oklch, var(--danger) 10%, transparent)',
              border: '1px solid color-mix(in oklch, var(--danger) 30%, transparent)',
              color: 'var(--danger)',
            }}>
              {errorMsg}
            </div>
            <button className="btn btn-sm" onClick={resetPick}>
              Tentar novamente
            </button>
          </div>
        )}
      </div>
    </div>
  );
};

// ─── Dev user helper ──────────────────────────────────────────────────────────

const getMktUser = (user) => {
  const p = new URLSearchParams(window.location.search);
  const dev = p.get('dev'), mkt = p.get('mkt_role');
  if (mkt === 'strategist') return { nome: 'Carlos Alves',   role: 'strategist', email: 'carlos.alves@decal.pt'       };
  if (dev === 'ana')        return { nome: 'Ana Rodrigues',  role: 'strategist', email: 'ana.rodrigues@branddigital.pt' };
  if (dev === 'rui')        return { nome: 'Rui Leitão',     role: 'board',      email: 'rui.leitao@digidelta.pt'   };
  if (dev === 'armando')    return { nome: 'Armando Mota',   role: 'ceo',        email: 'armando.mota@digidelta.pt' };
  if (dev === 'bruno')      return { nome: 'Bruno Rosa',     role: 'ceo',        email: 'bruno.rosa@digidelta.pt'   };
  if (dev === 'costa')      return { nome: 'Fábio Costa',    role: 'admin',      email: 'fabio.costa@digidelta.pt'  };
  if (dev === 'joao')       return { nome: 'João Paulino',   role: 'admin',      email: 'joao.paulino@digidelta.pt' };
  const u     = user || window.currentUser;
  const nome  = u?.nome_apresentar || u?.nome || 'Utilizador';
  const tipo  = u?.tipo || '';
  const email = u?.email || u?.email_profissional || null;
  const role  = tipo === 'admin' ? 'admin' : tipo === 'marketing' ? 'strategist' : 'board';
  return { nome, role, email };
};

// ─── Orquestrador principal ────────────────────────────────────────────────────

const MktBriefingsScreen = ({ userName, onNavToSub, user }) => {
  const { brand: brandId } = window.useMktBrand ? window.useMktBrand() : { brand: 'todas' };
  const [view, setView] = React.useState('list'); // list | form | detail
  const [selected, setSelected] = React.useState(null);
  const [showImport, setShowImport] = React.useState(false);
  const [mktUser] = React.useState(() => getMktUser(user));

  const currentUserName = mktUser.nome || userName || null;
  const userRole = mktUser.role;

  const goList   = () => { setSelected(null); setView('list'); };
  const goNew    = () => { setSelected(null); setView('form'); };
  const goEdit   = (b) => { if (b) setSelected(b); setView('form'); };
  const goDetail = (b) => { setSelected(b);   setView('detail'); };

  const handleGenerateStrategy = async (b) => {
    if (b?.id) {
      const result = await window.MktBriefingsAPI.ensureStrategy(b.id);
      if (result?.id) {
        try { localStorage.setItem('mkt-pending-proposal', String(result.id)); } catch {}
      }
    }
    onNavToSub?.('conteudos');
  };

  const handleCriarCampanha = async (b) => {
    const titulo = b.commercial_name || b.product_name || `Campanha #${b.id}`;
    try {
      await fetch('/api/marketing/campanhas', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ briefing_id: b.id, titulo, created_by: mktUser.email }),
      });
    } catch (e) {
      console.warn('[criarCampanha] erro ao criar:', e.message);
    }
    onNavToSub?.('campanhas');
  };

  const handleImportDone = (data, sources) => {
    setShowImport(false);
    setSelected({ ...data, _import_origin: sources?.length ? { sources } : null });
    setView('form');
  };

  const handleSave = async (formData, resolvedBrandId, apreciation) => {
    const bid = resolvedBrandId || brandId;
    const aprecPayload = apreciation ? { ai_apreciation: apreciation } : {};
    if (selected?.id) {
      await window.MktBriefingsAPI.update(selected.id, { ...formData, ...aprecPayload });
    } else {
      const importOrigin = selected?._import_origin || null;
      await window.MktBriefingsAPI.create({ brand_id: bid, created_by_name: currentUserName, import_origin: importOrigin, ...formData, ...aprecPayload });
    }
    goList();
  };

  const handleSubmit = async (formData, resolvedBrandId, apreciation) => {
    let id = selected?.id;
    if (!id) {
      const bid = resolvedBrandId || brandId;
      const importOrigin = selected?._import_origin || null;
      const aprecPayload = apreciation ? { ai_apreciation: apreciation } : {};
      const created = await window.MktBriefingsAPI.create({ brand_id: bid, created_by_name: currentUserName, import_origin: importOrigin, ...formData, ...aprecPayload });
      id = created?.id;
    } else if (apreciation) {
      await window.MktBriefingsAPI.update(selected.id, { ...formData, ai_apreciation: apreciation });
    }
    if (id) await window.MktBriefingsAPI.submit(id);
    goList();
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 }}>
      {showImport && (
        <ImportModal
          onDone={handleImportDone}
          onClose={() => setShowImport(false)}
        />
      )}
      {view === 'list' && (
        <BriefingsList
          brandId={brandId}
          onSelect={b => goEdit(b)}
          onNew={goNew}
          onEdit={goEdit}
          onImport={() => setShowImport(true)}
          onGenerateStrategy={handleGenerateStrategy}
          onCriarCampanha={handleCriarCampanha}
          userRole={userRole}
          userName={currentUserName}
        />
      )}
      {view === 'form' && (
        <BriefingForm
          briefing={selected}
          brandId={brandId}
          onSave={handleSave}
          onSubmit={handleSubmit}
          onCancel={goList}
          onApproved={goList}
          onRejected={goList}
          onGenerateStrategy={handleGenerateStrategy}
          onCriarCampanha={handleCriarCampanha}
          userRole={userRole}
          userName={currentUserName}
          userEmail={mktUser.email}
        />
      )}
      {view === 'detail' && (
        <BriefingDetail
          briefing={selected}
          onEdit={goEdit}
          onBack={goList}
          onApproved={goList}
          onRejected={goList}
          onGenerateStrategy={handleGenerateStrategy}
          onCriarCampanha={handleCriarCampanha}
          userRole={userRole}
          userName={currentUserName}
          userEmail={mktUser.email}
        />
      )}
    </div>
  );
};

window.MktBriefingsScreen = MktBriefingsScreen;
