/* 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: 'var(--text-muted, #4a5e7a)',  bg: 'var(--bg-sunken, #eef1f6)' },
  submetido:        { label: 'Para Manager',  color: 'var(--ai-500, #3859D0)',      bg: 'color-mix(in srgb, #3859D0 10%, transparent)' },
  validado_manager: { label: 'Para CEO',      color: 'var(--ai-500, #3859D0)',      bg: 'color-mix(in srgb, #3859D0 10%, transparent)' },
  validado_ceo:     { label: 'Para Chairman', color: 'var(--ai-500, #3859D0)',      bg: 'color-mix(in srgb, #3859D0 10%, transparent)' },
  aprovado:         { label: 'Aprovado',      color: 'var(--ai-500, #3859D0)',      bg: 'color-mix(in srgb, #3859D0 16%, transparent)' },
  rejeitado:        { label: 'Rejeitado',     color: 'var(--danger, #dc2626)',      bg: 'color-mix(in srgb, #dc2626 8%, transparent)' },
  em_campanha:      { label: 'Em Campanha',   color: 'var(--ai-500, #3859D0)',      bg: 'color-mix(in srgb, #3859D0 16%, transparent)' },
};

// ─── Configuração de aprovadores (espelho do servidor) ────────────────────────
// Fluxo sequencial: mkt_manager age em 'submetido', brand_dir (CEO) em 'validado_manager', rl (Chairman) em 'validado_ceo'
const APPROVER_CONFIG_FE = {
  'fabio.costa@digidelta.pt':  { roles: ['mkt_manager', 'rl', 'brand_dir'], brands: 'all' },
  'joao.paulino@digidelta.pt': { roles: ['rl', 'brand_dir'],                brands: 'all' },
  'rui.leitao@digidelta.pt':   { roles: ['rl'],                             brands: 'all' },
  'armando.mota@digidelta.pt': { roles: ['brand_dir'],                      brands: ['mimaki','biond','decal','alldecor','sensek'] },
  'bruno.rosa@digidelta.pt':   { roles: ['brand_dir'],                      brands: ['netscreen'] },
};
// Ordem dos roles por marca (mkt_manager primeiro, brand_dir depois, rl por último)
const BRAND_REQUIRED_ROLES_FE = { digidelta: ['mkt_manager', 'rl'], _default: ['mkt_manager', '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',
};

// Fase activa baseada no status do briefing
const getActiveRole = (briefingStatus, requiredRoles) => {
  if (briefingStatus === 'submetido')        return 'mkt_manager';
  if (briefingStatus === 'validado_manager') 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' : role === 'mkt_manager' ? 'Fábio Costa' : (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: '',
  tipo_campanha: 'produto',
  block1: { commercial_name: '', product_family: '', elevator_pitch: '', usps: [''], certifications: [], applications: [], geo_markets: [] },
  block2: { decision_maker: '', end_user: '', pain_points: [''], motivators: [''], objections: [''], purchase_trigger: '' },
  block3: { competitors: [''], differentiation_args: [''], market_trends: [''], positioning_narrative: '', sensitive_info: '' },
  block4: { objective: '', kpis: [], channels: [], tone: '', key_message: '', timeline_start: '', timeline_end: '', previous_campaigns: [], commercial_offer: null, meta_ads_conversao: '' },
  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(--ai-500, #3859D0)' : pct >= 40 ? 'var(--text-muted, #4a5e7a)' : 'var(--text-dim, #6b7fa3)';
  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: 'meta_ads',           label: 'Meta Ads',           hint: 'Facebook + Instagram' },
  { id: 'linkedin_ads',       label: 'LinkedIn Ads',       hint: '' },
  { id: 'google_ads_search',  label: 'Google Search Ads',  hint: 'RSA' },
  { id: 'google_ads_display', label: 'Google Display',     hint: 'Banners' },
  { id: 'email',              label: 'Email Marketing',    hint: 'Brevo' },
  { id: 'whatsapp',           label: 'WhatsApp',           hint: '' },
  { id: 'website',            label: 'Website / Blog',     hint: '' },
  { id: 'muppi_led',          label: 'Painéis LED',        hint: '' },
];

// ─── 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, action }) => (
  <div>
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>
      <label style={{ fontSize: 11, fontWeight: 600, color: 'var(--text-dim)', letterSpacing: '0.04em' }}>
        {label}
        {required && <span style={{ color: 'var(--danger, #CF2E2E)', marginLeft: 3 }}>*</span>}
      </label>
      {action}
    </div>
    {children}
    {hint && <div style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 4, lineHeight: 1.4 }}>{hint}</div>}
  </div>
);

const FormCard = ({ title, children }) => (
  <div style={{ background: 'var(--bg-elev, #ffffff)', border: '1px solid var(--border)', borderRadius: 10, padding: '20px 24px', marginBottom: 16, display: 'flex', flexDirection: 'column', gap: 20 }}>
    {title && (
      <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--text-muted)', fontFamily: 'var(--font-mono)', paddingBottom: 8, borderBottom: '1px solid var(--border)' }}>
        {title}
      </div>
    )}
    {children}
  </div>
);

const TInput = ({ value, onChange, placeholder, multiline, rows = 3 }) => {
  const [focused, setFocused] = React.useState(false);
  const base = {
    width: '100%', padding: '9px 14px', boxSizing: 'border-box',
    background: 'var(--bg-elev, #ffffff)',
    border: `1px solid ${focused ? 'var(--ai-500)' : 'var(--border-strong, var(--border))'}`,
    borderRadius: 7, 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 TDateInput = ({ value, onChange }) => {
  const [focused, setFocused] = React.useState(false);
  return (
    <input type="date" value={value || ''} onChange={e => onChange(e.target.value)}
      onFocus={() => setFocused(true)} onBlur={() => setFocused(false)}
      style={{
        width: '100%', padding: '9px 14px', boxSizing: 'border-box',
        background: 'var(--bg-elev, #ffffff)',
        border: `1px solid ${focused ? 'var(--ai-500)' : 'var(--border-strong, var(--border))'}`,
        borderRadius: 7, 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 TSelect = ({ value, onChange, options, placeholder }) => {
  const [open,    setOpen]    = React.useState(false);
  const [focused, setFocused] = React.useState(false);
  const [panelPos, setPanelPos] = React.useState({ top: 0, left: 0, width: 0 });
  const ref      = React.useRef(null);
  const triggerRef = React.useRef(null);
  const selected = options.find(o => o.value === value);

  React.useEffect(() => {
    if (!open) return;
    // Calcular posição via fixed — dropdown sempre abaixo, nunca sobrepõe o layout
    if (triggerRef.current) {
      const rect = triggerRef.current.getBoundingClientRect();
      setPanelPos({ top: rect.bottom, left: rect.left, width: rect.width });
    }
    const handler = (e) => { if (ref.current && !ref.current.contains(e.target)) { setOpen(false); setFocused(false); } };
    document.addEventListener('mousedown', handler);
    return () => document.removeEventListener('mousedown', handler);
  }, [open]);

  const handleKeyDown = (e) => {
    if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setOpen(o => !o); }
    if (e.key === 'Escape') { setOpen(false); setFocused(false); }
  };

  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button type="button"
        onClick={() => { setOpen(o => !o); setFocused(true); }}
        onFocus={() => setFocused(true)}
        onBlur={(e) => { if (!ref.current?.contains(e.relatedTarget)) { setFocused(false); if (!open) {} } }}
        ref={triggerRef}
        onKeyDown={handleKeyDown}
        style={{
          width: '100%', padding: '9px 14px', boxSizing: 'border-box', textAlign: 'left',
          background: 'var(--bg-elev, #ffffff)',
          border: `1px solid ${(open || focused) ? 'var(--ai-500)' : 'var(--border-strong, var(--border))'}`,
          borderRadius: 7,
          color: selected ? 'var(--text)' : 'var(--text-muted)', fontSize: 13,
          fontFamily: 'var(--font-body)', cursor: 'pointer', outline: 'none',
          boxShadow: (open || focused) ? '0 0 0 3px color-mix(in oklch, var(--ai-500) 12%, transparent)' : 'none',
          transition: 'border-color 0.15s, box-shadow 0.15s',
          display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8,
        }}>
        <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 }}>
          {selected ? selected.label : (placeholder || 'Seleccionar...')}
        </span>
        <svg width="12" height="12" viewBox="0 0 12 12" fill="none" style={{ flexShrink: 0, transition: 'transform 0.2s', transform: open ? 'rotate(180deg)' : 'none', color: 'var(--text-muted)' }}>
          <path d="M2 4l4 4 4-4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
        </svg>
      </button>
      {open && (
        <div style={{
          position: 'fixed',
          top: panelPos.top, left: panelPos.left, width: panelPos.width,
          zIndex: 9999,
          background: 'var(--bg-elev, #ffffff)',
          border: '1px solid var(--ai-500)',
          borderRadius: 7,
          boxShadow: '0 12px 32px rgba(17,41,84,0.16), 0 2px 8px rgba(17,41,84,0.08)',
          maxHeight: 220, overflowY: 'auto',
          animation: 'tselect-in 0.12s ease-out',
        }}>
        <style>{`@keyframes tselect-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }`}</style>
          {(value !== '' && value !== null && value !== undefined) && (
            <button type="button" onClick={() => { onChange(''); setOpen(false); setFocused(false); }}
              style={{ display: 'block', width: '100%', textAlign: 'left', padding: '8px 14px', fontSize: 13, color: 'var(--text-muted)', background: 'transparent', border: 'none', cursor: 'pointer', fontFamily: 'var(--font-body)', fontStyle: 'italic' }}>
              {placeholder || 'Seleccionar...'}
            </button>
          )}
          {options.map(o => (
            <button type="button" key={o.value} onClick={() => { onChange(o.value); setOpen(false); setFocused(false); }}
              style={{
                display: 'block', width: '100%', textAlign: 'left', padding: '8px 14px', fontSize: 13,
                color: o.value === value ? 'var(--ai-500)' : 'var(--text)',
                background: o.value === value ? 'color-mix(in oklch, var(--ai-500) 8%, transparent)' : 'transparent',
                border: 'none', cursor: 'pointer', fontFamily: 'var(--font-body)',
                fontWeight: o.value === value ? 600 : 400,
                transition: 'background 0.1s',
              }}
              onMouseEnter={e => { if (o.value !== value) e.currentTarget.style.background = 'var(--bg-sunken)'; }}
              onMouseLeave={e => { e.currentTarget.style.background = o.value === value ? 'color-mix(in oklch, var(--ai-500) 8%, transparent)' : 'transparent'; }}>
              {o.label}
            </button>
          ))}
        </div>
      )}
    </div>
  );
};

const ChipInput = ({ items, onChange, placeholder, maxItems, sortable }) => {
  const [inputVal, setInputVal]   = React.useState('');
  const [focused, setFocused]     = React.useState(false);
  const [dragIdx, setDragIdx]     = React.useState(null);
  const [dragOver, setDragOver]   = React.useState(null);
  const [editIdx, setEditIdx]     = React.useState(null);
  const [editVal, setEditVal]     = React.useState('');
  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) => { if (editIdx === i) setEditIdx(null); onChange(filled.filter((_, idx) => idx !== i)); };

  const startEdit = (i) => { setEditIdx(i); setEditVal(filled[i]); };
  const saveEdit  = () => {
    if (editIdx === null) return;
    const v = editVal.trim();
    if (v) { const next = [...filled]; next[editIdx] = v; onChange(next); }
    setEditIdx(null);
  };
  const cancelEdit = () => setEditIdx(null);

  const handleDrop = (targetIdx) => {
    if (dragIdx === null || dragIdx === targetIdx) return;
    const next = [...filled];
    const [moved] = next.splice(dragIdx, 1);
    next.splice(targetIdx, 0, moved);
    onChange(next);
    setDragIdx(null);
    setDragOver(null);
  };

  return (
    <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: '8px 12px',
              background: 'var(--bg-elev, #ffffff)',
              border: `1px solid ${focused ? 'var(--ai-500)' : 'var(--border-strong, var(--border))'}`,
              borderRadius: 7, 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 14px', height: 38, borderRadius: 7, border: 'none',
              background: inputVal.trim() ? 'var(--ai-500)' : 'var(--bg-sunken)',
              color: inputVal.trim() ? '#fff' : 'var(--text-dim)',
              cursor: inputVal.trim() ? 'pointer' : 'not-allowed', fontSize: 12,
              transition: 'background 0.15s, color 0.15s', whiteSpace: 'nowrap', flexShrink: 0,
            }}>+</button>
        </div>
      )}

      {sortable && filled.length > 0 && (
        <>
          <div style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', marginTop: 8, marginBottom: 6, letterSpacing: '0.05em' }}>
            Arrasta para ordenar — o 1.° tem maior peso na geração
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            {filled.map((item, i) => {
              const isDragging = dragIdx === i;
              const isOver     = dragOver === i;
              const isEditing  = editIdx === i;
              return (
                <div key={i}
                  draggable={!isEditing}
                  onDragStart={() => !isEditing && setDragIdx(i)}
                  onDragOver={e => { e.preventDefault(); if (!isEditing) setDragOver(i); }}
                  onDrop={() => !isEditing && handleDrop(i)}
                  onDragEnd={() => { setDragIdx(null); setDragOver(null); }}
                  style={{
                    display: 'flex', alignItems: 'flex-start', gap: 8,
                    padding: isEditing ? '6px 10px' : '8px 10px', borderRadius: 7,
                    background: isEditing ? 'var(--bg-elev, #fff)' : isDragging ? 'color-mix(in oklch, var(--ai-500) 5%, transparent)' : isOver ? 'color-mix(in oklch, var(--ai-500) 12%, transparent)' : 'var(--bg-elev, #fff)',
                    border: `1px solid ${isEditing ? 'var(--ai-500)' : isOver ? 'var(--ai-500)' : isDragging ? 'color-mix(in oklch, var(--ai-500) 30%, transparent)' : 'var(--border)'}`,
                    boxShadow: isEditing ? '0 0 0 3px color-mix(in oklch, var(--ai-500) 12%, transparent)' : 'none',
                    opacity: isDragging ? 0.5 : 1,
                    cursor: isEditing ? 'default' : 'grab',
                    transition: 'border-color 0.1s, background 0.1s',
                    userSelect: isEditing ? 'auto' : 'none',
                  }}>
                  {/* Drag handle */}
                  <span style={{ color: isEditing ? 'var(--border)' : 'var(--text-dim)', fontSize: 14, lineHeight: '20px', flexShrink: 0, cursor: isEditing ? 'default' : 'grab' }}>⠿</span>
                  {/* Priority badge */}
                  <span style={{
                    fontSize: 9, fontWeight: 700, fontFamily: 'var(--font-mono)',
                    padding: '2px 5px', borderRadius: 4, flexShrink: 0,
                    background: i === 0 ? 'color-mix(in oklch, var(--ai-500) 15%, transparent)' : 'var(--bg-sunken)',
                    color: i === 0 ? 'var(--ai-500)' : 'var(--text-dim)',
                    alignSelf: 'center',
                  }}>{i + 1}.°</span>
                  {/* Text or edit input */}
                  {isEditing ? (
                    <input
                      autoFocus
                      value={editVal}
                      onChange={e => setEditVal(e.target.value)}
                      onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); saveEdit(); } if (e.key === 'Escape') cancelEdit(); }}
                      onBlur={saveEdit}
                      style={{ flex: 1, fontSize: 13, color: 'var(--text)', lineHeight: 1.5, background: 'transparent', border: 'none', outline: 'none', fontFamily: 'var(--font-body)', padding: 0 }}
                    />
                  ) : (
                    <span
                      onClick={() => startEdit(i)}
                      title="Clica para editar"
                      style={{ flex: 1, fontSize: 13, color: 'var(--text)', lineHeight: 1.5, wordBreak: 'break-word', cursor: 'text' }}
                    >{item}</span>
                  )}
                  {/* Delete */}
                  {!isEditing && (
                    <button onClick={() => del(i)} style={{
                      background: 'none', border: 'none', padding: '0 2px',
                      color: 'var(--text-dim)', cursor: 'pointer', fontSize: 15, lineHeight: 1,
                      flexShrink: 0, alignSelf: 'center',
                    }}>×</button>
                  )}
                  {/* Save/cancel when editing */}
                  {isEditing && (
                    <div style={{ display: 'flex', gap: 4, flexShrink: 0, alignSelf: 'center' }}>
                      <button onMouseDown={e => { e.preventDefault(); saveEdit(); }} style={{ background: 'none', border: 'none', padding: '0 2px', color: 'var(--success, #16a34a)', cursor: 'pointer', fontSize: 13, lineHeight: 1 }}>✓</button>
                      <button onMouseDown={e => { e.preventDefault(); cancelEdit(); }} style={{ background: 'none', border: 'none', padding: '0 2px', color: 'var(--text-dim)', cursor: 'pointer', fontSize: 15, lineHeight: 1 }}>×</button>
                    </div>
                  )}
                </div>
              );
            })}
          </div>
        </>
      )}

      {!sortable && filled.length > 0 && (
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 8, alignItems: 'flex-start' }}>
          {filled.map((item, i) => {
            const isEditing = editIdx === i;
            return isEditing ? (
              <div key={i} style={{
                display: 'inline-flex', alignItems: 'center', gap: 4,
                padding: '3px 8px', borderRadius: 6,
                background: 'var(--bg-elev, #fff)',
                border: '1px solid var(--ai-500)',
                boxShadow: '0 0 0 3px color-mix(in oklch, var(--ai-500) 12%, transparent)',
                fontSize: 12,
              }}>
                <input
                  autoFocus
                  value={editVal}
                  onChange={e => setEditVal(e.target.value)}
                  onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); saveEdit(); } if (e.key === 'Escape') cancelEdit(); }}
                  onBlur={saveEdit}
                  style={{ fontSize: 12, color: 'var(--text)', background: 'transparent', border: 'none', outline: 'none', fontFamily: 'var(--font-body)', minWidth: 80, maxWidth: 220 }}
                />
                <button onMouseDown={e => { e.preventDefault(); saveEdit(); }} style={{ background: 'none', border: 'none', padding: '0 1px', color: 'var(--success, #16a34a)', cursor: 'pointer', fontSize: 12, lineHeight: 1 }}>✓</button>
                <button onMouseDown={e => { e.preventDefault(); cancelEdit(); }} style={{ background: 'none', border: 'none', padding: '0 1px', color: 'var(--text-dim)', cursor: 'pointer', fontSize: 13, lineHeight: 1 }}>×</button>
              </div>
            ) : (
              <div key={i} title="Clica para editar" style={{
                display: 'inline-flex', alignItems: 'center', gap: 5,
                padding: '4px 10px', borderRadius: 6,
                background: 'color-mix(in oklch, var(--ai-500) 8%, transparent)',
                border: '1px solid color-mix(in oklch, var(--ai-500) 20%, transparent)',
                fontSize: 12, color: 'var(--text)',
                maxWidth: 240, flexShrink: 0, cursor: 'pointer',
              }}
                onClick={() => startEdit(i)}
              >
                <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', minWidth: 0 }}>{item}</span>
                <button onClick={e => { e.stopPropagation(); del(i); }} style={{
                  background: 'none', border: 'none', padding: '0 0 0 2px',
                  color: 'var(--text-dim)', cursor: 'pointer', fontSize: 14, lineHeight: 1,
                  display: 'flex', alignItems: 'center', flexShrink: 0,
                }}>×</button>
              </div>
            );
          })}
          {maxItems && (
            <span style={{ marginLeft: 'auto', fontSize: 10, padding: '3px 7px', borderRadius: 99, background: 'var(--bg-sunken)', color: filled.length >= maxItems ? 'var(--danger, #CF2E2E)' : 'var(--text-dim)', fontFamily: 'var(--font-mono)', alignSelf: 'center', flexShrink: 0 }}>
              {filled.length}/{maxItems}
            </span>
          )}
        </div>
      )}
    </div>
  );
};

const ChannelToggle = ({ selected, onChange }) => (
  <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
    {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={{
          display: 'inline-flex', alignItems: 'center', gap: 5,
          padding: '6px 12px', borderRadius: 7, fontSize: 12, cursor: 'pointer',
          border: `1.5px solid ${active ? 'var(--ai-500)' : 'var(--border-strong, var(--border))'}`,
          background: active ? 'color-mix(in oklch, var(--ai-500) 10%, transparent)' : 'transparent',
          color: active ? 'var(--ai-500)' : 'var(--text-muted)',
          fontWeight: active ? 600 : 400,
          fontFamily: 'var(--font-body)',
          transition: 'border-color .15s, background .15s, color .15s',
        }}>
          {ch.label}
          {ch.hint && <span style={{ fontSize: 10, opacity: 0.65, fontWeight: 400 }}>{ch.hint}</span>}
        </button>
      );
    })}
  </div>
);

// ─── Mercados geográficos ──────────────────────────────────────────────────────

const BRAND_MARKETS_CONFIG = {
  digidelta: { tipo: 'fixo', opcoes: ['Portugal', 'Espanha'] },
  mimaki:    { tipo: 'fixo', opcoes: ['Portugal', 'Espanha'] },
  netscreen: { tipo: 'fixo', opcoes: ['Portugal', 'Espanha'] },
  sensek:    { tipo: 'fixo', opcoes: ['Portugal', 'Espanha'] },
  biond:     { tipo: 'geo' },
  decal:     { tipo: 'geo' },
  alldecor:  { tipo: 'geo' },
};

const GEO_HIERARCHY = {
  'Europa': [
    'Portugal','Espanha','França','Alemanha','Reino Unido','Itália','Países Baixos','Bélgica',
    'Luxemburgo','Suíça','Áustria','Suécia','Noruega','Dinamarca','Finlândia','Irlanda',
    'Polónia','República Checa','Eslováquia','Hungria','Roménia','Bulgária','Grécia',
    'Croácia','Sérvia','Eslovénia','Lituânia','Letónia','Estónia','Ucrânia','Rússia',
    'Bielorrússia','Moldávia','Albânia','Macedónia do Norte','Bósnia-Herzegovina','Montenegro',
    'Kosovo','Malta','Chipre','Islândia','Liechtenstein','Mónaco','Andorra','San Marino',
  ],
  'Américas': [
    'EUA','Canadá','México','Brasil','Argentina','Chile','Colômbia','Peru','Venezuela',
    'Equador','Bolívia','Paraguai','Uruguai','Costa Rica','Panamá','Guatemala','Honduras',
    'El Salvador','Nicarágua','Cuba','República Dominicana','Jamaica','Haiti','Porto Rico',
    'Trinidad e Tobago','Belize','Guiana','Suriname',
  ],
  'Ásia-Pacífico': [
    'China','Japão','Coreia do Sul','Índia','Austrália','Nova Zelândia','Singapura',
    'Indonésia','Malásia','Tailândia','Vietnam','Filipinas','Taiwan','Hong Kong','Macau',
    'Bangladesh','Paquistão','Sri Lanka','Myanmar','Camboja','Laos','Mongólia',
    'Nepal','Cazaquistão','Uzbequistão','Brunei','Timor-Leste',
  ],
  'Médio Oriente': [
    'Emirados Árabes Unidos','Arábia Saudita','Qatar','Kuwait','Omã','Bahrein',
    'Israel','Jordânia','Líbano','Turquia','Irão','Iraque','Síria','Iémen','Palestina',
    'Afeganistão',
  ],
  'África': [
    'África do Sul','Nigéria','Quénia','Gana','Etiópia','Tanzânia','Uganda','Ruanda',
    'Marrocos','Tunísia','Argélia','Líbia','Egipto','Angola','Moçambique','Zimbabué',
    'Zâmbia','Senegal','Costa do Marfim','Camarões','Madagáscar','Namíbia','Botswana',
    'Mali','Serra Leoa','Guiné','Cabo Verde','Ilhas Maurícias',
  ],
  'Oceânia': [
    'Austrália','Nova Zelândia','Papua Nova Guiné','Fiji','Ilhas Salomão','Vanuatu',
    'Samoa','Tonga','Micronésia',
  ],
};

const MarketToggle = ({ opcoes, value, onChange }) => (
  <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
    {opcoes.map(m => {
      const active = (value || []).includes(m);
      return (
        <button key={m} onClick={() => onChange(active ? (value||[]).filter(x=>x!==m) : [...(value||[]),m])} style={{
          padding: '6px 14px', borderRadius: 7, fontSize: 12, cursor: 'pointer',
          border: `1.5px solid ${active ? 'var(--ai-500)' : 'var(--border-strong, var(--border))'}`,
          background: active ? 'color-mix(in oklch, var(--ai-500) 10%, transparent)' : 'transparent',
          color: active ? 'var(--ai-500)' : 'var(--text-muted)',
          fontWeight: active ? 700 : 400, fontFamily: 'var(--font-body)',
        }}>{m}</button>
      );
    })}
  </div>
);

const GeoMarketSelect = ({ value, onChange }) => {
  const [openCont, setOpenCont] = React.useState(null);
  const selected = value || [];
  const countInContinent = (cont) => GEO_HIERARCHY[cont].filter(p => selected.includes(p)).length;
  const toggleContinent = (cont) => {
    const countries = GEO_HIERARCHY[cont];
    const allSelected = countries.every(p => selected.includes(p));
    if (allSelected) onChange(selected.filter(p => !countries.includes(p)));
    else onChange([...new Set([...selected, ...countries])]);
  };
  const toggleCountry = (country) => {
    onChange(selected.includes(country) ? selected.filter(p => p !== country) : [...selected, country]);
  };
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
      <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
        {Object.keys(GEO_HIERARCHY).map(cont => {
          const cnt = countInContinent(cont);
          const isOpen = openCont === cont;
          return (
            <button key={cont} onClick={() => setOpenCont(isOpen ? null : cont)} style={{
              padding: '5px 12px', borderRadius: 7, fontSize: 12, cursor: 'pointer',
              border: `1.5px solid ${cnt > 0 ? 'var(--ai-500)' : 'var(--border-strong, var(--border))'}`,
              background: isOpen ? 'color-mix(in oklch, var(--ai-500) 15%, transparent)' : cnt > 0 ? 'color-mix(in oklch, var(--ai-500) 6%, transparent)' : 'transparent',
              color: cnt > 0 ? 'var(--ai-500)' : 'var(--text-muted)',
              fontWeight: cnt > 0 ? 700 : 400, fontFamily: 'var(--font-body)',
              display: 'flex', alignItems: 'center', gap: 5,
            }}>
              {cont}
              {cnt > 0 && <span style={{ fontSize: 10, background: 'var(--ai-500)', color: '#fff', borderRadius: 99, padding: '0 5px', fontWeight: 700 }}>{cnt}</span>}
            </button>
          );
        })}
      </div>
      {openCont && (
        <div style={{ background: 'var(--bg-sunken, #f1f5f9)', borderRadius: 8, padding: '12px 14px' }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
            <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>{openCont}</span>
            <button onClick={() => toggleContinent(openCont)} style={{ fontSize: 10, color: 'var(--ai-500)', background: 'none', border: 'none', cursor: 'pointer', fontFamily: 'var(--font-body)', fontWeight: 600 }}>
              {GEO_HIERARCHY[openCont].every(p => selected.includes(p)) ? 'Desseleccionar todos' : 'Seleccionar todos'}
            </button>
          </div>
          <div style={{ display: 'flex', gap: 5, flexWrap: 'wrap' }}>
            {GEO_HIERARCHY[openCont].map(country => {
              const active = selected.includes(country);
              return (
                <button key={country} onClick={() => toggleCountry(country)} style={{
                  padding: '4px 10px', borderRadius: 6, fontSize: 11, cursor: 'pointer',
                  border: `1.5px solid ${active ? 'var(--ai-500)' : 'var(--border)'}`,
                  background: active ? 'color-mix(in oklch, var(--ai-500) 10%, transparent)' : 'var(--bg-elev, #fff)',
                  color: active ? 'var(--ai-500)' : 'var(--text-muted)',
                  fontWeight: active ? 700 : 400, fontFamily: 'var(--font-body)',
                }}>{country}</button>
              );
            })}
          </div>
        </div>
      )}
      {selected.length > 0 && (
        <div style={{ fontSize: 11, color: 'var(--text-muted)', fontFamily: 'var(--font-body)' }}>
          {selected.length} mercado{selected.length !== 1 ? 's' : ''} seleccionado{selected.length !== 1 ? 's' : ''}:
          <span style={{ marginLeft: 4, color: 'var(--ai-500)', fontWeight: 600 }}>{selected.slice(0,5).join(', ')}{selected.length > 5 ? ` +${selected.length - 5}` : ''}</span>
        </div>
      )}
    </div>
  );
};

// Normalização de nomes de países (importação pode trazer EN/ES → PT)
const COUNTRY_NORMALIZE = {
  'spain':'Espanha','españa':'Espanha','espagne':'Espanha',
  'france':'França','frankreich':'França',
  'germany':'Alemanha','deutschland':'Alemanha','allemagne':'Alemanha',
  'portugal':'Portugal',
  'united kingdom':'Reino Unido','uk':'Reino Unido','england':'Reino Unido','great britain':'Reino Unido',
  'italy':'Itália','italia':'Itália','italie':'Itália',
  'netherlands':'Países Baixos','holland':'Países Baixos','holanda':'Países Baixos',
  'belgium':'Bélgica','belgique':'Bélgica','bélgica':'Bélgica',
  'switzerland':'Suíça','suiza':'Suíça','schweiz':'Suíça',
  'austria':'Áustria','autriche':'Áustria',
  'sweden':'Suécia','suède':'Suécia','suecia':'Suécia',
  'norway':'Noruega','norvège':'Noruega','noruega':'Noruega',
  'denmark':'Dinamarca','danemark':'Dinamarca',
  'finland':'Finlândia','finlande':'Finlândia',
  'ireland':'Irlanda','irlande':'Irlanda',
  'poland':'Polónia','pologne':'Polónia','polonia':'Polónia',
  'brazil':'Brasil','brésil':'Brasil',
  'usa':'EUA','united states':'EUA','us':'EUA','états-unis':'EUA','estados unidos':'EUA',
  'canada':'Canadá','canada':'Canadá',
  'mexico':'México','mexique':'México',
  'argentina':'Argentina','chile':'Chile','colombia':'Colômbia','peru':'Peru',
  'china':'China','japan':'Japão','japon':'Japão','japón':'Japão',
  'south korea':'Coreia do Sul','korea':'Coreia do Sul','corée du sud':'Coreia do Sul',
  'india':'Índia','inde':'Índia',
  'australia':'Austrália','australie':'Austrália',
  'new zealand':'Nova Zelândia','nouvelle-zélande':'Nova Zelândia',
  'singapore':'Singapura','singapour':'Singapura',
  'turkey':'Turquia','turquie':'Turquia','turquía':'Turquia',
  'uae':'Emirados Árabes Unidos','united arab emirates':'Emirados Árabes Unidos',
  'saudi arabia':'Arábia Saudita','arabie saoudite':'Arábia Saudita',
  'south africa':'África do Sul','afrique du sud':'África do Sul',
  'russia':'Rússia','russie':'Rússia','rusia':'Rússia',
  'ukraine':'Ucrânia','ukraine':'Ucrânia',
  'greece':'Grécia','grèce':'Grécia','grecia':'Grécia',
  'croatia':'Croácia','croatie':'Croácia','croacia':'Croácia',
  'czech republic':'República Checa','czechia':'República Checa',
  'hungary':'Hungria','hongrie':'Hungria',
  'romania':'Roménia','roumanie':'Roménia','rumania':'Roménia',
  'bulgaria':'Bulgária','bulgarie':'Bulgária',
  'slovakia':'Eslováquia','slovaquie':'Eslováquia',
  'slovenia':'Eslovénia','slovénie':'Eslovénia',
  'luxembourg':'Luxemburgo','luxemburg':'Luxemburgo',
  'benelux':'Países Baixos',
};

const normalizeCountry = (name) => {
  if (!name) return name;
  // Strip parenthetical info: "Turquia (DB Reklam...)" → "Turquia"
  const stripped = name.replace(/\s*\(.*?\)\s*/g, '').trim();
  const key = stripped.toLowerCase().trim();
  return COUNTRY_NORMALIZE[key] || stripped;
};

// Mapa de regiões → países individuais (para marcas geo)
const REGION_EXPAND = {
  'países nórdicos': ['Suécia','Noruega','Dinamarca','Finlândia'],
  'nórdicos': ['Suécia','Noruega','Dinamarca','Finlândia'],
  'nordic': ['Suécia','Noruega','Dinamarca','Finlândia'],
  'nordics': ['Suécia','Noruega','Dinamarca','Finlândia'],
  'benelux': ['Bélgica','Países Baixos','Luxemburgo'],
  'ibéria': ['Portugal','Espanha'],
  'ibérico': ['Portugal','Espanha'],
  'iberian peninsula': ['Portugal','Espanha'],
  'península ibérica': ['Portugal','Espanha'],
  'dach': ['Alemanha','Áustria','Suíça'],
  'mena': ['Emirados Árabes Unidos','Arábia Saudita','Qatar','Kuwait','Omã','Bahrein','Israel','Jordânia','Líbano','Turquia','Marrocos','Tunísia','Argélia','Egipto'],
  'apac': ['China','Japão','Coreia do Sul','Índia','Austrália','Nova Zelândia','Singapura','Indonésia','Malásia','Tailândia','Vietnam'],
  'latam': ['Brasil','Argentina','Chile','Colômbia','Peru','México'],
};

const expandRegion = (name) => {
  const key = name.toLowerCase().trim();
  return REGION_EXPAND[key] || null;
};

const GeoMarketField = ({ brand, value, onChange }) => {
  const cfg = BRAND_MARKETS_CONFIG[brand];
  const allGeoFlat = React.useMemo(() => Object.values(GEO_HIERARCHY).flat(), []);

  const mapped = React.useMemo(() => {
    const raw = (value || []).map(normalizeCountry).filter(Boolean);
    if (!cfg) return raw;

    if (cfg.tipo === 'fixo') {
      // Correspondência directa: manter só os que estão na lista
      const direct = raw.filter(v => cfg.opcoes.includes(v));
      if (direct.length > 0) return direct;
      // Se a extracção encontrou conteúdo geo mas nenhum corresponde → auto-seleccionar todos os mercados da marca
      if (raw.length > 0) return [...cfg.opcoes];
      return [];
    }

    if (cfg.tipo === 'geo') {
      const result = [];
      for (const v of raw) {
        // Expandir regiões → países individuais
        const expanded = expandRegion(v);
        if (expanded) { expanded.forEach(c => result.includes(c) || result.push(c)); continue; }
        // Aceitar países directos que existam no GEO_HIERARCHY
        if (allGeoFlat.includes(v)) { result.includes(v) || result.push(v); }
      }
      return result;
    }

    return raw;
  }, [value, brand]);

  // Sincronizar com o form state quando o mapeamento alterar os valores
  React.useEffect(() => {
    if (!brand) return;
    const orig = value || [];
    if (mapped.length !== orig.length || mapped.some((v, i) => v !== orig[i])) {
      onChange(mapped);
    }
  }, [mapped]);

  if (!brand) return (
    <div style={{ fontSize: 12, color: 'var(--text-dim)', fontStyle: 'italic', padding: '6px 0' }}>
      Selecciona a marca primeiro
    </div>
  );
  if (cfg?.tipo === 'fixo') return <MarketToggle opcoes={cfg.opcoes} value={mapped} onChange={onChange} />;
  if (cfg?.tipo === 'geo')  return <GeoMarketSelect value={mapped} onChange={onChange} />;
  return <ChipInput items={mapped} onChange={onChange} placeholder="ex: Portugal, Espanha — prima Enter" />;
};

// ─── 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>
    <FormCard title="Identificação">
      <div style={{ display: 'grid', gridTemplateColumns: isAll ? (hasSeg ? '1fr 1fr 1fr 1fr' : '1fr 1fr 1fr') : (hasSeg ? '1fr 1fr 1fr' : '1fr 1fr'), gap: 18, alignItems: 'start' }}>
        {isAll && (
          <Field label="Marca">
            <TSelect value={selectedBrand} onChange={onBrandChange}
              options={(allBrands || []).map(b => ({ value: b.slug, label: b.name }))}
              placeholder="Seleccionar marca..." />
          </Field>
        )}
        {hasSeg && (
          <Field label="Categoria">
            <TSelect value={selectedSegment} onChange={onSegmentChange}
              options={segments.map(s => ({ value: s, label: s }))}
              placeholder={noMarca ? 'Selecciona a marca primeiro' : 'Seleccionar...'} />
          </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 primeiro a categoria' : '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>
    </FormCard>

    <FormCard title="Descrição">
      <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>
    </FormCard>

    <FormCard title="Características">
      <Field label="USPs — Pontos de valor únicos" hint="Ordena do mais importante para o menos — o 1.° tem maior peso na geração" required>
        <ChipInput items={data.usps} onChange={v => upd('usps', v)}
          placeholder={ph('usps', selectedBrand)} sortable />
      </Field>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18, alignItems: 'start' }}>
        <Field label="Certificações técnicas" hint="Ordena por relevância">
          <ChipInput items={data.certifications} onChange={v => upd('certifications', v)}
            placeholder={ph('certifications', selectedBrand)} sortable />
        </Field>
        <Field label="Aplicações primárias" hint="A 1.ª é o caso de uso principal">
          <ChipInput items={data.applications} onChange={v => upd('applications', v)}
            placeholder={ph('applications', selectedBrand)} sortable />
        </Field>
      </div>
    </FormCard>

    <FormCard title="Presença">
      <Field label="Mercados geográficos alvo" required>
        <GeoMarketField brand={selectedBrand} value={data.geo_markets || []} onChange={v => upd('geo_markets', v)} />
      </Field>
    </FormCard>
  </div>
  );
};

// ── Configuração de mercados por marca ────────────────────────────────────────
const Block2 = ({ data, upd, brand }) => (
  <div>
    <FormCard title="Perfil">
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18, alignItems: 'start' }}>
        <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>
      </div>
    </FormCard>

    <FormCard title="Psicografia">
      <Field label="Dores principais que o produto resolve" hint="A 1.ª dor é o gatilho dominante nos hooks" required>
        <ChipInput items={data.pain_points} onChange={v => upd('pain_points', v)}
          placeholder={ph('pain_points', brand)} sortable />
      </Field>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18, alignItems: 'start' }}>
        <Field label="Motivadores de compra" hint="O 1.° é o ângulo de desejo principal">
          <ChipInput items={data.motivators} onChange={v => upd('motivators', v)}
            placeholder={ph('motivators', brand)} sortable />
        </Field>
        <Field label="Objecções comuns" hint="A 1.ª é a mais crítica a antecipar">
          <ChipInput items={data.objections} onChange={v => upd('objections', v)}
            placeholder={ph('objections', brand)} sortable />
        </Field>
      </div>
    </FormCard>

    <FormCard title="Decisão">
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18, alignItems: 'start' }}>
        <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>
      </div>
    </FormCard>
  </div>
);

const Block3 = ({ data, upd, brand }) => (
  <div>
    <FormCard title="Panorama competitivo">
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18, alignItems: 'start' }}>
        <Field label="Concorrentes directos" required>
          <ChipInput items={data.competitors} onChange={v => upd('competitors', v)}
            placeholder={ph('competitors', brand)} sortable />
        </Field>
        <Field label="Argumentos de diferenciação" hint="O 1.° é o diferenciador mais forte" required>
          <ChipInput items={data.differentiation_args} onChange={v => upd('differentiation_args', v)}
            placeholder={ph('differentiation_args', brand)} sortable />
        </Field>
      </div>
    </FormCard>

    <FormCard title="Posicionamento">
      <Field label="Tendências de mercado relevantes">
        <ChipInput items={data.market_trends} onChange={v => upd('market_trends', v)}
          placeholder={ph('market_trends', brand)} sortable />
      </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>
    </FormCard>

    <FormCard title="Notas internas">
      <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>
    </FormCard>
  </div>
);

const CommercialOfferBlock = ({ data, upd, brand }) => {
  const [solutions, setSolutions] = React.useState({});
  React.useEffect(() => {
    if (!brand) return;
    fetch(`/api/marketing/brand-solutions?marca_slug=${encodeURIComponent(brand)}`)
      .then(r => r.ok ? r.json() : {})
      .then(d => setSolutions(d || {}))
      .catch(() => {});
  }, [brand]);

  const offer = data.commercial_offer || null;
  const updOffer = (field, val) => upd('commercial_offer', { ...(offer || {}), [field]: val });
  const availableMechanics = Object.keys(solutions);
  const tiers = offer?.type ? (solutions[offer.type] || []) : [];

  const MECHANIC_LABELS = { digirent: 'Digirent', printplan: 'PrintPlan', voucher: 'Voucher on Demand', direct_discount: 'Desconto Directo', trade_in: 'Trade-In' };
  const CTA_OPTS = [
    { value: 'quote_request',  label: 'Pedido de proposta' },
    { value: 'demo_request',   label: 'Pedido de demonstração' },
    { value: 'visit_showroom', label: 'Visita ao showroom' },
    { value: 'whatsapp',       label: 'WhatsApp' },
    { value: 'form',           label: 'Formulário' },
    { value: 'call',          label: 'Chamada directa' },
    { value: 'landing',       label: 'Landing page' },
    { value: 'none',          label: 'Sem CTA específico' },
  ];

  if (availableMechanics.length === 0) return null;

  return (
    <FormCard title="Oferta Comercial">
      <div style={{ marginBottom: 0 }}>
        <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text)', marginBottom: 8 }}>Mecânica</div>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
          <button onClick={() => upd('commercial_offer', null)} style={{ padding: '5px 12px', borderRadius: 6, fontSize: 12, fontWeight: 600, cursor: 'pointer', background: !offer ? 'var(--ai-500)' : 'var(--bg-sunken)', color: !offer ? '#fff' : 'var(--text-muted)', border: !offer ? 'none' : '1px solid var(--border)', fontFamily: 'var(--font-body)' }}>
            Sem oferta específica
          </button>
          {availableMechanics.map(type => (
            <button key={type} onClick={() => upd('commercial_offer', { type, tier: null, details: '', primary_cta: '', negotiable: false })} style={{ padding: '5px 12px', borderRadius: 6, fontSize: 12, fontWeight: 600, cursor: 'pointer', background: offer?.type === type ? 'var(--ai-500)' : 'var(--bg-sunken)', color: offer?.type === type ? '#fff' : 'var(--text)', border: offer?.type === type ? 'none' : '1px solid var(--border)', fontFamily: 'var(--font-body)' }}>
              {MECHANIC_LABELS[type] || type}
            </button>
          ))}
        </div>
      </div>

      {offer?.type && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14, padding: 16, background: 'var(--bg, #f8fafc)', borderRadius: 8, border: '1px solid var(--border)' }}>
          {tiers.length > 0 && (
            <div>
              <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--text-muted)', marginBottom: 6, textTransform: 'uppercase', letterSpacing: '0.06em', fontFamily: 'var(--font-mono)' }}>Tier *</div>
              <TSelect value={offer.tier || ''} onChange={v => updOffer('tier', v)}
                options={[{ value: '', label: 'Selecciona o tier…' }, ...tiers.map(t => ({ value: t.tier, label: t.label }))]} />
              {offer.tier && tiers.find(t => t.tier === offer.tier)?.description && (
                <div style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 5, lineHeight: 1.55, fontFamily: 'var(--font-body)' }}>
                  {tiers.find(t => t.tier === offer.tier).description}
                </div>
              )}
            </div>
          )}
          <div>
            <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--text-muted)', marginBottom: 6, textTransform: 'uppercase', letterSpacing: '0.06em', fontFamily: 'var(--font-mono)' }}>Detalhes da oferta</div>
            <TInput value={offer.details || ''} onChange={v => updOffer('details', v)} placeholder="ex: 36 meses, €1.250/mês, entrada 20%" multiline />
          </div>
          <div>
            <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--text-muted)', marginBottom: 6, textTransform: 'uppercase', letterSpacing: '0.06em', fontFamily: 'var(--font-mono)' }}>CTA principal</div>
            <TSelect value={offer.primary_cta || ''} onChange={v => updOffer('primary_cta', v)}
              options={[{ value: '', label: 'Selecciona o CTA…' }, ...CTA_OPTS]} />
          </div>
          <label style={{ display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', userSelect: 'none' }}>
            <input type="checkbox" checked={!!offer.negotiable} onChange={e => updOffer('negotiable', e.target.checked)} />
            <span style={{ fontSize: 12, color: 'var(--text)', fontFamily: 'var(--font-body)' }}>Tier negociável <span style={{ color: 'var(--text-muted)', fontStyle: 'italic' }}>(indicativo — o comercial fecha as condições)</span></span>
          </label>
        </div>
      )}
    </FormCard>
  );
};

// ─── KpiChannelInput ───────────────────────────────────────────────────────────
const KPIS_CANAL_LABELS = {
  meta_ads: 'Meta Ads', linkedin_ads: 'LinkedIn Ads', google_ads_search: 'Google Search',
  google_ads_display: 'Google Display', email: 'Email', whatsapp: 'WhatsApp',
  website: 'Website', muppi_led: 'Painéis LED',
};
const KPIS_CANAL_ABBR = {
  meta_ads: 'META', linkedin_ads: 'LIN', google_ads_search: 'GGL',
  google_ads_display: 'DISP', email: 'EML', whatsapp: 'WA',
  website: 'WEB', muppi_led: 'LED',
};

const KpiChannelInput = ({ items, onChange, selectedChannels }) => {
  const safeItems    = Array.isArray(items) ? items : [];
  const safeChanList = Array.isArray(selectedChannels) && selectedChannels.length > 0
    ? selectedChannels : Object.keys(KPIS_CANAL_LABELS);

  const [editCanal, setEditCanal] = React.useState('');
  const [editKpi,   setEditKpi]   = React.useState('');

  // Normaliza: string legado → { canal:'', kpi: string }
  const normalized = safeItems.map(function(k) {
    return typeof k === 'string' ? { canal: '', kpi: k } : (k && k.kpi ? k : null);
  }).filter(Boolean);

  // Canal efectivo para o select (reset se não está na lista)
  const activeCanal = safeChanList.includes(editCanal) ? editCanal : (safeChanList[0] || '');

  function add() {
    if (!editKpi.trim()) return;
    onChange(normalized.concat([{ canal: activeCanal, kpi: editKpi.trim() }]));
    setEditKpi('');
  }

  function remove(i) { onChange(normalized.filter(function(_, idx) { return idx !== i; })); }

  var st = { background:'var(--bg-elev,#fff)', border:'1px solid var(--border-strong,var(--border))', borderRadius:6, fontSize:12, color:'var(--text)', padding:'7px 10px', fontFamily:'inherit', outline:'none', boxSizing:'border-box' };

  return (
    React.createElement('div', null,
      React.createElement('div', { style: { display:'flex', gap:6, marginBottom: normalized.length > 0 ? 8 : 0 } },
        React.createElement('select', { value: activeCanal, onChange: function(e) { setEditCanal(e.target.value); }, style: Object.assign({}, st, { width:148, flexShrink:0 }) },
          React.createElement('option', { value: '' }, 'Canal...'),
          safeChanList.map(function(c) { return React.createElement('option', { key: c, value: c }, KPIS_CANAL_LABELS[c] || c); })
        ),
        React.createElement('input', { value: editKpi, onChange: function(e) { setEditKpi(e.target.value); }, onKeyDown: function(e) { if (e.key === 'Enter') { e.preventDefault(); add(); } }, placeholder: 'ex: CPL < €45/lead', style: Object.assign({}, st, { flex:1 }) }),
        React.createElement('button', { onClick: add, className: 'btn', style: { flexShrink:0, height:34, padding:'0 12px', fontSize:12 } }, '+')
      ),
      normalized.length > 0 && React.createElement('div', { style: { display:'flex', flexWrap:'wrap', gap:6, marginTop:6 } },
        normalized.map(function(item, i) {
          var abbr = item.canal ? (KPIS_CANAL_ABBR[item.canal] || item.canal.slice(0,3).toUpperCase()) : null;
          var isOrphan = item.canal && safeChanList.length > 0 && !safeChanList.includes(item.canal);
          return React.createElement('span', { key: i, style: { display:'inline-flex', alignItems:'center', gap:5, background: isOrphan ? 'color-mix(in srgb,#f59e0b 8%,transparent)' : 'var(--bg-sunken)', border: isOrphan ? '1px solid color-mix(in srgb,#f59e0b 40%,transparent)' : '1px solid var(--border)', borderRadius:5, padding:'3px 8px 3px 5px', fontSize:12 }, title: isOrphan ? 'Canal removido — este KPI ficou órfão' : undefined },
            abbr ? React.createElement('span', { style: { fontSize:9, fontWeight:700, background: isOrphan ? '#f59e0b' : 'var(--ai-500,#3859D0)', color:'#fff', borderRadius:3, padding:'1px 4px', fontFamily:'var(--font-mono)', flexShrink:0 } }, abbr) : null,
            React.createElement('span', { style: { color: isOrphan ? '#92400e' : 'var(--text)' } }, item.kpi),
            isOrphan && React.createElement('span', { style: { fontSize:9, color:'#92400e', fontFamily:'var(--font-mono)', marginLeft:2 } }, '!'),
            React.createElement('button', { onClick: function() { remove(i); }, style: { background:'none', border:'none', cursor:'pointer', color:'var(--text-dim)', padding:'0 0 0 2px', lineHeight:1, fontSize:14 } }, '×')
          );
        })
      ),
      React.createElement('div', { style: { fontSize:11, color:'var(--text-dim)', marginTop:4 } }, 'Um KPI por canal seleccionado')
    )
  );
};

const Block4 = ({ data, upd, brand, aiSuggested, onSuggestBudget, suggestingBudget, budgetSuggested, budgetRationale, budgetError }) => (
  <div>
    {/* Banner apenas para sugestão via Objectivos — o banner global já cobre o caso aiSuggested */}
    {budgetSuggested && (
      <div style={{ marginBottom: 14, padding: '10px 14px', borderRadius: 8, fontSize: 11, display: 'flex', alignItems: 'flex-start', gap: 8,
        background: 'color-mix(in oklch, var(--success) 8%, transparent)',
        border: '1px solid color-mix(in oklch, var(--success) 25%, transparent)',
        color: 'var(--success)',
      }}>
        <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0, marginTop: 1 }}>
          <polyline points="20 6 9 17 4 12"/>
        </svg>
        <div>
          <span style={{ fontWeight: 500 }}>Campanha sugerida com base nos Objectivos — revê e confirma.</span>
          {budgetRationale && (
            <div style={{ marginTop: 4, color: 'var(--text-muted)', fontWeight: 400, lineHeight: 1.5 }}>{budgetRationale}</div>
          )}
        </div>
      </div>
    )}
    {budgetError && (
      <div style={{ marginBottom: 14, 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)',
        display: 'flex', alignItems: 'flex-start', gap: 6 }}>
        <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" style={{ flexShrink: 0, marginTop: 1 }}>
          <path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/>
        </svg>
        <span>{budgetError}</span>
      </div>
    )}

    <FormCard title={
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <span>Estratégia</span>
        {onSuggestBudget && (
          <button onClick={onSuggestBudget} disabled={suggestingBudget}
            style={{ background: 'none', border: 'none', cursor: suggestingBudget ? 'default' : 'pointer', padding: 0, display: 'inline-flex', alignItems: 'center', gap: 5, color: suggestingBudget ? 'var(--text-dim)' : 'var(--ai-500)', fontSize: 10, fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', fontFamily: 'var(--font-mono)' }}>
            {suggestingBudget
              ? <><div style={{ width: 10, height: 10, borderRadius: '50%', border: '1.5px solid var(--text-dim)', borderTopColor: 'var(--ai-500)', animation: 'spin 0.8s linear infinite' }} />A sugerir...</>
              : <><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>{budgetSuggested ? 'Re-sugerir' : 'Sugerir com AI'}</>
            }
          </button>
        )}
      </div>
    }>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18, alignItems: 'start' }}>
        <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 que definem o sucesso">
          <KpiChannelInput items={Array.isArray(data.kpis) ? data.kpis : []} onChange={v => upd('kpis', v)} selectedChannels={Array.isArray(data.channels) ? data.channels : []} />
        </Field>
      </div>
      <Field label="Canais prioritários" required>
        <ChannelToggle selected={data.channels} onChange={v => upd('channels', v)} />
      </Field>
      {(data.channels || []).includes('meta_ads') && (
        <Field label="Meta Ads — Tipo de conversão" hint="Define como o utilizador converte — vem do briefing para o conceito">
          <select value={data.meta_ads_conversao || ''} onChange={e => upd('meta_ads_conversao', e.target.value)}
            style={{ width: '100%', padding: '9px 12px', borderRadius: 8, fontSize: 13, background: 'var(--bg-app,#f8fafc)', border: '1.5px solid var(--border,#dde3ef)', color: data.meta_ads_conversao ? 'var(--text,#1d2e38)' : 'var(--text-dim,#94a3b8)', appearance: 'none' }}>
            <option value="">Seleccionar tipo de conversão…</option>
            <option value="formulario_nativo">Formulário nativo Meta (Lead Ads)</option>
            <option value="landing_page">Landing page de pedido de proposta</option>
          </select>
        </Field>
      )}
    </FormCard>

    <FormCard title="Identidade">
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 18, alignItems: 'start' }}>
        <Field label="Tom e linguagem" required>
          <TInput value={data.tone} onChange={v => upd('tone', v)} placeholder={ph('tone', brand)} />
        </Field>
        <Field label="Início">
          <TDateInput value={data.timeline_start} onChange={v => upd('timeline_start', v)} />
        </Field>
        <Field label="Fim">
          <TDateInput value={data.timeline_end} onChange={v => upd('timeline_end', v)} />
        </Field>
      </div>
      <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>
    </FormCard>

    <FormCard title="Histórico">
      <Field label="Campanhas anteriores relevantes">
        <ChipInput items={data.previous_campaigns} onChange={v => upd('previous_campaigns', v)}
          placeholder={ph('previous_campaigns', brand)} sortable />
      </Field>
    </FormCard>

    <CommercialOfferBlock data={data} upd={upd} brand={brand} />
  </div>
);

const toArr = v => Array.isArray(v) ? v : (v ? [String(v)] : []);
const Block5 = ({ data, upd, brand, aiSuggested }) => (
  <div>
    {/* Banner coberto pelo banner global do form — omitido aqui para evitar duplicação */}
    <FormCard title="Limites de conteúdo">
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18, alignItems: 'start' }}>
        <Field label="Claims proibidos" hint="O 1.° é o mais crítico a evitar">
          <ChipInput items={toArr(data.prohibited_claims)} onChange={v => upd('prohibited_claims', v)}
            placeholder={ph('prohibited_claims', brand)} sortable />
        </Field>
        <Field label="Mensagens fora do posicionamento">
          <ChipInput items={toArr(data.off_brand_messages)} onChange={v => upd('off_brand_messages', v)}
            placeholder={ph('off_brand_messages', brand)} sortable />
        </Field>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18, alignItems: 'start' }}>
        <Field label="Concorrentes a não nomear">
          <ChipInput items={toArr(data.competitors_not_name)} onChange={v => upd('competitors_not_name', v)}
            placeholder={ph('competitors_not_name', brand)} sortable />
        </Field>
        <Field label="Mercados / segmentos fora de âmbito">
          <ChipInput items={toArr(data.out_of_scope_markets)} onChange={v => upd('out_of_scope_markets', v)}
            placeholder={ph('out_of_scope_markets', brand)} sortable />
        </Field>
      </div>
      <Field label="Outras restrições">
        <ChipInput items={toArr(data.other_restrictions)} onChange={v => upd('other_restrictions', v)}
          placeholder={ph('other_restrictions', brand)} sortable />
      </Field>
    </FormCard>
  </div>
);

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


const Block6 = ({ briefingId, apreciation: initialApreciation, userRole, calcProgressValue, onUpdate, importOrigin, previewData, form, brandName, onApplyImprovement, onNavigateToBlock }) => {
  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 [applyState, setApplyState]             = React.useState({});
  const [expandedBlock, setExpandedBlock]       = React.useState(null);
  const [expandedField, setExpandedField]       = React.useState(null);
  const [improvementsApplied, setImprovementsApplied] = React.useState(false);
  const [scoreGain, setScoreGain]               = React.useState(0);
  const [improvedFields, setImprovedFields]     = React.useState(() => new Set(initialApreciation?.improved_fields || [])); // campos melhorados (persiste na BD) // pontos ganhos pelas melhorias aplicadas

  // Pesos dos campos — carregados do servidor (fonte única de verdade)
  const [FIELD_WEIGHTS, setFieldWeights] = React.useState({
    b1: { blockW: 0.20, fields: { elevator_pitch: 0.25, usps: 0.45, certifications: 0.20 } },
    b2: { blockW: 0.45, fields: { pain_points: 0.28, motivators: 0.11, objections: 0.07, purchase_trigger: 0.05, decision_maker: 0.27, end_user: 0.22 } },
    b3: { blockW: 0.10, fields: { competitors: 0.20, market_trends: 0.10, positioning_narrative: 0.35, differentiation_args: 0.35 } },
    b4: { blockW: 0.25, fields: { key_message: 0.35, objective: 0.30, tone: 0.20, kpis: 0.15 } },
    b5: { blockW: 0,    fields: {} },
  });
  React.useEffect(() => {
    fetch('/api/marketing/briefings/field-weights')
      .then(r => r.ok ? r.json() : null)
      .then(d => { if (d) setFieldWeights(d); })
      .catch(() => {});
  }, []);

  const estimateGain = (bKey, fieldName, currentFieldScore, numCriteriaFixed) => {
    const fw = FIELD_WEIGHTS[bKey];
    if (!fw) return 0;
    const fieldW = fw.fields[fieldName] || 0.25;
    const estimatedNewScore = Math.min(95, currentFieldScore + numCriteriaFixed * 18);
    return Math.max(1, Math.round((estimatedNewScore - currentFieldScore) / 100 * fieldW * fw.blockW * 100));
  };
  const stepStartRef  = React.useRef(null);
  const lastStepAtRef = React.useRef(0);
  const [stepPct, setStepPct] = React.useState(0);

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

  const scoreColor = (s) => s >= 80 ? 'var(--ai-500, #3859D0)' : s >= 60 ? 'var(--text-muted, #4a5e7a)' : 'var(--text-dim, #6b7fa3)';
  const scoreLabel = (s) => s >= 90 ? 'Excelente' : s >= 70 ? 'Bom' : s >= 40 ? 'Suficiente' : 'Insuficiente';

  const processLines = async (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') {
        const wait = 500 - (Date.now() - lastStepAtRef.current);
        if (wait > 0 && lastStepAtRef.current > 0) await new Promise(r => setTimeout(r, wait));
        setCurrentStep(evt.step); setMessages(m => [...m, evt.message]);
        lastStepAtRef.current = Date.now();
      }
      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('');
    setImprovementsApplied(false);
    setScoreGain(0);
    lastStepAtRef.current = 0;
    try {
      // Sempre usa o endpoint inline (aceita dados directamente, sem precisar de briefing guardado)
      const inlineData = previewData || (form ? {
        block1: form.block1, block2: form.block2, block3: form.block3,
        block4: form.block4, block5: form.block5,
        brand_name: brandName || '',
      } : null);

      const res = await fetch('/api/marketing/briefings/apreciation-full', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ ...(inlineData || {}), briefing_id: briefingId || undefined }),
      });
      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() ?? '');
          await processLines(lines);
        }
        if (done) {
          // Flush any remaining incomplete line in buf
          if (buf.startsWith('data: ')) await processLines([buf]);
          break;
        }
      }
    } catch (e) { setErrorMsg(e.message); setPhase('error'); }
  };

  // Auto-gerar ao abrir o tab — só quando não existe apreciação guardada com score ≥ 80
  React.useEffect(() => {
    const hasApreciation = (initialApreciation?.quality_score ?? 0) > 0;
    const hasEvals = !!(initialApreciation?.evals);
    if ((!hasApreciation || !hasEvals) && canGenerate && hasContent) runGenerate();
  }, []);

  const STEPS = [
    { step: 1, label: 'A carregar briefing',          duration: 1500  },
    { step: 2, label: 'A avaliar Bloco 1 — Produto',  duration: 8000  },
    { step: 3, label: 'A avaliar Bloco 2 — Cliente',  duration: 8000  },
    { step: 4, label: 'A avaliar Bloco 3 — Mercado',  duration: 8000  },
    { step: 5, label: 'A avaliar Bloco 4 — Campanha', duration: 8000  },
    { step: 6, label: 'A avaliar Bloco 5 — Guardrails',duration: 8000 },
    { step: 7, label: 'A calcular score global',      duration: 3000  },
  ];

  // Countdown timer — tempo restante estimado
  const [timeLeft, setTimeLeft] = React.useState(0);
  const totalMs = STEPS.reduce((s, st) => s + st.duration, 0); // ~44.5s total
  const EST_TOKENS = 17; // ~17K tokens (8.5K input + 8.5K output)
  // Claude Sonnet 4.6: $3/MTok input + $15/MTok output → ~$0.15 → ~€0.14
  const EST_COST_EUR = '€0.14';

  React.useEffect(() => {
    if (phase !== 'loading') { setTimeLeft(0); return; }
    // Calcular tempo restante: soma das durações dos steps não completados
    const remaining = STEPS.filter(s => s.step >= currentStep).reduce((acc, s) => acc + s.duration, 0);
    setTimeLeft(Math.ceil(remaining / 1000));
    const iv = setInterval(() => setTimeLeft(t => Math.max(0, t - 1)), 1000);
    return () => clearInterval(iv);
  }, [phase, currentStep]);

  // Timer de progresso por step — igual às campanhas
  React.useEffect(() => {
    if (phase !== 'loading' || currentStep <= 0) { setStepPct(0); return; }
    stepStartRef.current = Date.now();
    setStepPct(0);
  }, [currentStep, phase]);

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

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

  const BLOCK_NAMES_B6 = { block1: '1. Produto', block2: '2. Cliente', block3: '3. Mercado', block4: '4. Campanha', block5: '5. Restrições' };

  const formatVal = (v) => {
    if (v == null || v === '' || (Array.isArray(v) && v.length === 0)) return null;
    if (Array.isArray(v)) {
      const items = v.filter(i => i != null && String(i).trim());
      if (!items.length) return null;
      // Decisão 8: lista numerada por linha para arrays
      return items.map((item, i) => `${i + 1}. ${typeof item === 'object' ? (item.kpi || JSON.stringify(item)) : item}`).join('\n');
    }
    return String(v).trim() || null;
  };

  const handleApply = async (text, tipo, index, criterionData) => {
    // Se criterionData presente, usar index directamente como chave (já é o stateKey correcto)
    const key = criterionData ? index : `${tipo}_${index}`;
    setApplyState(s => ({ ...s, [key]: { status: 'loading' } }));
    try {
      const body = criterionData
        ? { ...criterionData, form_context: form || {}, brand_name: brandName || '' }
        : { suggestion: text, tipo, form_context: form || {}, brand_name: brandName || '' };
      const res = await fetch('/api/marketing/briefings/improve-field', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body),
      });
      if (!res.ok) { const j = await res.json().catch(() => ({})); throw new Error(j.error || `HTTP ${res.status}`); }
      const result = await res.json();
      if (result.block && result.field && result.value != null) {
        setApplyState(s => ({ ...s, [key]: { status: 'review', result } }));
      } else {
        throw new Error('Resposta inválida');
      }
    } catch (e) {
      console.error('[improve-field]', e.message);
      setApplyState(s => ({ ...s, [key]: { status: 'error' } }));
    }
  };

  // Actualiza evals sem chamada Claude: marca critérios como passed e recalcula scores
  const updateEvalsAfterImprovement = (blockStr, fieldName, currentAprec) => {
    if (!currentAprec?.evals) return currentAprec;
    const bKey = blockStr.replace('block', 'b'); // 'block1' → 'b1'
    const blockEval = currentAprec.evals[bKey];
    if (!blockEval || !Array.isArray(blockEval.fields)) return currentAprec;

    const updatedFields = blockEval.fields.map(f => {
      if (f.field !== fieldName) return f;
      const updatedCriteria = (f.criteria || []).map(c => c.passed ? c : { ...c, passed: true, feedback: null });
      const total = updatedCriteria.length;
      const passed = updatedCriteria.filter(c => c.passed).length;
      const newScore = total > 0 ? Math.min(95, Math.round((passed / total) * 100)) : Math.max(f.score, 80);
      return { ...f, criteria: updatedCriteria, score: newScore };
    });

    // Recalcular block_score pela média ponderada dos campos
    const fieldWeights = FIELD_WEIGHTS[bKey]?.fields || {};
    const totalW = Object.values(fieldWeights).reduce((s, w) => s + w, 0) || 100;
    const newBlockScore = Math.round(
      updatedFields.reduce((sum, f) => sum + (f.score * ((fieldWeights[f.field] || 0) / totalW)), 0)
    );

    const newEvals = { ...currentAprec.evals, [bKey]: { ...blockEval, fields: updatedFields, block_score: newBlockScore } };

    // Decisão 1: quality_score calculado por fórmula (consistente com potencialGlobal)
    const b1s = newEvals.b1?.block_score || 0;
    const b2s = newEvals.b2?.block_score || 0;
    const b3s = newEvals.b3?.block_score || 0;
    const b4s = newEvals.b4?.block_score || 0;
    const newQualityScore = Math.round(b1s * (FIELD_WEIGHTS.b1?.blockW || 0.20) + b2s * (FIELD_WEIGHTS.b2?.blockW || 0.45) + b3s * (FIELD_WEIGHTS.b3?.blockW || 0.10) + b4s * (FIELD_WEIGHTS.b4?.blockW || 0.25));

    const updated = {
      ...currentAprec,
      quality_score: newQualityScore,
      evals: newEvals,
      breakdown: { ...currentAprec.breakdown, [bKey]: { ...(currentAprec.breakdown?.[bKey] || {}), score: newBlockScore } },
    };
    return updated;
  };

  const handleConfirm = (key, result) => {
    let value = result.value;
    if (typeof value === 'string' && value.trim().startsWith('[')) {
      try { value = JSON.parse(value); } catch {}
    }
    onApplyImprovement?.(result.block, result.field, value, result.field_label);
    if (result.block && result.field) {
      const fieldKey = `${result.block}.${result.field}`;
      setImprovedFields(prev => new Set([...prev, fieldKey]));
      // Actualizar evals + block score + quality_score (fórmula) + improved_fields
      const evalsUpdated = updateEvalsAfterImprovement(result.block, result.field, apreciation);
      const newImprovedFields = [...new Set([...(apreciation?.improved_fields || []), fieldKey])];
      const updated = { ...evalsUpdated, improved_fields: newImprovedFields };
      setApreciation(updated);
      onUpdate?.(updated);
      const gain = evalsUpdated.quality_score - (apreciation?.quality_score || 0);
      if (gain > 0) setScoreGain(prev => prev + gain);
    }
    setApplyState(s => ({ ...s, [key]: { status: 'done', ...result } }));
    setImprovementsApplied(true);
  };

  const handleReject = (key) => {
    setApplyState(s => ({ ...s, [key]: { status: 'idle' } }));
  };

  const ApplyBtn = ({ text, tipo, index }) => {
    const key  = `${tipo}_${index}`;
    const item = applyState[key] || { status: 'idle' };
    const st   = item.status;
    if (!onApplyImprovement) return null;
    if (st === 'review') return null; // review card takes over
    return (
      <button
        onClick={() => st === 'error' ? setApplyState(s => ({ ...s, [key]: { status: 'idle' } })) : handleApply(text, tipo, index)}
        disabled={st === 'loading' || st === 'done'}
        style={{ flexShrink: 0, background: 'none', border: 'none', cursor: st === 'done' ? 'default' : 'pointer', padding: '2px 0', display: 'flex', alignItems: 'center', gap: 4,
          fontSize: 10, fontWeight: 600, letterSpacing: '0.03em', fontFamily: 'var(--font-mono)',
          color: st === 'done' ? 'var(--success)' : st === 'error' ? 'var(--danger)' : 'var(--ai-500)',
          opacity: st === 'done' ? 0.8 : 1,
        }}>
        {st === 'loading' && <div style={{ width: 10, height: 10, borderRadius: '50%', border: '1.5px solid var(--border)', borderTopColor: 'var(--ai-500)', animation: 'spin 0.8s linear infinite' }} />}
        {st === 'done'    && <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>}
        {st === 'error'   && <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 11-2.12-9.36L23 10"/></svg>}
        {st === 'idle'    && <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>}
        {st === 'loading' ? 'A gerar...' : st === 'done' ? 'Aplicado' : st === 'error' ? 'Tentar de novo' : 'Melhorar'}
      </button>
    );
  };

  // Botão de melhoria específico para critérios — sabe o campo exacto
  const CriterionApplyBtn = ({ bKey, fieldName, critId, critLabel, feedback, currentValue, overrideStateKey, overrideCriterionData }) => {
    const stateKey = overrideStateKey || `crit_${bKey}_${fieldName}_${critId}`;
    const item = applyState[stateKey] || { status: 'idle' };
    const st   = item.status;
    if (!onApplyImprovement) return null;
    if (st === 'review') return null;
    const blockMap = { b1: 'block1', b2: 'block2', b3: 'block3', b4: 'block4', b5: 'block5' };
    const criterionData = overrideCriterionData || {
      block: blockMap[bKey], field: fieldName,
      criterion_id: critId, criterion_label: critLabel,
      feedback, current_value: currentValue,
    };
    const label = overrideStateKey ? `Melhorar ${fieldName.replace(/_/g,' ')}` : 'Melhorar';
    return (
      <button
        onClick={() => st === 'error'
          ? setApplyState(s => ({ ...s, [stateKey]: { status: 'idle' } }))
          : handleApply(feedback, 'lacuna', stateKey, criterionData)}
        disabled={st === 'loading' || st === 'done'}
        style={{ flexShrink: 0, background: st === 'idle' ? 'color-mix(in oklch, var(--ai-500) 8%, transparent)' : 'none',
          border: st === 'idle' ? '1px solid color-mix(in oklch, var(--ai-500) 25%, transparent)' : 'none',
          borderRadius: 5, cursor: st === 'done' ? 'default' : 'pointer',
          padding: st === 'idle' ? '3px 8px' : '2px 0',
          display: 'flex', alignItems: 'center', gap: 4,
          fontSize: 10, fontWeight: 600, fontFamily: 'var(--font-mono)',
          color: st === 'done' ? 'var(--success)' : st === 'error' ? 'var(--danger)' : 'var(--ai-500)',
        }}>
        {st === 'loading' && <div style={{ width: 10, height: 10, borderRadius: '50%', border: '1.5px solid var(--border)', borderTopColor: 'var(--ai-500)', animation: 'spin 0.8s linear infinite' }} />}
        {st === 'done'    && <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>}
        {st === 'error'   && <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 11-2.12-9.36L23 10"/></svg>}
        {st === 'idle'    && <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>}
        {st === 'loading' ? 'A gerar...' : st === 'done' ? 'Aplicado' : st === 'error' ? 'Tentar de novo' : label}
      </button>
    );
  };

  // Card de transparência KB — substitui KbSourcesLine
  const KbSourcesLine = ({ sources = [], total = 0, confidence, avgScore, semanticSources = [] }) => {
    const [expanded, setExpanded] = React.useState(false);
    const [showModal, setShowModal] = React.useState(false);
    if (!sources || sources.length === 0) return null;

    const urlLabel = (fn) => { try { const u = new URL(fn); return u.hostname.replace('www.','') + (u.pathname.length > 1 ? u.pathname.slice(0,30) : ''); } catch { return fn.split('/').pop()?.slice(0,40) || fn; } };
    const docLabel = (fn) => fn.split('/').pop()?.replace(/_/g,' ').replace(/\.[a-z]+$/,'').slice(0,45) || fn;

    const confColor = confidence === 'high' ? '#15803d' : confidence === 'medium' ? '#b45309' : confidence === 'low' ? '#dc2626' : '#94a3b8';
    const confLabel = confidence === 'high' ? 'Alta' : confidence === 'medium' ? 'Média' : confidence === 'low' ? 'Baixa' : '—';
    const confBg    = confidence === 'high' ? 'rgba(21,128,61,.08)' : confidence === 'medium' ? 'rgba(180,83,9,.08)' : confidence === 'low' ? 'rgba(220,38,38,.06)' : 'transparent';

    // Agrupar por source_label para evitar repetição
    const grouped = sources.reduce((acc, s) => {
      const key = s.source_label || s.source;
      if (!acc[key]) acc[key] = [];
      acc[key].push(s);
      return acc;
    }, {});

    return (
      <>
        {/* Card compacto */}
        <div style={{ marginBottom:8, marginTop:4, borderRadius:7, border:'1px solid var(--border)', overflow:'hidden' }}>
          <div style={{ display:'flex', alignItems:'center', gap:8, padding:'6px 10px', background:'var(--bg-sunken)', cursor:'pointer' }} onClick={() => setExpanded(e => !e)}>
            <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="var(--text-dim)" strokeWidth="2" strokeLinecap="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
            <span style={{ fontSize:10, fontFamily:'var(--font-mono)', color:'var(--text-muted)', flex:1 }}>
              KB verificado · {total} excertos · {Object.keys(grouped).join(', ')}
            </span>
            {confidence && (
              <span style={{ fontSize:9, fontWeight:700, padding:'1px 6px', borderRadius:3, background:confBg, color:confColor, fontFamily:'var(--font-mono)' }}>
                {confLabel}
              </span>
            )}
            <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="var(--text-dim)" strokeWidth="2.5" strokeLinecap="round" style={{ transform: expanded ? 'rotate(180deg)' : 'none', transition:'transform .15s' }}><polyline points="6 9 12 15 18 9"/></svg>
          </div>
          {expanded && (
            <div style={{ padding:'8px 10px', fontSize:10, fontFamily:'var(--font-mono)', borderTop:'1px solid var(--border)' }}>
              {Object.entries(grouped).map(([label, items]) => (
                <div key={label} style={{ marginBottom:4 }}>
                  <span style={{ fontWeight:600, color:'var(--text-muted)' }}>{label}</span>
                  <div style={{ paddingLeft:8, marginTop:2 }}>
                    {items.map((s, i) => (
                      <div key={i} style={{ color:'var(--text-dim)', marginBottom:2 }}>
                        {s.type === 'url'
                          ? <a href={s.file_name} target="_blank" rel="noopener noreferrer" style={{ color:'var(--ai-500,#3859D0)', textDecoration:'none' }}>{urlLabel(s.file_name)}</a>
                          : <span>{docLabel(s.file_name)}</span>
                        }
                      </div>
                    ))}
                  </div>
                </div>
              ))}
              {semanticSources.length > 0 && (
                <div style={{ marginTop:4, color:'var(--text-dim)', borderTop:'1px solid var(--border)', paddingTop:4 }}>
                  + análise semântica: {semanticSources.map(s => s.source_label || s.source).join(', ')}
                </div>
              )}
              <button onClick={() => setShowModal(true)} style={{ marginTop:6, background:'none', border:'none', color:'var(--ai-500)', cursor:'pointer', fontSize:10, fontFamily:'var(--font-mono)', padding:0 }}>
                Ver fontes detalhadas →
              </button>
            </div>
          )}
        </div>

        {/* Modal fontes detalhadas */}
        {showModal && (
          <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,0.4)', zIndex:9999, display:'flex', alignItems:'center', justifyContent:'center' }} onClick={() => setShowModal(false)}>
            <div style={{ background:'var(--bg-elev,#fff)', borderRadius:10, padding:'20px 24px', maxWidth:560, width:'90%', maxHeight:'80vh', overflowY:'auto', boxShadow:'0 8px 32px rgba(17,41,84,.18)' }} onClick={e => e.stopPropagation()}>
              <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:16 }}>
                <div style={{ fontSize:14, fontWeight:700, color:'var(--text)' }}>Fontes consultadas na KB</div>
                <button onClick={() => setShowModal(false)} style={{ background:'none', border:'none', cursor:'pointer', color:'var(--text-dim)', fontSize:18, lineHeight:1 }}>×</button>
              </div>
              <div style={{ fontSize:11, color:'var(--text-muted)', marginBottom:12 }}>
                {total} excertos · confiança <strong style={{ color:confColor }}>{confLabel}</strong>{avgScore ? ` (score médio ${avgScore})` : ''}
              </div>
              {Object.entries(grouped).map(([label, items]) => (
                <div key={label} style={{ marginBottom:14 }}>
                  <div style={{ fontSize:11, fontWeight:700, color:'var(--text)', marginBottom:6, padding:'4px 8px', background:'var(--bg-sunken)', borderRadius:5 }}>{label}</div>
                  {items.map((s, i) => (
                    <div key={i} style={{ padding:'6px 8px', borderRadius:5, border:'1px solid var(--border)', marginBottom:4, display:'flex', alignItems:'center', gap:8 }}>
                      <div style={{ flex:1, fontSize:11, color:'var(--text-muted)', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
                        {s.type === 'url'
                          ? <a href={s.file_name} target="_blank" rel="noopener noreferrer" style={{ color:'var(--ai-500,#3859D0)', textDecoration:'none' }} title={s.file_name}>{urlLabel(s.file_name)}</a>
                          : <span title={s.file_name}>{docLabel(s.file_name)}</span>
                        }
                      </div>
                      {s.type === 'url' && (
                        <a href={s.file_name} target="_blank" rel="noopener noreferrer" style={{ flexShrink:0 }}>
                          <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="var(--ai-500)" strokeWidth="2" strokeLinecap="round"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
                        </a>
                      )}
                    </div>
                  ))}
                </div>
              ))}
            </div>
          </div>
        )}
      </>
    );
  };

  const CriterionReviewCard = ({ bKey, fieldName, critId, overrideStateKey, numCriteriaFixed = 1 }) => {
    const stateKey = overrideStateKey || `crit_${bKey}_${fieldName}_${critId}`;
    const item = applyState[stateKey];
    if (!item || item.status !== 'review' || !item.result) return null;
    const { block, field, field_label, value, _kb_sources, _kb_total, _kb_confidence, _kb_avg_score, _kb_semantic_sources } = item.result;
    const currentRaw = form?.[block]?.[field];
    const beforeStr  = formatVal(currentRaw);
    let afterParsed  = value;
    if (typeof value === 'string' && value.trim().startsWith('[')) {
      try { afterParsed = JSON.parse(value); } catch {}
    }
    const afterStr = formatVal(afterParsed);
    return (
      <div style={{ margin: '6px 0 10px', padding: '12px 14px', borderRadius: 9, background: 'rgba(56,89,208,.04)', border: '1px solid rgba(56,89,208,.18)' }}>
        <div style={{ fontSize: 9, fontWeight: 700, color: 'var(--ai-500)', marginBottom: 10, fontFamily: 'var(--font-mono)', letterSpacing: '0.04em', textTransform: 'uppercase' }}>
          Sugestão Digi AI — {field_label || field}
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginBottom: 10 }}>
          <div>
            <div style={{ fontSize: 9, fontWeight: 600, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', letterSpacing: '0.06em', marginBottom: 4, textTransform: 'uppercase' }}>Actual</div>
            <div style={{ fontSize: 11, color: 'var(--text-muted)', background: 'var(--bg-sunken)', borderRadius: 6, padding: '8px 10px', lineHeight: 1.7, whiteSpace: 'pre-line' }}>
              {beforeStr || <span style={{ fontStyle: 'italic' }}>não preenchido</span>}
            </div>
          </div>
          <div>
            <div style={{ fontSize: 9, fontWeight: 600, color: 'var(--ai-500)', fontFamily: 'var(--font-mono)', letterSpacing: '0.06em', marginBottom: 4, textTransform: 'uppercase' }}>Sugestão Digi AI</div>
            <div style={{ fontSize: 11, color: 'var(--text)', background: 'rgba(56,89,208,.06)', border: '1px solid rgba(56,89,208,.15)', borderRadius: 6, padding: '8px 10px', lineHeight: 1.7, whiteSpace: 'pre-line' }}>
              {afterStr || '—'}
            </div>
          </div>
        </div>
        <KbSourcesLine sources={_kb_sources} total={_kb_total} confidence={_kb_confidence} avgScore={_kb_avg_score} semanticSources={_kb_semantic_sources} />
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <button className="btn" style={{ fontSize: 11 }} onClick={() => setApplyState(s => ({ ...s, [stateKey]: { status: 'idle' } }))}>Rejeitar</button>
          <div style={{ display: 'flex', gap: 6 }}>
            {onNavigateToBlock && (
              <button className="btn" style={{ fontSize: 11 }} onClick={() => onNavigateToBlock(parseInt(block.replace('block',''), 10))}>
                Editar manualmente
              </button>
            )}
            <button className="btn btn-ai" style={{ fontSize: 11, display: 'flex', alignItems: 'center', gap: 5 }}
              onClick={() => {
                let v = value;
                if (typeof v === 'string' && v.trim().startsWith('[')) { try { v = JSON.parse(v); } catch {} }
                onApplyImprovement?.(block, field, v, field_label);
                const fk = `${block}.${field}`;
                setImprovedFields(prev => new Set([...prev, fk]));
                const evalsUpd = updateEvalsAfterImprovement(block, field, apreciation);
                const newImpFields = [...new Set([...(apreciation?.improved_fields || []), fk])];
                const finalAp = { ...evalsUpd, improved_fields: newImpFields };
                setApreciation(finalAp);
                onUpdate?.(finalAp);
                const gain = evalsUpd.quality_score - (apreciation?.quality_score || 0);
                if (gain > 0) setScoreGain(prev => prev + gain);
                setApplyState(s => ({ ...s, [stateKey]: { status: 'done' } }));
                setImprovementsApplied(true);
              }}>
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
              Confirmar e aplicar
            </button>
          </div>
        </div>
      </div>
    );
  };

  const ReviewCard = ({ itemKey }) => {
    const item = applyState[itemKey];
    if (!item || item.status !== 'review' || !item.result) return null;
    const { block, field, field_label, value, _kb_sources, _kb_total, _kb_confidence, _kb_avg_score, _kb_semantic_sources } = item.result;
    const currentRaw = form?.[block]?.[field];
    const beforeStr  = formatVal(currentRaw);
    let afterParsed  = value;
    if (typeof value === 'string' && value.trim().startsWith('[')) {
      try { afterParsed = JSON.parse(value); } catch {}
    }
    const afterStr = formatVal(afterParsed);
    const blockLabel = BLOCK_NAMES_B6[block] || block;
    return (
      <div style={{ margin: '4px 0 10px 8px', padding: '14px 16px', borderRadius: 9, background: 'rgba(56,89,208,.04)', border: '1px solid rgba(56,89,208,.18)' }}>
        <div style={{ fontSize: 10, fontWeight: 700, color: 'var(--ai-500)', marginBottom: 12, fontFamily: 'var(--font-mono)', letterSpacing: '0.04em', textTransform: 'uppercase' }}>
          {field_label || field} — {blockLabel}
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginBottom: 12 }}>
          <div>
            <div style={{ fontSize: 9, fontWeight: 600, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', letterSpacing: '0.06em', marginBottom: 5, textTransform: 'uppercase' }}>Actual</div>
            <div style={{ fontSize: 11, color: 'var(--text-muted)', background: 'var(--bg-sunken, #f1f5f9)', borderRadius: 6, padding: '8px 10px', lineHeight: 1.7, whiteSpace: 'pre-line' }}>
              {beforeStr || <span style={{ color: 'var(--text-dim)', fontStyle: 'italic' }}>não preenchido</span>}
            </div>
          </div>
          <div>
            <div style={{ fontSize: 9, fontWeight: 600, color: 'var(--ai-500)', fontFamily: 'var(--font-mono)', letterSpacing: '0.06em', marginBottom: 5, textTransform: 'uppercase' }}>Sugestão Digi AI</div>
            <div style={{ fontSize: 11, color: 'var(--text)', background: 'rgba(56,89,208,.06)', border: '1px solid rgba(56,89,208,.15)', borderRadius: 6, padding: '8px 10px', lineHeight: 1.7, whiteSpace: 'pre-line' }}>
              {afterStr || '—'}
            </div>
          </div>
        </div>
        <KbSourcesLine sources={_kb_sources} total={_kb_total} confidence={_kb_confidence} avgScore={_kb_avg_score} semanticSources={_kb_semantic_sources} />
        <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
          <button className="btn" style={{ fontSize: 11 }} onClick={() => handleReject(itemKey)}>Rejeitar</button>
          <button className="btn btn-ai" style={{ fontSize: 11, display: 'flex', alignItems: 'center', gap: 5 }} onClick={() => handleConfirm(itemKey, item.result)}>
            <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
            Confirmar
          </button>
        </div>
      </div>
    );
  };

  return (
    <div>
      {/* Fontes importadas — integradas no score card abaixo */}

      {/* Loading — mesmo padrão que campanhas (conceito/copy) */}
      {phase === 'loading' && (() => {
        const totalSteps = STEPS.length;
        const doneSteps  = STEPS.filter(s => s.step < currentStep).length;
        const overallPct = Math.round(((doneSteps + stepPct / 100) / totalSteps) * 100);
        return (
          <div style={{ background: 'var(--bg-elev, #fff)', border: '1px solid var(--border, #e2e8f0)', borderRadius: 10, padding: '24px 24px 20px', marginBottom: 16 }}>
            <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
            {/* Header */}
            <div style={{ display: 'flex', alignItems: 'flex-start', gap: 12, marginBottom: 18 }}>
              <div style={{ width: 36, height: 36, borderRadius: '50%', background: 'rgba(56,89,208,.08)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="var(--ai-500, #3859D0)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                  <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>
                </svg>
              </div>
              <div style={{ flex: 1 }}>
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 5 }}>
                  <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--text, #1d2e38)', fontFamily: 'var(--font-display, Montserrat, sans-serif)' }}>A gerar apreciação</div>
                  <span style={{ fontSize: 13, fontWeight: 700, color: 'var(--ai-500, #3859D0)', fontFamily: 'var(--font-mono, monospace)' }}>{overallPct}%</span>
                </div>
                <div style={{ height: 4, borderRadius: 99, background: 'var(--bg-sunken, #f1f5f9)', overflow: 'hidden', marginBottom: 4 }}>
                  <div style={{ height: '100%', borderRadius: 99, background: 'var(--ai-500, #3859D0)', width: `${overallPct}%`, transition: 'width 0.1s linear' }} />
                </div>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
                  <span style={{ fontSize: 10, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>Digi AI</span>
                  <span style={{ fontSize: 10, color: 'var(--text-dim)' }}>·</span>
                  <span style={{ fontSize: 10, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>~{EST_TOKENS}K tokens</span>
                  <span style={{ fontSize: 10, color: 'var(--text-dim)' }}>·</span>
                  <span style={{ fontSize: 10, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>~{EST_COST_EUR}</span>
                  <span style={{ fontSize: 10, color: 'var(--text-dim)' }}>·</span>
                  <span style={{
                    fontSize: 11, fontWeight: 700, fontFamily: 'var(--font-mono)',
                    color: 'var(--ai-500, #3859D0)',
                    display: 'flex', alignItems: 'center', gap: 3,
                  }}>
                    <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
                      <circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>
                    </svg>
                    {timeLeft}s
                  </span>
                </div>
              </div>
            </div>
            {/* Steps */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              {STEPS.map(s => {
                const done    = currentStep > s.step;
                const active  = currentStep === s.step;
                const pending = currentStep < s.step;
                const pct     = done ? 100 : active ? stepPct : 0;
                return (
                  <div key={s.step}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: active ? 5 : 0 }}>
                      <div style={{
                        width: 26, height: 26, borderRadius: '50%', flexShrink: 0,
                        display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 10, fontWeight: 700,
                        background: done ? 'rgba(21,128,61,.1)' : active ? 'rgba(56,89,208,.1)' : 'var(--bg-sunken, #f1f5f9)',
                        border: `1.5px solid ${done ? 'rgba(21,128,61,.3)' : active ? 'var(--ai-500, #3859D0)' : 'var(--border, #e2e8f0)'}`,
                        color: done ? '#15803d' : active ? 'var(--ai-500, #3859D0)' : '#94a3b8',
                        transition: 'all 0.3s',
                      }}>
                        {done
                          ? <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
                          : s.step
                        }
                      </div>
                      <span style={{ fontSize: 12.5, fontWeight: active ? 600 : 400, color: pending ? '#94a3b8' : 'var(--text, #1d2e38)', flex: 1 }}>{s.label}</span>
                      {active && <span style={{ fontSize: 10, fontWeight: 700, color: 'var(--ai-500, #3859D0)', fontFamily: 'var(--font-mono, monospace)', flexShrink: 0 }}>{Math.round(pct)}%</span>}
                      {done  && <span style={{ fontSize: 10, color: '#15803d', fontFamily: 'var(--font-mono, monospace)', flexShrink: 0 }}>100%</span>}
                    </div>
                    {active && (
                      <div style={{ marginLeft: 36, height: 3, borderRadius: 99, background: 'rgba(56,89,208,.1)', overflow: 'hidden' }}>
                        <div style={{ height: '100%', borderRadius: 99, background: 'var(--ai-500, #3859D0)', width: `${pct}%`, transition: 'width 0.1s linear' }} />
                      </div>
                    )}
                    {done && (
                      <div style={{ marginLeft: 36, height: 2, borderRadius: 99, background: 'rgba(21,128,61,.2)' }} />
                    )}
                  </div>
                );
              })}
            </div>
          </div>
        );
      })()}

      {/* Error */}
      {phase === 'error' && (
        <FormCard>
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12, padding: '8px 0', textAlign: 'center' }}>
            <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="var(--danger)" strokeWidth="1.5" strokeLinecap="round">
              <path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/>
            </svg>
            <div style={{ fontSize: 13, color: 'var(--danger)' }}>{errorMsg}</div>
            {canGenerate && <button className="btn btn-ai" onClick={runGenerate} style={{ marginTop: 4 }}>Tentar novamente</button>}
          </div>
        </FormCard>
      )}

      {/* Empty state */}
      {phase !== 'loading' && !apreciation && phase !== 'error' && (
        <FormCard>
          <div style={{ textAlign: 'center', padding: '24px 24px 16px', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}>
            <div style={{ width: 40, height: 40, borderRadius: 12, background: 'color-mix(in oklch, var(--ai-500) 10%, transparent)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--ai-500)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>
              </svg>
            </div>
            <div style={{ fontSize: 15, fontWeight: 600, color: 'var(--text)' }}>Apreciação não gerada</div>
            <div style={{ fontSize: 13, color: 'var(--text-muted)', maxWidth: 400, lineHeight: 1.65 }}>
              {!hasContent
                ? 'Preenche pelo menos 40% dos blocos 1-3 para gerar a apreciação.'
                : <>A apreciação é <strong>obrigatória</strong> antes de submeter — o score deve atingir ≥ 80.</>}
            </div>
            {canGenerate && (
              <button className="btn btn-ai" onClick={runGenerate} disabled={!hasContent}>Gerar Apreciação</button>
            )}
          </div>
        </FormCard>
      )}

      {/* 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: 'Bloco 1 — Produto', b2: 'Bloco 2 — Cliente', b3: 'Bloco 3 — Mercado', b4: 'Bloco 4 — Campanha', b5: 'Bloco 5 — Restrições' };
        const BLOCK_KEYS   = ['b1','b2','b3','b4','b5'];
        const evals        = apreciation.evals || {};
        const hasEvalsData = !!(apreciation.evals);
        const scoreReady = score >= 80;
        return (
          <>
            {/* Score global */}
            <div style={{ background: 'var(--bg-elev, #ffffff)', border: `2px solid ${scoreReady ? 'color-mix(in srgb, var(--ai-500, #3859D0) 25%, transparent)' : 'var(--border)'}`, borderRadius: 10, padding: '20px 24px', marginBottom: 16, display: 'flex', alignItems: 'flex-start', gap: 20 }}>
              {/* Coluna esquerda — número */}
              <div style={{ textAlign: 'center', flexShrink: 0, minWidth: 52 }}>
                <div style={{ fontSize: 44, fontWeight: 700, color: scoreColor(score), lineHeight: 1, fontFamily: 'var(--font-display)' }}>{score}</div>
                <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', color: scoreColor(score), letterSpacing: '0.06em', marginTop: 3 }}>{scoreLabel(score)}</div>
              </div>
              {/* Coluna central — barra + meta */}
              <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 6, paddingTop: 4 }}>
                <div style={{ height: 8, borderRadius: 4, background: 'var(--border)', overflow: 'hidden' }}>
                  <div style={{ width: `${score}%`, height: '100%', background: scoreColor(score), borderRadius: 4, transition: 'width 0.6s ease' }} />
                </div>
                <div style={{ display: 'flex', alignItems: 'center', gap: 5, fontSize: 12, color: scoreReady ? 'var(--ai-500, #3859D0)' : 'var(--text-muted, #4a5e7a)', fontWeight: 600 }}>
                  {scoreReady
                    ? <><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg> Pronto para submeter</>
                    : `Necessário ≥ 80 para submeter · faltam ${80 - score} pontos`
                  }
                </div>
                <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
                  <span style={{ fontSize: 10, color: 'var(--text-dim)', 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' }) : ''}
                  </span>
                  {sources.length > 0 && (
                    <>
                      <span style={{ fontSize: 10, color: 'var(--border)', fontFamily: 'var(--font-mono)' }}>·</span>
                      <span style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)' }}>
                        <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                          <path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/>
                        </svg>
                        {sources.map(s => s.name || s.url).join(', ')}
                      </span>
                    </>
                  )}
                </div>
              </div>
              {/* Botão */}
              {canGenerate && (
                <button className="btn" onClick={runGenerate} style={{ flexShrink: 0, fontSize: 11, display: 'flex', alignItems: 'center', gap: 5, marginTop: 2 }}>
                  <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
                    <polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 11-2.12-9.36L23 10"/>
                  </svg>
                  Analisar Novamente
                </button>
              )}
            </div>

            {/* Card: análise por campo em falta — score < 100% mas sem evals */}
            {!hasEvalsData && score < 100 && canGenerate && (
              <div style={{ display:'flex', alignItems:'center', gap:14, padding:'14px 18px', borderRadius:9, background:'color-mix(in srgb, var(--ai-500,#3859D0) 5%, transparent)', border:'1px solid color-mix(in srgb, var(--ai-500,#3859D0) 20%, transparent)', marginBottom:8 }}>
                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="var(--ai-500,#3859D0)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink:0 }}><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
                <div style={{ flex:1 }}>
                  <div style={{ fontSize:13, fontWeight:600, color:'var(--text)', marginBottom:2 }}>
                    {score}/100 — há melhorias disponíveis
                  </div>
                  <div style={{ fontSize:11, color:'var(--text-muted)' }}>
                    Corre a análise por campo para ver exactamente o que melhorar em cada bloco.
                  </div>
                </div>
                <button className="btn btn-ai" onClick={runGenerate} style={{ fontSize:12, flexShrink:0 }}>
                  Ver melhorias →
                </button>
              </div>
            )}

            {/* Banner melhorias aplicadas */}
            {improvementsApplied && (
              <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 16px', borderRadius: 9, background: 'color-mix(in srgb, var(--ai-500, #3859D0) 6%, transparent)', border: '1px solid color-mix(in srgb, var(--ai-500, #3859D0) 20%, transparent)', marginBottom: 4 }}>
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--ai-500, #3859D0)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}><polyline points="20 6 9 17 4 12"/></svg>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text)', marginBottom: 2 }}>
                    Melhorias aplicadas
                    {scoreGain > 0 && <span style={{ color: 'var(--ai-500, #3859D0)', marginLeft: 8 }}>+{scoreGain} pts</span>}
                  </div>
                  {scoreGain > 0 && (
                    <div style={{ fontSize: 11, color: 'var(--text-muted)' }}>
                      Score actualizado: <strong style={{ color: 'var(--ai-500, #3859D0)' }}>{score}/100</strong>
                    </div>
                  )}
                </div>
                {canGenerate && <button className="btn" style={{ fontSize: 11, height: 28, padding: '0 12px', flexShrink: 0 }} onClick={runGenerate}>Re-analisar</button>}
              </div>
            )}

            {/* Resumo narrativo */}
            {apreciation.resumo && (
              <FormCard title="Resumo">
                <p style={{ margin: 0, fontSize: 13, color: 'var(--text)', lineHeight: 1.75 }}>{apreciation.resumo}</p>
              </FormCard>
            )}

            {/* Score por bloco — reformulado */}
            {(() => {
              // Labels PT para campos
              const FIELD_LABELS = {
                elevator_pitch: 'Elevator Pitch', usps: 'USPs — Pontos de Valor', certifications: 'Certificações Técnicas', differentiation_args: 'Argumentos de Diferenciação', applications: 'Aplicações Primárias',
                pain_points: 'Dores Principais', motivators: 'Motivadores de Compra', objections: 'Objecções Comuns', purchase_trigger: 'Trigger de Compra', decision_maker: 'Decisor de Compra', end_user: 'Utilizador Final',
                competitors: 'Concorrentes', market_trends: 'Tendências de Mercado', positioning_narrative: 'Posicionamento Desejado', sensitive_info: 'Intel Competitiva',
                key_message: 'Mensagem-Chave', objective: 'Objectivo', tone: 'Tom da Campanha', kpis: 'KPIs de Sucesso',
                prohibited_claims: 'Claims Proibidos', off_brand_messages: 'Mensagens Off-Brand', competitors_not_name: 'Concorrentes a Não Nomear', out_of_scope_markets: 'Mercados Fora de Âmbito', other_restrictions: 'Outras Restrições',
              };
              const CRIT_LABELS = { especificidade: 'Especificidade', especificidade_contexto: 'Especificidade do contexto', linguagem_persona: 'Linguagem da persona', consequencia_real: 'Consequência real', ordenacao_prioridade: 'Ordenação por prioridade', ordenacao_forca: 'Ordenação por força', ordenacao_frequencia: 'Ordenação por frequência', ordenacao_urgencia: 'Ordenação por urgência', ordenacao_impacto: 'Ordenação por impacto', ordenacao_criticidade: 'Ordenação por criticidade', ordenacao_relevancia: 'Ordenação por relevância', resultado_concreto: 'Resultado concreto', distincao_dores: 'Distinção das dores', especificidade_persona: 'Específico para esta persona', respondivel_usp: 'Respondível por USP/certificação', linguagem_hesitante: 'Linguagem real do hesitante', evento_concreto: 'Evento concreto', timing_identificavel: 'Timing identificável', ligacao_objectivo: 'Ligação ao objectivo', sintese_real: 'Síntese real', tensao_diferenciadora: 'Tensão diferenciadora', linguagem_mercado: 'Linguagem do mercado', coerencia_usps_dores: 'Coerência com USPs e dores', defensabilidade: 'Defensabilidade das claims', beneficio_cliente: 'Benefício para o cliente', unicidade_real: 'Unicidade real', ligacao_dor: 'Ligação a dor declarada', nao_sobreposicao: 'Sem sobreposição entre USPs', ligacao_usp: 'Ligação a USP específico', reconhecimento: 'Reconhecimento no mercado', especificidade_completa: 'Nome completo e específico', relevancia_regulatoria: 'Relevância regulatória', posicionamento_competitivo: 'Posicionamento competitivo real', verificabilidade: 'Verificabilidade', relevancia_decisor: 'Relevância para o decisor', distincao_usps: 'Distinção dos USPs', ligacao_concorrentes: 'Ligação aos concorrentes', relevancia_temporal: 'Relevância temporal', ligacao_produto: 'Ligação ao produto', relevancia_geografica: 'Relevância geográfica', accionabilidade_hooks: 'Accionabilidade para hooks', clareza_posicao: 'Clareza da posição', unicidade: 'Unicidade', coerencia_usps_diff: 'Coerência com USPs', credibilidade: 'Credibilidade', identificacao_especifica: 'Identificação específica', contexto_ameaca: 'Contexto da ameaça', coerencia_diferenciacao: 'Coerência com diferenciação', pontos_fortes: 'Pontos fortes percebidos', diferenciacao_real: 'Diferenciação real', tensao_criativa: 'Tensão criativa', clareza_destinatario: 'Clareza do destinatário', completude: 'Completude', ligacao_dor_dominante: 'Ligação à dor dominante', coerencia_kpis: 'Coerência com KPIs', coerencia_canais: 'Coerência com canais', coerencia_mensagem: 'Coerência com mensagem-chave', coerencia_timeline: 'Coerência com timeline', adequacao_produto: 'Adequação ao produto', especificidade_accionavel: 'Especificidade accionável', inclui_evitar: 'Inclui o que evitar', coerencia_persona: 'Coerência com a persona', diferenciacao_marca: 'Diferenciação da marca', aplicabilidade_multicanal: 'Aplicabilidade multi-canal', mensuráveis: 'Mensuráveis e concretos', coerencia_objectivo: 'Coerência com objectivo', contexto_profissional: 'Contexto profissional específico', responsabilidades: 'Responsabilidades e pressões', papel_decisao: 'Papel na decisão', coerencia_dores: 'Coerência com as dores', indicadores_confianca: 'Indicadores de confiança', contexto_uso: 'Contexto de uso diário', dor_diferente: 'Dor diferente do decisor', influencia_decisao: 'Influência na decisão', distincao_decisor: 'Distinção do decisor', confianca_tecnica: 'Critérios de confiança técnica', razao_documentada: 'Razão documentada', cobertura_riscos: 'Cobertura dos riscos', especificidade_estrategica: 'Especificidade estratégica', ligacao_posicionamento: 'Ligação ao posicionamento', cobertura_derives: 'Cobertura das derives', razao_estrategica: 'Razão estratégica', completude_concorrentes: 'Completude', alternativa: 'Alternativa definida', clareza_ambito: 'Clareza do âmbito', coerencia_mercados: 'Coerência com mercados declarados', classificacao_correcta: 'Classificação correcta' };
              const fieldLabel  = (f) => FIELD_LABELS[f] || f.split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
              const critLabel   = (id) => CRIT_LABELS[id] || id.split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
              const scoreLabel  = (s) => s === 0 ? 'Campo vazio' : s < 40 ? 'Fraco' : s < 60 ? 'A melhorar' : s < 80 ? 'Suficiente' : s < 90 ? 'Bom' : 'Excelente';

              // Calcular score potencial (se todos os campos melhorassem a 85)
              // potScore: score potencial se todas as melhorias fossem aplicadas
              // Regra: sem breakdown de campos → assume potencial de 85 (não sabemos o que está mal mas pode melhorar)
              //        com breakdown → campos com falhas vão a 85, campos ok ficam como estão
              //        tudo passou → mantém score actual
              const potScore = (bkk) => {
                const fields = evals[bkk]?.fields || [];
                if (!fields.length) return 100; // sem detalhe → potencial máximo
                const allPass = fields.every(f => (f.criteria||[]).every(c => c.passed));
                if (allPass) return Math.round(fields.reduce((s, f) => s + f.score, 0) / Math.max(fields.length, 1));
                // Campos com falhas → potencial 100 (se todas as sugestões forem aplicadas)
                return Math.round(fields.reduce((sum, f) => sum + (f.criteria?.some(c=>!c.passed) ? 100 : f.score), 0) / Math.max(fields.length, 1));
              };
              const potentialGlobal = Math.min(100, Math.round(
                potScore('b2') * 0.45 + potScore('b4') * 0.25 + potScore('b1') * 0.20 + potScore('b3') * 0.10
              ));
              // Blocos realmente vazios (score=0): o utilizador não preencheu
              const emptyHighWeightBlocks = ['b1','b2','b3','b4'].filter(k => (bk[k]?.score ?? 0) === 0);
              // Blocos com conteúdo mas sem breakdown de campos (score>0, fields=[]): avaliação incompleta
              const noBreakdownBlocks = ['b1','b2','b3','b4'].filter(k => {
                const hasScore = (bk[k]?.score ?? 0) > 0;
                const hasNoFields = (evals[k]?.fields||[]).length === 0;
                return hasScore && hasNoFields;
              });
              const totalCriteriaImprovements = BLOCK_KEYS.reduce((n, bkk) => n + (evals[bkk]?.fields||[]).reduce((m, f) => m + (f.criteria||[]).filter(c=>!c.passed).length, 0), 0);
              const totalImprovements = totalCriteriaImprovements + emptyHighWeightBlocks.length;

              const BLOCK_WEIGHT_LABEL = { b1:'Produto (peso 20%)', b2:'Cliente-alvo (peso 45%)', b3:'Mercado (peso 10%)', b4:'Campanha (peso 25%)' };
              const BLOCK_WEIGHT_PTS   = { b1: Math.round(85*0.20), b2: Math.round(85*0.45), b3: Math.round(85*0.10), b4: Math.round(85*0.25) };

              return (
                <FormCard title="Avaliação de qualidade">
                  {/* Resumo actual → potencial */}
                  <div style={{ display: 'grid', gridTemplateColumns: '1fr auto 1fr', gap: 12, alignItems: 'center', marginBottom: 16, padding: '14px 18px', borderRadius: 10, background: 'var(--bg-sunken)', border: '1px solid var(--border)' }}>
                    <div style={{ textAlign: 'center' }}>
                      <div style={{ fontSize: 28, fontWeight: 700, color: scoreColor(score), fontFamily: 'var(--font-display)', lineHeight: 1 }}>{score}</div>
                      <div style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', marginTop: 4 }}>Score actual</div>
                      <div style={{ fontSize: 10, color: scoreColor(score), fontFamily: 'var(--font-body)', marginTop: 2, fontWeight: 600 }}>{scoreLabel(score)}</div>
                    </div>
                    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4 }}>
                      <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--text-dim)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>
                      <div style={{ fontSize: 9, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', textAlign: 'center', whiteSpace: 'nowrap' }}>{totalImprovements} melhorias{'\n'}disponíveis</div>
                    </div>
                    <div style={{ textAlign: 'center' }}>
                      <div style={{ fontSize: 28, fontWeight: 700, color: scoreColor(potentialGlobal), fontFamily: 'var(--font-display)', lineHeight: 1 }}>~{potentialGlobal}</div>
                      <div style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', marginTop: 4 }}>Score potencial</div>
                      <div style={{ fontSize: 10, color: scoreColor(potentialGlobal), fontFamily: 'var(--font-body)', marginTop: 2, fontWeight: 600 }}>{potentialGlobal >= 80 ? 'Pronto para submeter' : scoreLabel(potentialGlobal)}</div>
                    </div>
                  </div>

                  {/* Blocos realmente vazios → utilizador tem de preencher */}
                  {emptyHighWeightBlocks.map(bkk => (
                    <div key={bkk} style={{ display: 'flex', alignItems: 'flex-start', gap: 10, padding: '10px 14px', borderRadius: 8, background: 'color-mix(in oklch, var(--danger) 5%, transparent)', border: '1px solid color-mix(in oklch, var(--danger) 20%, transparent)', marginBottom: 8 }}>
                      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--danger)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0, marginTop: 1 }}><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
                      <div style={{ flex: 1 }}>
                        <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text)', marginBottom: 2 }}>
                          {{ b1:'Bloco 1 — Produto', b2:'Bloco 2 — Cliente-alvo', b3:'Bloco 3 — Mercado', b4:'Bloco 4 — Campanha' }[bkk]} está vazio — principal limitador do score
                        </div>
                        <div style={{ fontSize: 11, color: 'var(--text-muted)', lineHeight: 1.5 }}>
                          Com peso de {({b1:'20%',b2:'45%',b3:'10%',b4:'25%'})[bkk]}, preenchê-lo pode adicionar até <strong>+{BLOCK_WEIGHT_PTS[bkk]} pontos</strong> ao score.
                        </div>
                      </div>
                      {onNavigateToBlock && (
                        <button className="btn btn-ai" style={{ fontSize: 11, height: 28, padding: '0 12px', flexShrink: 0 }}
                          onClick={() => onNavigateToBlock(parseInt(bkk.slice(1), 10))}>
                          Ir ao bloco →
                        </button>
                      )}
                    </div>
                  ))}

                  {/* Blocos com conteúdo mas sem breakdown → avaliação incompleta */}

                  <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                    {BLOCK_KEYS.map(bKey => {
                      const isGuardrail = bKey === 'b5';
                      const blockScore  = bk[bKey]?.score ?? 0;
                      const blockEval   = evals[bKey];
                      const fields      = blockEval?.fields || [];
                      const failedCount = fields.reduce((n, f) => n + (f.criteria||[]).filter(c=>!c.passed).length, 0);
                      const isExpanded  = expandedBlock === bKey;
                      const bLabel      = { b1:'Bloco 1 — Produto', b2:'Bloco 2 — Cliente-alvo', b3:'Bloco 3 — Mercado', b4:'Bloco 4 — Campanha', b5:'Bloco 5 — Guardrails' }[bKey];

                      return (
                        <div key={bKey} style={{ border: `1px solid ${isGuardrail ? 'var(--border)' : 'var(--border)'}`, borderRadius: 10, overflow: 'hidden', opacity: isGuardrail ? 0.85 : 1 }}>
                          {/* Block header */}
                          <div onClick={() => setExpandedBlock(isExpanded ? null : bKey)} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 16px', cursor: 'pointer', background: isExpanded ? 'var(--bg-sunken)' : 'transparent' }}>
                            <div style={{ flex: 1 }}>
                              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                                <span style={{ fontSize: 12, fontWeight: 600, color: 'var(--text)' }}>{bLabel}</span>
                                {isGuardrail && <span style={{ fontSize: 9, fontFamily: 'var(--font-mono)', color: 'var(--text-dim)', background: 'var(--bg-sunken)', padding: '1px 5px', borderRadius: 3 }}>não conta para o score</span>}
                              </div>
                            </div>
                            {failedCount > 0 && <span style={{ fontSize: 10, fontFamily: 'var(--font-mono)', padding: '2px 8px', borderRadius: 99, background: 'var(--bg-sunken)', color: 'var(--text-muted)', fontWeight: 700, whiteSpace: 'nowrap' }}>{failedCount} {failedCount === 1 ? 'ponto' : 'pontos'} a melhorar</span>}
                            {failedCount === 0 && fields.length > 0 && <span style={{ fontSize: 10, fontFamily: 'var(--font-mono)', color: 'var(--ai-500, #3859D0)' }}>✓ Tudo bem</span>}
                            {!isGuardrail && (
                              <>
                                <div style={{ width: 80, height: 5, borderRadius: 99, background: 'var(--border)', overflow: 'hidden', flexShrink: 0 }}>
                                  <div style={{ width: `${blockScore}%`, height: '100%', background: scoreColor(blockScore), borderRadius: 99 }} />
                                </div>
                                <div style={{ textAlign: 'right', flexShrink: 0, minWidth: 70 }}>
                                  <div style={{ fontSize: 13, fontWeight: 700, color: scoreColor(blockScore), fontFamily: 'var(--font-mono)', lineHeight: 1 }}>{blockScore}</div>
                                  <div style={{ fontSize: 9, color: 'var(--text-dim)', fontFamily: 'var(--font-body)', marginTop: 1 }}>{scoreLabel(blockScore)}</div>
                                </div>
                              </>
                            )}
                            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="var(--text-dim)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0, transform: isExpanded ? 'rotate(180deg)' : 'none', transition: 'transform .15s' }}><polyline points="6 9 12 15 18 9"/></svg>
                          </div>

                          {/* Fields */}
                          {isExpanded && (
                            <div style={{ borderTop: '1px solid var(--border)', padding: '10px 16px', display: 'flex', flexDirection: 'column', gap: 6 }}>
                              {fields.length === 0 && <div style={{ fontSize: 11, color: 'var(--text-dim)', fontStyle: 'italic', padding: '4px 0' }}>Regenera a apreciação para ver o detalhe por campo.</div>}
                              {fields.map(fieldEval => {
                                const fKey   = `${bKey}.${fieldEval.field}`;
                                const fExp   = expandedField === fKey;
                                const fFailed = (fieldEval.criteria||[]).filter(c=>!c.passed);
                                const fPassed = (fieldEval.criteria||[]).filter(c=>c.passed);
                                const fVal   = form?.[`block${bKey.slice(1)}`]?.[fieldEval.field];
                                const fEmpty  = fieldEval.score === 0;
                                return (
                                  <div key={fieldEval.field} style={{ border: '1px solid var(--border)', borderRadius: 8, overflow: 'hidden', background: 'transparent' }}>
                                    <div onClick={() => setExpandedField(fExp ? null : fKey)} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '9px 12px', cursor: 'pointer', background: fExp ? 'var(--bg-sunken)' : 'transparent' }}>
                                      <div style={{ flex: 1, display: 'flex', alignItems: 'center', gap: 8 }}>
                                        <span style={{ fontSize: 12, color: 'var(--text)', fontWeight: fFailed.length > 0 ? 500 : 400 }}>{fieldLabel(fieldEval.field)}</span>
                                        {fEmpty && <span style={{ fontSize: 9, fontFamily: 'var(--font-mono)', color: 'var(--text-muted)', background: 'var(--bg-sunken)', padding: '1px 6px', borderRadius: 3 }}>Campo vazio</span>}
                                      </div>
                                      {fFailed.length > 0 && !fEmpty && <span style={{ fontSize: 10, color: scoreColor(fieldEval.score), fontFamily: 'var(--font-mono)', whiteSpace: 'nowrap' }}>{fFailed.length} {fFailed.length === 1 ? 'ponto' : 'pontos'} a melhorar</span>}
                                      {fFailed.length === 0 && <span style={{ fontSize: 10, color: 'var(--ai-500, #3859D0)', fontFamily: 'var(--font-mono)' }}>✓</span>}
                                      <div style={{ width: 50, height: 4, borderRadius: 99, background: 'var(--border)', overflow: 'hidden', flexShrink: 0 }}>
                                        <div style={{ width: `${fieldEval.score}%`, height: '100%', background: scoreColor(fieldEval.score), borderRadius: 99 }} />
                                      </div>
                                      <div style={{ fontSize: 11, fontWeight: 700, color: scoreColor(fieldEval.score), fontFamily: 'var(--font-mono)', width: 24, textAlign: 'right', flexShrink: 0 }}>{fieldEval.score}</div>
                                      {(fFailed.length > 0 || fPassed.length > 0) && <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="var(--text-dim)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0, transform: fExp ? 'rotate(180deg)' : 'none', transition: 'transform .15s' }}><polyline points="6 9 12 15 18 9"/></svg>}
                                    </div>
                                    {fExp && (
                                      <div style={{ borderTop: '1px solid var(--border)', padding: '10px 12px', display: 'flex', flexDirection: 'column', gap: 8 }}>
                                        {fPassed.length > 0 && (
                                          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
                                            {fPassed.map(c => <span key={c.id} style={{ fontSize: 9, fontFamily: 'var(--font-mono)', color: 'var(--ai-500, #3859D0)', background: 'color-mix(in srgb, var(--ai-500, #3859D0) 8%, transparent)', padding: '2px 7px', borderRadius: 99 }}>✓ {critLabel(c.id)}</span>)}
                                          </div>
                                        )}
                                        {fFailed.map(crit => (
                                          <div key={crit.id} style={{ padding: '10px 12px', background: 'var(--bg-sunken)', border: '1px solid var(--border)', borderRadius: 7 }}>
                                            <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--text)', marginBottom: 4 }}>{critLabel(crit.id)}</div>
                                            <div style={{ fontSize: 11, color: 'var(--text-muted)', lineHeight: 1.6 }}>{crit.feedback}</div>
                                          </div>
                                        ))}
                                        {fFailed.length > 0 && (() => {
                                          const fsk = `field_${bKey}_${fieldEval.field}`;
                                          const bm  = { b1:'block1', b2:'block2', b3:'block3', b4:'block4', b5:'block5' };
                                          const fcd = { block: bm[bKey], field: fieldEval.field, criteria_failed: fFailed.map(c=>({id:c.id,label:critLabel(c.id),feedback:c.feedback})), current_value: fVal };
                                          const alreadyImproved = improvedFields.has(`${bm[bKey]}.${fieldEval.field}`);
                                          if (alreadyImproved) return (
                                            <div style={{ display:'flex', alignItems:'center', gap:6, paddingTop:4, borderTop:'1px solid var(--border)', fontSize:11, color:'var(--ai-500,#3859D0)', fontFamily:'var(--font-mono)' }}>
                                              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
                                              Melhoria aplicada — reanalisar para confirmar
                                            </div>
                                          );
                                          return (
                                            <>
                                              <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', paddingTop: 4, borderTop: '1px solid var(--border)' }}>
                                                {onNavigateToBlock && <button className="btn" style={{ fontSize: 11, height: 28, padding: '0 12px' }} onClick={() => onNavigateToBlock(parseInt(bKey.slice(1),10))}>Editar manualmente</button>}
                                                <CriterionApplyBtn bKey={bKey} fieldName={fieldEval.field} critId="all" critLabel={fieldLabel(fieldEval.field)} feedback={fFailed.map(c=>c.feedback).join(' | ')} currentValue={fVal} overrideStateKey={fsk} overrideCriterionData={fcd} />
                                              </div>
                                              <CriterionReviewCard bKey={bKey} fieldName={fieldEval.field} critId="all" overrideStateKey={fsk} numCriteriaFixed={fFailed.length} />
                                            </>
                                          );
                                        })()}
                                      </div>
                                    )}
                                  </div>
                                );
                              })}
                            </div>
                          )}
                        </div>
                      );
                    })}
                  </div>
                </FormCard>
              );
            })()}

          </>
        );
      })()}
    </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 = {
  // Formato novo (CHANNELS_OPTS actuais)
  meta_ads:            { abbr: 'META' },
  linkedin_ads:        { abbr: 'LIN'  },
  google_ads_search:   { abbr: 'GGL'  },
  google_ads_display:  { abbr: 'DISP' },
  website:             { abbr: 'WEB'  },
  muppi_led:           { abbr: 'LED'  },
  // Formato antigo (retrocompatibilidade)
  email:               { abbr: 'EML'  },
  whatsapp:            { abbr: 'WA'   },
  linkedin:            { abbr: 'LIN'  },
  google_ads:          { abbr: 'GGL'  },
  facebook:            { abbr: 'FB'   },
  instagram:           { abbr: 'IG'   },
  youtube:             { abbr: 'YT'   },
  social:              { abbr: 'SOC'  },
  site:                { abbr: 'WEB'  },
  led:                 { abbr: 'LED'  },
  ads:                 { abbr: 'ADS'  },
};

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, onVerCampanha, onDuplicar, attentionColor, selected, onToggle, rowIndex, userRole, aiUsage }) => {
  const [menuOpen, setMenuOpen] = React.useState(false);
  const [hovered, setHovered] = React.useState(false);
  const [menuPos, setMenuPos] = React.useState({ top: 0, right: 0 });
  const menuRef     = React.useRef(null);
  const btnRef      = React.useRef(null);
  const dropdownRef = React.useRef(null);
  const pct = parseInt(b.completion_pct) || 0;
  const pctColor = pct >= 80 ? 'var(--ai-500, #3859D0)' : pct >= 40 ? 'var(--text-muted, #4a5e7a)' : 'var(--text-dim, #6b7fa3)';
  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) => {
      const inMenu     = menuRef.current     && menuRef.current.contains(e.target);
      const inDropdown = dropdownRef.current && dropdownRef.current.contains(e.target);
      if (!inMenu && !inDropdown) setMenuOpen(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 cfg = STATUS_CONFIG[b.status] || STATUS_CONFIG.rascunho;
          return <span style={{ fontSize: 11, fontFamily: 'var(--font-mono)', fontWeight: 600, padding: '2px 8px', borderRadius: 4, display: 'inline-block', color: cfg.color, background: cfg.bg }}>{cfg.label.toUpperCase()}</span>;
        })()}
      </td>
      {/* ── Qualidade ── */}
      <td style={{ padding: '10px 12px' }}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
          {b.quality_score != null ? (
            <span style={{ fontSize: 12, fontFamily: 'var(--font-mono)', fontWeight: 700, color: b.quality_score >= 80 ? 'var(--ai-500, #3859D0)' : b.quality_score >= 60 ? 'var(--text-muted, #4a5e7a)' : 'var(--text-dim, #6b7fa3)' }}>
              {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: pctColor }} />
              </div>
              <span style={{ fontSize: 10.5, fontFamily: 'var(--font-mono)', color: pctColor, whiteSpace: 'nowrap' }}>{pct}%</span>
            </div>
          )}
          {aiUsage && parseFloat(aiUsage.custo_eur) > 0 && (
            <span style={{ fontSize: 10, fontFamily: 'var(--font-mono)', color: 'var(--text-dim, #6b7fa3)', whiteSpace: 'nowrap' }}>
              €{parseFloat(aiUsage.custo_eur).toFixed(4).replace(/0+$/, '').replace(/\.$/, '')} · {Number(aiUsage.total_tokens).toLocaleString('pt-PT')} tok
            </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(--text)', fontWeight: 400, 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={{ display: 'inline-block' }}>
          <button
            ref={btnRef}
            onClick={() => {
              if (!menuOpen) {
                const r = btnRef.current.getBoundingClientRect();
                setMenuPos({ top: r.bottom + 4, right: window.innerWidth - r.right });
              }
              setMenuOpen(o => !o);
            }}
            style={{
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              width: 28, height: 28, borderRadius: 6, cursor: 'pointer',
              background: menuOpen ? 'rgba(0,0,0,0.06)' : 'transparent',
              border: 'none', outline: 'none', color: 'var(--text-muted)', transition: 'background 120ms ease',
              fontSize: 16, lineHeight: 1,
            }}
            onMouseEnter={e => { if (!menuOpen) e.currentTarget.style.background = 'rgba(0,0,0,0.06)'; }}
            onMouseLeave={e => { if (!menuOpen) e.currentTarget.style.background = 'transparent'; }}
          >⋮</button>

          {menuOpen && ReactDOM.createPortal(
            <div ref={dropdownRef} className="animate-in" style={{
              position: 'fixed', top: menuPos.top, right: menuPos.right, zIndex: 9999,
              background: '#ffffff', border: '1px solid var(--border, #ECEFF5)',
              borderRadius: 8, minWidth: 176, padding: 4,
              boxShadow: '0 4px 16px rgba(17,41,84,0.10)',
            }}>
              {(() => {
                const st       = b.status;
                const isAdmin  = userRole === 'admin' || userRole === 'board';
                const isEmCamp = st === 'em_campanha';
                const itemStyle   = { display: 'block', width: '100%', textAlign: 'left', background: 'transparent', border: 'none', padding: '8px 12px', borderRadius: 4, fontSize: 13, color: 'var(--text, #283252)', cursor: 'pointer', fontFamily: 'inherit', whiteSpace: 'nowrap' };
                const dangerStyle = { ...itemStyle, color: 'var(--danger, #DC2626)' };
                const onHover  = e => e.currentTarget.style.background = 'rgba(0,0,0,0.04)';
                const offHover = e => e.currentTarget.style.background = 'transparent';
                const onDanger = e => e.currentTarget.style.background = 'rgba(220,38,38,0.06)';

                if (isEmCamp) {
                  // Em Campanha: Ver Campanha · Duplicar · Apagar
                  return (
                    <>
                      {onVerCampanha && (
                        <button onClick={() => { setMenuOpen(false); onVerCampanha(b); }} style={itemStyle} onMouseEnter={onHover} onMouseLeave={offHover}>
                          Ver Campanha
                        </button>
                      )}
                      {onDuplicar && (
                        <button onClick={() => { setMenuOpen(false); onDuplicar(b); }} style={itemStyle} onMouseEnter={onHover} onMouseLeave={offHover}>
                          Duplicar
                        </button>
                      )}
                      <div style={{ height: 1, background: 'var(--border, #ECEFF5)', margin: '4px 0' }} />
                      <button onClick={() => { setMenuOpen(false); onDelete(); }} style={dangerStyle} onMouseEnter={onDanger} onMouseLeave={offHover}>
                        Apagar
                      </button>
                    </>
                  );
                }

                // Outros estados: menu normal
                const canSubmit   = st === 'rascunho' || st === 'rejeitado';
                const canCampanha = st === 'aprovado' && !!onCriarCampanha;
                const canDelete   = isAdmin || st === 'rascunho' || st === 'rejeitado';
                return (
                  <>
                    <button onClick={() => { setMenuOpen(false); onEdit(); }} style={itemStyle} onMouseEnter={onHover} onMouseLeave={offHover}>
                      Editar
                    </button>
                    {onDuplicar && (
                      <button onClick={() => { setMenuOpen(false); onDuplicar(b); }} style={itemStyle} onMouseEnter={onHover} onMouseLeave={offHover}>
                        Duplicar
                      </button>
                    )}
                    {canSubmit && onSubmit && (
                      <button onClick={() => { setMenuOpen(false); onSubmit(b); }} style={itemStyle} onMouseEnter={onHover} onMouseLeave={offHover}>
                        {st === 'rejeitado' ? 'Submeter novamente' : 'Submeter'}
                      </button>
                    )}
                    {canCampanha && (
                      <button onClick={() => { setMenuOpen(false); onCriarCampanha(b); }} style={itemStyle} onMouseEnter={onHover} onMouseLeave={offHover}>
                        Criar Campanha
                      </button>
                    )}
                    {canDelete && (
                      <>
                        <div style={{ height: 1, background: 'var(--border, #ECEFF5)', margin: '4px 0' }} />
                        <button onClick={() => { setMenuOpen(false); onDelete(); }} style={dangerStyle} onMouseEnter={onDanger} onMouseLeave={offHover}>
                          Apagar
                        </button>
                      </>
                    )}
                  </>
                );
              })()}
            </div>,
            document.body
          )}
        </div>
      </td>

    </tr>
  );
};

// ─── Digi AI Banner ────────────────────────────────────────────────────────────
const DigiAIBanner = ({ kpi, userRole, userName, onFilter, briefings = [] }) => {
  const hour = new Date().getHours();
  const [pickSeed] = React.useState(Math.random);

  const { heading, body } = 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 n = firstName || 'Olá';
    // ── Grupos por estado ──────────────────────────────────────────────────────
    const G = {
      rascunho:    briefings.filter(b => b.status === 'rascunho'),
      submetido:   briefings.filter(b => ['submetido','validado_manager','validado_ceo'].includes(b.status)),
      aprovado:    briefings.filter(b => b.status === 'aprovado'),
      rejeitado:   briefings.filter(b => b.status === 'rejeitado'),
      emCampanha:  briefings.filter(b => b.status === 'em_campanha'),
    };
    const marcas = (arr) => {
      const ms = [...new Set(arr.map(b => b.brand_name || b.marca || '').filter(Boolean))];
      if (!ms.length) return '';
      if (ms.length === 1) return ms[0];
      if (ms.length === 2) return `${ms[0]} e ${ms[1]}`;
      return `${ms.slice(0,-1).join(', ')} e ${ms[ms.length-1]}`;
    };

    // ── Heading com humor — contextual ao estado real ───────────────────────────
    let hPool;
    if (isBoard) {
      if (kpi.submitted > 0)
        hPool = [`${n}, ${pl(kpi.submitted,'um briefing aguarda','${kpi.submitted} briefings aguardam')} a tua aprovação. A Digi AI só arranca com o teu OK.`.replace('${kpi.submitted}', kpi.submitted),
          `${n}, a equipa submeteu ${kpi.submitted} ${pl(kpi.submitted,'briefing','briefings')} para aprovação.`,
          `${n}, tens ${kpi.submitted} ${pl(kpi.submitted,'briefing','briefings')} à espera do teu go.`];
      else if (kpi.total === 0)
        hPool = [`${n}, ainda não há briefings criados. Quando a equipa começar, vês tudo aqui.`];
      else
        hPool = [`${n}, sem aprovações pendentes. Tudo em ordem.`, `${n}, pipeline limpo da tua parte.`];
    } else {
      if (kpi.rejected > 0)
        hPool = [`${n}, o board devolveu o trabalho de casa. Há revisões para fazer.`, `${n}, ${kpi.rejected} ${pl(kpi.rejected,'briefing foi rejeitado','briefings foram rejeitados')}. Corrige e resubmete.`];
      else if (kpi.approved > 0 && kpi.draft === 0 && kpi.submitted === 0)
        hPool = [`${n}, a Digi AI está à tua espera. ${kpi.approved} ${pl(kpi.approved,'briefing aprovado','briefings aprovados')} prontos para gerar conteúdo.`,
          `${n}, tudo aprovado. A Digi AI pode arrancar assim que abrires.`];
      else if (kpi.draft > 0)
        hPool = [`${n}, o briefing não se escreve sozinho — tens ${kpi.draft} ${pl(kpi.draft,'rascunho','rascunhos')} por completar.`,
          `${n}, quanto mais sólido o briefing, melhor o que a Digi AI produz.`,
          `${n}, ${kpi.draft} ${pl(kpi.draft,'rascunho incompleto','rascunhos incompletos')} à espera. A Digi AI fica parada enquanto não estiverem prontos.`];
      else if (kpi.total === 0)
        hPool = [`${n}, pipeline vazio. Cria o primeiro briefing e dá ordem à Digi AI para trabalhar.`, `${n}, ainda não há briefings. Começa por criar um.`];
      else
        hPool = [`${n}, ${kpi.total} ${pl(kpi.total,'briefing activo','briefings activos')} no pipeline.`, `${n}, vê o estado do pipeline.`];
    }
    const heading = hPool[Math.floor(pickSeed * hPool.length)];

    // ── Corpo — parágrafo fluido e humano sobre todos os briefings ───────────────
    const partes = [];
    if (briefings.length === 0) {
      partes.push(isBoard
        ? 'Ainda não há briefings submetidos para aprovação. Quando a equipa submeter, aparecem aqui.'
        : 'Ainda não há briefings criados. Começa por criar o primeiro — a Digi AI usa-o como base para gerar todo o conteúdo desta marca.');
    } else {
      if (!isBoard) {
        if (G.rejeitado.length) partes.push(`${marcas(G.rejeitado)} ${pl(G.rejeitado.length,'foi rejeitado','foram rejeitados')} pelo board — revê o feedback, corrige os pontos assinalados e resubmete.`);
        if (G.aprovado.length)  partes.push(`${marcas(G.aprovado)} ${pl(G.aprovado.length,'está aprovado e pronto','estão aprovados e prontos')} para a Digi AI gerar conteúdo — abre e clica em Gerar Conteúdo.`);
        if (G.submetido.length) partes.push(`${marcas(G.submetido)} ${pl(G.submetido.length,'foi submetido','foram submetidos')} e ${pl(G.submetido.length,'aguarda','aguardam')} aprovação do board.`);
        if (G.rascunho.length)  partes.push(`${marcas(G.rascunho)} ${pl(G.rascunho.length,'tem rascunho por completar','têm rascunhos por completar')} — quanto mais sólido o briefing, melhor o conteúdo que a Digi AI vai gerar.`);
        if (G.emCampanha.length) partes.push(`${marcas(G.emCampanha)} já ${pl(G.emCampanha.length,'tem campanha activa','têm campanhas activas')} — a Digi AI está a trabalhar.`);
      } else {
        if (G.submetido.length) partes.push(`${marcas(G.submetido)} ${pl(G.submetido.length,'aguarda','aguardam')} a tua aprovação — a Digi AI só arranca assim que deres o go.`);
        if (G.aprovado.length)  partes.push(`${marcas(G.aprovado)} ${pl(G.aprovado.length,'foi aprovado por ti','foram aprovados por ti')} e ${pl(G.aprovado.length,'está','estão')} em produção de conteúdo.`);
        if (G.emCampanha.length) partes.push(`${marcas(G.emCampanha)} já ${pl(G.emCampanha.length,'tem campanha activa','têm campanhas activas')}.`);
        if (G.rejeitado.length) partes.push(`${marcas(G.rejeitado)} ${pl(G.rejeitado.length,'foi devolvido','foram devolvidos')} para revisão.`);
        if (G.rascunho.length)  partes.push(`${marcas(G.rascunho)} ainda ${pl(G.rascunho.length,'está em rascunho','estão em rascunho')} — quando submetido, vem para a tua aprovação.`);
      }
    }
    const body = partes.join(' ');

    return { heading, body };
  }, [kpi, briefings, userRole, userName, hour, pickSeed]);

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

// ─── 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 stageIdx   = required.findIndex(r => r.role === role) + 1;
          const roleNames  = { mkt_manager: 'Manager', brand_dir: 'CEO', rl: 'Chairman' };
          const stageLabel = `Fase ${stageIdx} · ${roleNames[role] || role}`;

          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.created_at && (
                      <> · {new Date(aprov.created_at).toLocaleString('pt-PT', { day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' })}</>
                    )}
                    {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>
  );
};

// ── Briefing Rápido — placeholders por marca (ordem: objectivo, mensagem, público, restrições) ──
const BR_PLACEHOLDERS = {
  mimaki: [
    'ex: Gerar leads qualificados para demonstração técnica. Posicionar a Mimaki como a escolha certa para gráficas que querem crescer.',
    'ex: Produz mais, em menos espaço, com a tecnologia Mimaki.',
    'ex: Directores de produção e responsáveis de compras de gráficas médias e grandes em Portugal e Espanha.',
    'ex: Não comparar directamente com Roland ou Epson. Evitar promessas de preço sem contexto técnico.',
  ],
  biond: [
    'ex: Aumentar a notoriedade dos filmes BIOND junto de impressoras e distribuidores. Posicionar como a alternativa sustentável ao PVC.',
    'ex: Imprime com consciência. BIOND é o vinil que cuida do planeta.',
    'ex: Responsáveis de compras e gestores de produto de empresas de sinalização com compromissos ESG.',
    'ex: Não focar excessivamente nas certificações técnicas. Não comparar directamente com 3M ou Avery.',
  ],
  decal: [
    'ex: Gerar leads de distribuidores e aplicadores para a gama Decal. Reforçar a posição como fornecedor de referência em Portugal.',
    'ex: O vinil que trabalha tão bem quanto tu.',
    'ex: Aplicadores profissionais e gestores de compras de empresas de sinalização e decoração exterior.',
    'ex: Não mencionar preços. Evitar comparações directas com Orafal ou Avery sem dados de suporte.',
  ],
  digidelta: [
    'ex: Comunicar ao mercado uma nova solução, parceria ou marco da Digidelta. Reforçar a posição como distribuidor de referência.',
    'ex: A Digidelta: o parceiro que te dá acesso ao melhor do mercado de impressão digital.',
    'ex: Clientes actuais e potenciais da Digidelta em Portugal e Espanha. Integradores e parceiros do sector.',
    'ex: Não apresentar como simples revendedor. Evitar linguagem demasiado técnica sem contexto de negócio.',
  ],
  netscreen: [
    'ex: Anunciar a parceria com a Fabulux e posicionar a NetScreen como a empresa mais forte e preparada no mercado LED profissional em Portugal.',
    'ex: A NetScreen está mais forte do que nunca.',
    'ex: Clientes actuais, potenciais clientes, integradores AV, entidades públicas e comunicação social.',
    'ex: Não apresentar como novo fornecedor. A Fabulux não é o protagonista. A NetScreen não depende da Fabulux.',
  ],
  sensek: [
    'ex: Posicionar o Sensek como o sistema de personalização têxtil mais acessível para negócios de bordado e transfer digital.',
    'ex: O teu negócio têxtil começa aqui.',
    'ex: Proprietários de pequenos negócios têxteis, bordadeiras e empresas de personalização de vestuário.',
    'ex: Não comparar directamente com Brother ou Tajima. Evitar linguagem demasiado industrial.',
  ],
  alldecor: [
    'ex: Aumentar o tráfego ao site AllDecor e gerar pedidos de instalação. Posicionar como a escolha premium de decoração bio-based.',
    'ex: Transforma o teu espaço num fim de semana.',
    'ex: Proprietários de casa, designers de interiores e gestores de espaços comerciais em Portugal.',
    'ex: Não focar apenas na sustentabilidade — o design e a facilidade de aplicação são o argumento principal.',
  ],
};

// ── Briefing Rápido — perguntas sequenciais ───────────────────────────────────
const BR_PERGUNTAS = [
  {
    bloco: 'Objectivo', label: 'B1',
    pergunta: 'Qual é o objectivo desta campanha?',
    hint: 'O que queres que aconteça depois de as pessoas verem isto?',
    placeholder: 'ex: Anunciar a parceria com a Fabulux e posicionar a NetScreen como a empresa mais forte e preparada no mercado LED profissional em Portugal.',
  },
  {
    bloco: 'Mensagem', label: 'B2',
    pergunta: 'Se tivesses 10 segundos, o que dirias?',
    hint: 'A frase ou ideia central da campanha.',
    placeholder: 'ex: A NetScreen está mais forte do que nunca.',
  },
  {
    bloco: 'Público', label: 'B3',
    pergunta: 'A quem se dirige esta campanha?',
    hint: 'Quem são as pessoas que precisam de ver isto?',
    placeholder: 'ex: Clientes actuais, potenciais clientes, integradores AV, entidades públicas e comunicação social.',
  },
  {
    bloco: 'Restrições', label: 'B4',
    pergunta: 'O que esta campanha NÃO deve comunicar ou parecer?',
    hint: 'Erros a evitar, mensagens incorrectas, percepções indesejadas — pode deixar em branco.',
    placeholder: 'ex: Não apresentar como novo fornecedor. A Fabulux não é o protagonista. A NetScreen não depende da Fabulux.',
  },
];

const BriefingRapidoModal = ({ allBrands, onDone, onClose }) => {
  const [selectedBrand, setSelectedBrand] = React.useState('');
  const [currentQ,      setCurrentQ]      = React.useState(-1); // -1 = seleccionar marca
  const [respostas,     setRespostas]      = React.useState({});
  const [generating,    setGenerating]     = React.useState(false);
  const [genStep,       setGenStep]        = React.useState('');
  const [genLog,        setGenLog]         = React.useState([]);
  const [genPct,        setGenPct]         = React.useState(0);
  const [error,         setError]          = React.useState('');

  const total     = BR_PERGUNTAS.length;
  const pct       = currentQ < 0 ? 0 : Math.round(((currentQ + 1) / total) * 100);
  const isLast    = currentQ === total - 1;
  const pergunta  = currentQ >= 0 ? BR_PERGUNTAS[currentQ] : null;

  const handleGerar = async () => {
    setGenerating(true); setError(''); setGenLog([]); setGenPct(0); setGenStep('A analisar as respostas...');
    const descricao = BR_PERGUNTAS.map((p, i) =>
      `BLOCO ${p.label} — ${p.bloco.toUpperCase()}: "${respostas[i] || '(sem resposta)'}"`
    ).join('\n');
    try {
      const res = await fetch('/api/marketing/briefings/rapido', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ descricao, brand_name: selectedBrand }),
      });
      const reader = res.body.getReader(); const decoder = new TextDecoder(); let buf = '';
      let _lastStepAt = 0;
      while (true) {
        const { done, value } = await reader.read();
        if (value) {
          buf += decoder.decode(value, { stream: !done });
          const lines = buf.split('\n'); buf = done ? '' : (lines.pop() ?? '');
          for (const line of lines) {
            if (!line.startsWith('data: ')) continue;
            let evt; try { evt = JSON.parse(line.slice(6)); } catch { continue; }
            if (evt.type === 'status') {
              const wait = 500 - (Date.now() - _lastStepAt);
              if (wait > 0 && _lastStepAt > 0) await new Promise(r => setTimeout(r, wait));
              setGenLog(l => [...l, evt.message]); setGenStep(evt.message);
              if (evt.step) setGenPct(Math.round((evt.step / 3) * 100));
              _lastStepAt = Date.now();
            }
            else if (evt.type === 'done') setTimeout(() => onDone(evt.data, selectedBrand), 700);
            else if (evt.type === 'error') throw new Error(evt.message);
          }
        }
        if (done) break;
      }
    } catch (e) {
      setError(e.message || 'Erro inesperado');
      setGenerating(false);
    }
  };

  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24 }}>
      <div style={{ background: '#fff', borderRadius: 14, width: '100%', maxWidth: 560, boxShadow: '0 24px 80px rgba(0,0,0,.2)', display: 'flex', flexDirection: 'column' }}>

        {/* Header */}
        <div style={{ padding: '18px 24px 14px', borderBottom: '1px solid #f1f5f9' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: currentQ >= 0 ? 12 : 0 }}>
            <div style={{ width: 30, height: 30, borderRadius: 8, background: 'rgba(56,89,208,.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
              <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="#3859D0" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
            </div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 14, fontWeight: 700, color: '#1d2e38', fontFamily: 'Montserrat, sans-serif' }}>Briefing Rápido</div>
              {currentQ >= 0 && !generating && (
                <div style={{ fontSize: 11, color: '#94a3b8' }}>
                  Pergunta {currentQ + 1} de {total}
                  {selectedBrand && <span style={{ marginLeft: 8, color: '#3859D0', fontWeight: 600 }}>{allBrands?.find(b=>b.slug===selectedBrand)?.name}</span>}
                </div>
              )}
            </div>
            {!generating && <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 16, color: '#94a3b8', padding: 4, lineHeight: 1 }}>✕</button>}
          </div>
          {/* Barra de progresso */}
          {currentQ >= 0 && !generating && (
            <div style={{ height: 4, background: '#f1f5f9', borderRadius: 99, overflow: 'hidden' }}>
              <div style={{ height: '100%', background: '#3859D0', borderRadius: 99, width: `${pct}%`, transition: 'width .3s' }} />
            </div>
          )}
        </div>

        {/* Body */}
        <div style={{ padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 16, minHeight: 220 }}>

          {/* Seleccionar marca */}
          {currentQ < 0 && !generating && (
            <>
              <div style={{ fontSize: 13, color: '#475569', lineHeight: 1.6 }}>
                Vou fazer-te <strong>4 perguntas rápidas</strong>.<br/>
                A Digi AI preenche o resto com base nas tuas respostas.
              </div>
              {allBrands?.length > 0 && (
                <div>
                  <label style={{ fontSize: 11, fontWeight: 600, color: '#64748b', letterSpacing: '0.04em', display: 'block', marginBottom: 6 }}>MARCA</label>
                  <select value={selectedBrand} onChange={e => setSelectedBrand(e.target.value)} style={{ width: '100%', padding: '8px 10px', fontSize: 13, border: '1px solid #e2e8f0', borderRadius: 7, background: '#fff', color: '#1d2e38', outline: 'none' }}>
                    <option value="">Seleccionar marca...</option>
                    {allBrands.map(b => <option key={b.slug} value={b.slug}>{b.name}</option>)}
                  </select>
                </div>
              )}
            </>
          )}

          {/* Pergunta actual */}
          {currentQ >= 0 && !generating && pergunta && (
            <>
              <div>
                <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#3859D0', fontFamily: 'monospace', marginBottom: 6 }}>
                  {pergunta.label} — {pergunta.bloco}
                </div>
                <div style={{ fontSize: 14, fontWeight: 600, color: '#1d2e38', fontFamily: 'Montserrat, sans-serif', lineHeight: 1.4, marginBottom: 4 }}>
                  {pergunta.pergunta}
                </div>
                <div style={{ fontSize: 11, color: '#94a3b8' }}>{pergunta.hint}</div>
              </div>
              <textarea
                autoFocus
                value={respostas[currentQ] || ''}
                onChange={e => setRespostas(r => ({ ...r, [currentQ]: e.target.value }))}
                rows={5}
                placeholder={(BR_PLACEHOLDERS[selectedBrand] || [])[currentQ] || pergunta.placeholder}
                style={{ width: '100%', padding: '10px 12px', fontSize: 13, border: '1px solid #e2e8f0', borderRadius: 8, resize: 'vertical', fontFamily: 'Inter, sans-serif', lineHeight: 1.6, outline: 'none', boxSizing: 'border-box', color: '#1d2e38' }}
              />
              <div style={{ fontSize: 10, color: '#94a3b8' }}>
                Podes deixar em branco — a AI preenche com base no contexto da marca e das outras respostas.
              </div>
            </>
          )}

          {/* Generating */}
          {generating && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 16, padding: '8px 0' }}>
              <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
              {/* Header Digi AI */}
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <div style={{ width: 32, height: 32, borderRadius: 8, background: 'rgba(56,89,208,.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#3859D0" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
                </div>
                <div>
                  <div style={{ fontSize: 13, fontWeight: 700, color: '#1d2e38', fontFamily: 'Montserrat, sans-serif' }}>Digi AI</div>
                  <div style={{ fontSize: 11, color: '#94a3b8' }}>a preencher o briefing...</div>
                </div>
                <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 8, flexShrink: 0 }}>
                <span style={{ fontSize: 11, fontWeight: 600, color: '#3859D0', fontFamily: 'monospace' }}>{genPct}%</span>
                <div style={{ width: 18, height: 18, borderRadius: '50%', border: '2px solid #e2e8f0', borderTopColor: '#3859D0', animation: 'spin 0.8s linear infinite' }} />
              </div>
              </div>
              {/* Log de fases */}
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                {genLog.map((msg, i) => (
                  <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                    <div style={{ width: 18, height: 18, borderRadius: '50%', background: i < genLog.length - 1 ? 'rgba(16,185,129,.15)' : 'rgba(56,89,208,.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                      {i < genLog.length - 1
                        ? <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#10b981" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
                        : <div style={{ width: 6, height: 6, borderRadius: '50%', background: '#3859D0' }} />
                      }
                    </div>
                    <span style={{ fontSize: 12, color: i < genLog.length - 1 ? '#94a3b8' : '#1d2e38', fontWeight: i < genLog.length - 1 ? 400 : 500 }}>{msg}</span>
                  </div>
                ))}
                {genLog.length === 0 && (
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                    <div style={{ width: 18, height: 18, borderRadius: '50%', background: 'rgba(56,89,208,.1)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                      <div style={{ width: 6, height: 6, borderRadius: '50%', background: '#3859D0' }} />
                    </div>
                    <span style={{ fontSize: 12, color: '#1d2e38', fontWeight: 500 }}>A analisar as respostas...</span>
                  </div>
                )}
              </div>
            </div>
          )}

          {error && <div style={{ fontSize: 12, color: '#ef4444', background: 'rgba(239,68,68,.06)', borderRadius: 6, padding: '8px 12px' }}>{error}</div>}
        </div>

        {/* Footer */}
        {!generating && (
          <div style={{ padding: '14px 24px', borderTop: '1px solid #f1f5f9', display: 'flex', gap: 8, justifyContent: 'space-between', alignItems: 'center' }}>
            <button onClick={onClose} className="btn" style={{ fontSize: 12 }}>Cancelar</button>
            <div style={{ display: 'flex', gap: 8 }}>
              {currentQ >= 0 && (
                <button onClick={() => setCurrentQ(q => q - 1)} className="btn" style={{ fontSize: 12 }}>Voltar</button>
              )}
              {currentQ >= 0 && !isLast && (
                <button onClick={() => setCurrentQ(q => q + 1)} className="btn btn-ai" style={{ fontSize: 12 }}>Seguinte</button>
              )}
              {currentQ >= 0 && isLast && (
                <button onClick={handleGerar} className="btn btn-ai" style={{ fontSize: 12 }}>Gerar Briefing</button>
              )}
              {currentQ < 0 && (
                <button onClick={() => setCurrentQ(0)} disabled={!selectedBrand && (allBrands?.length > 0)} className="btn btn-ai" style={{ fontSize: 12 }}>Iniciar</button>
              )}
            </div>
          </div>
        )}
      </div>
    </div>
  );
};

// ─── Modal de confirmação de eliminação ───────────────────────────────────────
const DeleteConfirmModal = ({ pendingDelete, onConfirm, onClose }) => {
  const [confirmText, setConfirmText] = React.useState('');
  const canConfirm = confirmText === 'DELETE';
  const count  = pendingDelete?.ids?.length || 0;
  const labels = pendingDelete?.labels || [];
  const isBulk = count > 1;
  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24 }}>
      <div style={{ background: '#fff', borderRadius: 14, width: '100%', maxWidth: 480, boxShadow: '0 24px 80px rgba(0,0,0,.2)', display: 'flex', flexDirection: 'column' }}>

        {/* Header */}
        <div style={{ padding: '18px 24px 14px', borderBottom: '1px solid #f1f5f9' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <div style={{ width: 30, height: 30, borderRadius: 8, background: 'rgba(220,38,38,.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#dc2626" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/>
                <path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 011-1h4a1 1 0 011 1v2"/>
              </svg>
            </div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 14, fontWeight: 700, color: '#1d2e38', fontFamily: 'Montserrat, sans-serif' }}>
                {isBulk ? `Eliminar ${count} briefings` : 'Eliminar briefing'}
              </div>
              <div style={{ fontSize: 11, color: '#94a3b8' }}>Esta acção é permanente e não pode ser revertida</div>
            </div>
            <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#94a3b8', padding: 4, lineHeight: 1, display: 'flex' }}>
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
            </button>
          </div>
        </div>

        {/* Body */}
        <div style={{ padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 16 }}>
          <div style={{ borderRadius: 8, background: 'rgba(220,38,38,.04)', border: '1px solid rgba(220,38,38,.15)', padding: '10px 14px' }}>
            {labels.slice(0, 3).map((label, i) => (
              <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '3px 0' }}>
                <div style={{ width: 4, height: 4, borderRadius: '50%', background: '#dc2626', flexShrink: 0 }} />
                <span style={{ fontSize: 12, color: '#1d2e38', fontWeight: 500 }}>{label}</span>
              </div>
            ))}
            {labels.length > 3 && (
              <div style={{ fontSize: 11, color: '#94a3b8', marginTop: 4, paddingLeft: 12 }}>+{labels.length - 3} mais</div>
            )}
          </div>
          <div>
            <label style={{ fontSize: 11, fontWeight: 600, color: '#64748b', letterSpacing: '0.04em', display: 'block', marginBottom: 6 }}>
              ESCREVE{' '}
              <span style={{ fontFamily: 'monospace', color: '#dc2626', background: 'rgba(220,38,38,.08)', padding: '1px 5px', borderRadius: 3 }}>DELETE</span>
              {' '}PARA CONFIRMAR
            </label>
            <input
              autoFocus type="text" value={confirmText}
              onChange={e => setConfirmText(e.target.value)}
              placeholder="DELETE"
              onKeyDown={e => e.key === 'Enter' && canConfirm && onConfirm()}
              style={{
                width: '100%', boxSizing: 'border-box', padding: '9px 12px', borderRadius: 8, fontSize: 13,
                border: `1px solid ${canConfirm ? 'rgba(220,38,38,.4)' : '#e2e8f0'}`,
                outline: 'none', fontFamily: 'Inter, sans-serif', color: '#1d2e38',
                letterSpacing: '0.05em', transition: 'border-color 0.15s',
              }}
            />
          </div>
        </div>

        {/* Footer */}
        <div style={{ padding: '14px 24px', borderTop: '1px solid #f1f5f9', display: 'flex', gap: 8, justifyContent: 'space-between' }}>
          <button onClick={onClose} className="btn" style={{ fontSize: 12 }}>Cancelar</button>
          <button onClick={onConfirm} disabled={!canConfirm} className="btn"
            style={{ fontSize: 12, background: canConfirm ? '#dc2626' : '#f1f5f9', color: canConfirm ? '#fff' : '#94a3b8', borderColor: canConfirm ? '#dc2626' : '#e2e8f0', cursor: canConfirm ? 'pointer' : 'default', transition: 'all 0.15s' }}>
            {isBulk ? `Eliminar ${count} briefings` : 'Eliminar briefing'}
          </button>
        </div>
      </div>
    </div>
  );
};

const BriefingsList = ({ brandId, onSelect, onNew, onEdit, onImport, onGenerateStrategy, onCriarCampanha, onVerCampanha, onDuplicar, 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 [showRapido, setShowRapido] = React.useState(false);
  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 [aiUsage, setAiUsage]           = React.useState(null);
  const [aiUsageMap, setAiUsageMap]     = React.useState({});

  const loadAiUsage = React.useCallback((bId, dRange) => {
    const p = new URLSearchParams();
    if (bId && bId !== 'todas') p.set('brand', bId);
    if (dRange && dRange.preset !== 'all') {
      p.set('date_from', dRange.start.toISOString().slice(0, 10));
      p.set('date_to',   dRange.end.toISOString().slice(0, 10));
    }
    const qs = p.toString() ? '?' + p.toString() : '';
    fetch(`/api/marketing/api-usage-summary${qs}`)
      .then(r => r.ok ? r.json() : null).then(d => setAiUsage(d || null)).catch(() => setAiUsage(null));
    fetch(`/api/marketing/api-usage-by-briefing${qs}`)
      .then(r => r.ok ? r.json() : [])
      .then(rows => {
        const map = {};
        (rows || []).forEach(r => { map[r.briefing_id] = r; });
        setAiUsageMap(map);
      }).catch(() => setAiUsageMap({}));
  }, []);

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

  React.useEffect(load, [load]);

  const [showDeleteModal, setShowDeleteModal] = React.useState(false);
  const [pendingDelete, setPendingDelete]     = React.useState(null);

  const handleDelete = (id, label) => {
    setPendingDelete({ ids: [id], labels: [label || `Briefing #${id}`] });
    setShowDeleteModal(true);
  };

  const handleDeleteConfirm = async () => {
    if (!pendingDelete) return;
    await Promise.all(pendingDelete.ids.map(id => window.MktBriefingsAPI.remove(id)));
    if (pendingDelete.ids.length > 1) clearSelection();
    setShowDeleteModal(false);
    setPendingDelete(null);
    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, mktUser.email);
      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_manager','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_manager: byStatus('validado_manager'), 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_manager','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_manager','validado_ceo','aprovado','em_campanha'].includes(b.status)) &&
      (!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 = () => {
    const ids    = [...selectedIds];
    const labels = ids.map(id => { const b = (briefings || []).find(x => x.id === id); return b?.commercial_name || b?.product_name || `Briefing #${id}`; });
    setPendingDelete({ ids, labels });
    setShowDeleteModal(true);
  };

  const handleBulkStatus = async (status) => {
    setBulkStatusOpen(false);
    try {
      await Promise.all([...selectedIds].map(id =>
        status === 'submetido' ? window.MktBriefingsAPI.submit(id, mktUser.email) : 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_manager + kpi.validado_ceo;
  const kpiCards = [
    { label: 'Pendentes',   value: kpiPendentes,    accent: 'var(--text-muted, #4a5e7a)', fill: kpi.total ? kpiPendentes    / kpi.total : 0, filter: 'pendentes'   },
    { label: 'Aprovados',   value: kpi.approved,    accent: 'var(--ai-500, #3859D0)',     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(--text-muted, #4a5e7a)', fill: 1,                                           filter: 'all'         },
  ];

  const attnCount  = attentionIds.size;
  const attnColor  = 'var(--ai-500, #3859D0)';
  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 data-theme="light" style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', '--bg': '#f8fafc', '--bg-elev': '#ffffff', '--bg-hover': '#f1f5f9', '--bg-sunken': '#e8edf5', '--border': '#dde3ef', '--border-strong': '#bec9de', '--text': '#112954', '--text-muted': '#4a5e7a', '--text-dim': '#6b7fa3', '--ai-500': '#3859D0' }}>

      {showRapido && (
        <BriefingRapidoModal
          allBrands={allBrands}
          onClose={() => setShowRapido(false)}
          onDone={(data, brandSlug, tipoCampanha) => {
            setShowRapido(false);
            onNew?.({ _prefill: data, _brand: brandSlug, _tipo: tipoCampanha });
          }}
        />
      )}

      {showDeleteModal && (
        <DeleteConfirmModal
          pendingDelete={pendingDelete}
          onConfirm={handleDeleteConfirm}
          onClose={() => { setShowDeleteModal(false); setPendingDelete(null); }}
        />
      )}

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

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

        {/* Title + actions */}
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16 }}>
          <div>
            <h1 style={{ margin: 0, fontSize: 21, fontWeight: 700, color: 'var(--text)', fontFamily: 'var(--font-display)', letterSpacing: '-0.01em', lineHeight: 1.2 }}>
              Briefings
            </h1>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6, flexWrap: 'wrap' }}>
              <span style={{ fontSize: 11, color: 'var(--text-muted)', fontFamily: 'var(--font-body)' }}>
                {kpi.total} briefing{kpi.total !== 1 ? 's' : ''}{isAll ? ' · todas as marcas' : (briefings && briefings[0] ? ` · ${briefings[0].brand_name}` : '')}
              </span>
              {kpi.total > 0 && (() => {
                const pct = Math.round((kpi.approved + kpi.em_campanha) / kpi.total * 100);
                return (
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                    <div style={{ width: 60, height: 4, borderRadius: 99, background: 'var(--border)', overflow: 'hidden' }}>
                      <div style={{ width: `${pct}%`, height: '100%', borderRadius: 99, background: 'var(--ai-500, #3859D0)', transition: 'width 0.4s ease' }} />
                    </div>
                    <span style={{ fontSize: 11, color: 'var(--text-muted)', fontFamily: 'var(--font-body)' }}>{pct}% aprovados</span>
                  </div>
                );
              })()}
            </div>
          </div>
          <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexShrink: 0, marginTop: 2 }}>
            <button className="btn" style={{ height: 30, padding: '0 14px', fontSize: 12 }} onClick={onImport} title="Importar PDF ou DOCX para preencher o briefing automaticamente com IA">Importar</button>
            <button className="btn" style={{ height: 30, padding: '0 14px', fontSize: 12, color: '#3859D0', borderColor: 'rgba(56,89,208,.3)' }} onClick={() => setShowRapido(true)}>Briefing Rápido</button>
            <button className="btn btn-ai" style={{ height: 30, padding: '0 14px', fontSize: 12 }} onClick={onNew} data-tutorial-step="select_brand_product">+ Criar</button>
          </div>
        </div>

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

        {/* Brand tabs */}
        <div style={{ display: 'flex', gap: 0, overflowX: 'auto' }}>
          {[{ slug: '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'}`,
                  color: active ? 'var(--text)' : 'var(--text-muted)',
                  fontSize: 13, fontWeight: active ? 600 : 500,
                  fontFamily: 'var(--font-display)', letterSpacing: '.01em',
                  padding: '10px 18px 12px',
                  cursor: 'pointer', whiteSpace: 'nowrap',
                  transition: 'color .15s, border-color .15s',
                }}>
                {b.name}
              </button>
            );
          })}
        </div>
      </div>

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

        {/* Digi AI Banner */}
        <DigiAIBanner kpi={kpi} userRole={userRole} userName={userName} onFilter={setFilterEstado} briefings={briefings || []} />

        {/* ── 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 Manager' },
                  { value: 'validado_manager', 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 Manager' },
                  { value: 'validado_manager', 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_manager', label: 'Para CEO' },
                  { 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>


        {/* ── Table ── */}
      {briefings === null ? (
        <div>
          {[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 }}>
          <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 }}>
          <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={{ paddingBottom: 48 }}>
          <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: 72 }} />{/* acções */}
            </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, textAlign: 'center' }}>Acções</th>
              </tr>
            </thead>
            <tbody>
              {filtered.map((b, i) => {
                let rowAttention = null;
                if (attentionIds.has(b.id)) {
                  if (userRole === 'board') rowAttention = 'var(--ai-500, #3859D0)';
                  else rowAttention = b.status === 'aprovado' ? 'var(--ai-500, #3859D0)' : 'var(--danger, #dc2626)';
                }
                return (
                  <BriefingRow key={b.id} b={b} isAll={isAll}
                    onSelect={() => onSelect(b)}
                    onEdit={() => onEdit(b)}
                    onDelete={() => handleDelete(b.id, b.commercial_name || b.product_name || `Briefing #${b.id}`)}
                    onSubmit={handleSubmitRow}
                    onGenerateStrategy={onGenerateStrategy}
                    onCriarCampanha={onCriarCampanha}
                    onVerCampanha={onVerCampanha}
                    onDuplicar={onDuplicar}
                    attentionColor={rowAttention}
                    selected={selectedIds.has(b.id)}
                    onToggle={() => toggleSelect(b.id)}
                    rowIndex={i}
                    userRole={userRole}
                    aiUsage={aiUsageMap[b.id] || null}
                  />
                );
              })}
            </tbody>
          </table>
          </div>
        </div>
      )}
      </div> {/* fim scrollable body */}

      {/* ── Rodapé AI Budget (fixo) ── */}
      {aiUsage && parseFloat(aiUsage.custo_total) > 0 && (() => {
        const fmtEur = (v) => v != null ? `€${parseFloat(v).toFixed(4).replace(/0+$/, '').replace(/\.$/, '')}` : '—';
        const fmtTok = (v) => Number(v || 0).toLocaleString('pt-PT');
        const periodLabel = dateRange.preset !== 'all'
          ? `${dateRange.start.toLocaleDateString('pt-PT', { day: '2-digit', month: 'short' })} – ${dateRange.end.toLocaleDateString('pt-PT', { day: '2-digit', month: 'short' })}`
          : 'total';
        const sep = <span style={{ margin: '0 5px', opacity: 0.35 }}>·</span>;
        return (
          <div style={{
            flexShrink: 0, borderTop: '1px solid var(--border, #dde3ef)',
            background: 'var(--bg-elev, #fff)', padding: '5px 40px',
            display: 'flex', alignItems: 'center', gap: 0,
            fontSize: 10.5, fontFamily: 'var(--font-mono)', color: 'var(--text-dim, #6b7fa3)',
          }}>
            <span style={{ fontWeight: 600, color: 'var(--text-muted, #4a5e7a)', marginRight: 10, fontSize: 10 }}>DIGI AI</span>
            <span>Custo {periodLabel}: <strong style={{ color: 'var(--text-muted)', fontWeight: 600 }}>{fmtEur(aiUsage.custo_total)}</strong></span>
            {sep}<span>Média/briefing: <strong style={{ color: 'var(--text-muted)', fontWeight: 600 }}>{fmtEur(aiUsage.media_por_briefing)}</strong></span>
            {sep}<span>Tokens: <strong style={{ color: 'var(--text-muted)', fontWeight: 600 }}>{fmtTok(aiUsage.total_tokens)}</strong></span>
            {sep}<span>{Number(aiUsage.total_chamadas || 0)} chamadas API</span>
          </div>
        );
      })()}
    </div>
  );
};

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

const BriefingForm = ({ briefing, brandId, onSave, onSubmit, onCancel, onApproved, onRejected, onGenerateStrategy, onCriarCampanha, onVerCampanha, userRole, userName, userEmail }) => {
  const isMobile = window.useMktMobile();
  const isAll = !brandId || brandId === 'todas';
  const [activeBlock, setActiveBlock] = React.useState(1);
  const [aiImproved, setAiImproved]   = React.useState({}); // { 'block2.end_user': 'Utilizador final', ... }
  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 [savedAt,   setSavedAt]   = React.useState(null); // timestamp persistente
  const [justSaved, setJustSaved] = React.useState(false); // estado botão 2s
  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 [b4BannerDismissed, setB4BannerDismissed] = React.useState(false);
  const [b5BannerDismissed, setB5BannerDismissed] = React.useState(false);
  const [suggestingB4B5, setSuggestingB4B5] = React.useState(false);
  const [showB4EmptyModal, setShowB4EmptyModal] = React.useState(false);
  const [budgetError, setBudgetError] = React.useState('');
  const [toast, setToast] = React.useState(null); // { msg, type: 'success'|'error' }
  const [localStatus, setLocalStatus] = React.useState(null); // override após aprovação
  const showToast = (msg, type = 'success') => {
    setToast({ msg, type });
    setTimeout(() => setToast(null), 3500);
  };
  const curStatus = localStatus ?? briefing?.status;
  const toInputDate = (v) => {
    if (!v) return '';
    if (v instanceof Date) return v.toISOString().slice(0, 10);
    if (typeof v === 'string') return v.length > 10 ? v.slice(0, 10) : v;
    return '';
  };

  const LEGACY_CHAN = {
    linkedin: 'linkedin_ads', linkedin_ads: 'linkedin_ads',
    google_ads: 'google_ads_search', google_ads_search: 'google_ads_search', google_search: 'google_ads_search',
    google_display: 'google_ads_display', google_ads_display: 'google_ads_display',
    instagram: 'meta_ads', facebook: 'meta_ads', meta: 'meta_ads', meta_ads: 'meta_ads',
    social: 'meta_ads', social_media: 'meta_ads', paid_social: 'meta_ads',
    site: 'website', blog: 'website', website: 'website',
    led: 'muppi_led', muppi_led: 'muppi_led', paineis: 'muppi_led', paineis_led: 'muppi_led',
    email: 'email', email_marketing: 'email',
    whatsapp: 'whatsapp', whatsapp_marketing: 'whatsapp',
  };
  const VALID_CHANS = new Set(['meta_ads','linkedin_ads','google_ads_search','google_ads_display','email','whatsapp','website','muppi_led']);
  const normChannels = (arr) => [...new Set((Array.isArray(arr) ? arr : []).map(ch => LEGACY_CHAN[(ch||'').toLowerCase().replace(/[^a-z_]/g,'')] || ch).filter(ch => VALID_CHANS.has(ch)))];

  // Normaliza kpis: string[] legado → {canal,kpi}[]
  const normKpis = (arr) => (Array.isArray(arr) ? arr : []).map(k =>
    typeof k === 'string' ? { canal: '', kpi: k } : k
  ).filter(k => k?.kpi?.trim());

  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, channels: normChannels(b4.channels), kpis: normKpis(b4.kpis), timeline_start: toInputDate(b4.timeline_start), timeline_end: toInputDate(b4.timeline_end) },
      block5: { ...EMPTY_FORM.block5, ...(src.block5 || {}) },
    };
  };

  const [form, setForm] = React.useState(() => {
    console.log('[BriefingForm] briefing:', briefing, 'block1:', briefing?.block1);
    return 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());
    const noChannels = !(b4.channels?.length);
    if ((missingKpis || missingTimeline) && noChannels && 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 handleApplyImprovement = async (block, field, rawValue, fieldLabel) => {
    let value = rawValue;
    if (typeof rawValue === 'string' && rawValue.trim().startsWith('[')) {
      try { value = JSON.parse(rawValue); } catch {}
    }
    const updatedForm = { ...form, [block]: { ...form[block], [field]: value } };
    setForm(updatedForm);
    setAiImproved(prev => ({ ...prev, [`${block}.${field}`]: fieldLabel || field }));

    // Auto-save: apenas guarda o conteúdo dos blocos
    // A apreciação (evals, scores, improved_fields) é gerida pelo Block6 via onUpdate
    try {
      setSaving(true);
      await onSave(updatedForm, isAll ? selectedBrand : undefined, undefined);
      setSavedAt(new Date());
      setJustSaved(true);
      setTimeout(() => setJustSaved(false), 2000);
    } catch (e) {
      console.warn('[auto-save improvement]', e.message);
    } finally {
      setSaving(false);
    }
  };

  const handleDismissBlockImproved = (blockKey) => {
    setAiImproved(prev => {
      const next = { ...prev };
      Object.keys(next).filter(k => k.startsWith(blockKey + '.')).forEach(k => delete next[k]);
      return next;
    });
  };

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

  // Sugerir B4/B5 a partir do formulário (quando vazios após importação)
  const handleSuggestB4B5FromForm = async () => {
    setSuggestingB4B5(true);
    try {
      const brand_name = selectedBrand || briefing?.brand_slug || briefing?.brand_name || '';
      const res = await fetch('/api/marketing/briefings/suggest-b4b5', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ block1: form.block1, block2: form.block2, block3: form.block3, block4: form.block4, block5: form.block5, brand_name }),
      });
      const reader = res.body.getReader(); const dec = new TextDecoder(); let buf = '';
      while (true) {
        const { done, value } = await reader.read();
        if (value) {
          buf += dec.decode(value, { stream: !done });
          const lines = buf.split('\n'); buf = done ? '' : (lines.pop() ?? '');
          for (const line of lines) {
            if (!line.startsWith('data: ')) continue;
            let evt; try { evt = JSON.parse(line.slice(6)); } catch { continue; }
            if (evt.type === 'done') {
              setForm(f => ({ ...f, block4: evt.data.block4, block5: evt.data.block5 }));
              setB4BannerDismissed(true); setB5BannerDismissed(true);
            }
          }
        }
        if (done) break;
      }
    } catch (e) { console.warn('[suggest-b4b5-form]', e.message); }
    finally { setSuggestingB4B5(false); }
  };

  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:         f.block4.channels?.length ? f.block4.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,
        commercial_offer: s.commercial_offer || f.block4.commercial_offer,
      }}));
      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);
    setSavedAt(null);
    if (isAll && !selectedBrand) { setSaveError('Selecciona uma marca antes de guardar.'); return; }
    // Aviso se B4 estiver vazio — modal custom em vez de window.confirm
    const b4IsEmpty = !form.block4?.objective && !form.block4?.key_message && !(form.block4?.channels || []).length;
    if (b4IsEmpty && !b4BannerDismissed) {
      setShowB4EmptyModal(true); return;
    }
    setSaving(true);
    try {
      await onSave(form, isAll ? selectedBrand : undefined, apreciation || undefined);
      const now = new Date();
      setSavedAt(now);
      setJustSaved(true);
      setTimeout(() => setJustSaved(false), 2000);
    } 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        = !curStatus || curStatus === 'rascunho';
  const isRejected     = curStatus === 'rejeitado';
  const isSubmitted    = curStatus === 'submetido';
  const isValidadoCeo  = curStatus === 'validado_ceo';
  const isApproved     = curStatus === 'aprovado';
  const isInApproval   = ['submetido','validado_manager','validado_ceo'].includes(curStatus);
  const b4Ready        = !!(form.block4?.objective && form.block4?.key_message?.trim() && (form.block4?.channels || []).length > 0);
  const canSubmit      = (isDraft || isRejected) && calcProgress(form) >= 80 && (apreciation?.quality_score ?? 0) >= 80 && b4Ready;
  const brandSlug      = briefing?.brand_slug;
  const pendingRoles   = isInApproval ? getMyPendingRoles(userEmail, brandSlug, approvals, curStatus) : [];
  const canApprove     = pendingRoles.length > 0;
  const canEditApproved = userRole === 'admin' || userEmail === 'rui.leitao@digidelta.pt';
  const [editingApproved, setEditingApproved] = React.useState(false);
  // Aprovador activo pode editar; aprovado = read-only por defeito (desbloqueado por Editar)
  const isReadOnly     = !canApprove && (isInApproval || curStatus === 'em_campanha' || (isApproved && !editingApproved));

  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;
      let lastResult;
      for (const role of pendingRoles) {
        lastResult = await window.MktBriefingsAPI.approve(briefing.id, {
          action,
          notes: approvalNotes,
          approver_name,
          approver_email: userEmail,
          role,
        });
      }
      // Refetch para actualizar estado local sem navegar
      const refreshed = await window.MktBriefingsAPI.get(briefing.id);
      if (refreshed?.approvals) setApprovals(refreshed.approvals);
      const newStatus = lastResult?.status || refreshed?.status;
      if (newStatus) setLocalStatus(newStatus);
      setApprovalNotes('');
      // Toast contextual
      if (action === 'approved') {
        const toastMsg = newStatus === 'aprovado'
          ? 'Briefing aprovado. Pode criar a campanha.'
          : 'Fase aprovada. Aguarda aprovação do Chairman.';
        showToast(toastMsg, 'success');
      } else if (action === 'rejected') {
        showToast('Briefing rejeitado. O autor será notificado.', 'error');
      } else {
        showToast('Alterações pedidas. O autor será notificado.', 'info');
      }
    } 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 }}>

      {/* Toast pós-aprovação */}
      {toast && (
        <div style={{
          position: 'fixed', bottom: 24, right: 24, zIndex: 9999,
          padding: '12px 20px', borderRadius: 8, fontSize: 13, fontWeight: 500,
          background: toast.type === 'error' ? '#dc2626' : toast.type === 'info' ? '#d97706' : '#15803d',
          color: '#fff', boxShadow: '0 4px 16px rgba(0,0,0,0.2)',
          animation: 'fadeIn .2s ease',
        }}>
          {toast.msg}
        </div>
      )}

      {/* Modal: B4 vazio ao guardar */}
      {showB4EmptyModal && (
        <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,0.4)', zIndex:9999, display:'flex', alignItems:'center', justifyContent:'center' }}>
          <div style={{ background:'var(--bg-elev,#fff)', borderRadius:12, padding:'28px 32px', maxWidth:420, width:'90%', boxShadow:'0 8px 32px rgba(17,41,84,.18)', fontFamily:'var(--font-body,Inter,sans-serif)' }}>
            <div style={{ fontSize:16, fontWeight:700, color:'var(--text)', marginBottom:8 }}>Bloco 4 — Campanha vazio</div>
            <div style={{ fontSize:13, color:'var(--text-muted)', lineHeight:1.6, marginBottom:20 }}>
              A estratégia de campanha não está definida. Podes guardar assim e preencher depois, ou gerar automaticamente com AI.
            </div>
            <div style={{ display:'flex', gap:8, justifyContent:'flex-end' }}>
              <button className="btn" style={{ fontSize:12 }} onClick={() => { setShowB4EmptyModal(false); setB4BannerDismissed(true); handleSave(); }}>Guardar sem campanha</button>
              <button className="btn btn-ai" style={{ fontSize:12 }} onClick={() => { setShowB4EmptyModal(false); setActiveBlock(4); }}>Preencher Bloco 4 →</button>
            </div>
          </div>
        </div>
      )}

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

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

        {/* Title + actions */}
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16 }}>
          <div style={{ minWidth: 0 }}>
            <h1 style={{ margin: 0, fontSize: 21, fontWeight: 700, color: 'var(--text)', fontFamily: 'var(--font-display)', letterSpacing: '-0.01em', lineHeight: 1.2 }}>
              {isNew ? 'Novo Briefing' : (briefing?.commercial_name || briefing?.product_name || 'Briefing')}
            </h1>

            {/* Meta row: status + brand pill + progress bar */}
            <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 8, flexWrap: 'wrap' }}>
              {(() => {
                const cfg = STATUS_CONFIG[briefing?.status || 'rascunho'];
                return <span style={{ fontSize: 10, fontWeight: 700, padding: '2px 8px', borderRadius: 4, background: cfg.bg, color: cfg.color, fontFamily: 'var(--font-mono)', letterSpacing: '0.04em' }}>{cfg.label.toUpperCase()}</span>;
              })()}
              {briefing?.brand_name && (() => {
                const BRAND_COLORS = { mimaki: '#0066CC', biond: '#E8A000', decal: '#00A86B', alldecor: '#7C3AED', sensek: '#DC2626', netscreen: '#0EA5E9', digidelta: '#3859D0' };
                const bc = briefing.brand_color || BRAND_COLORS[briefing.brand_slug] || 'var(--ai-500)';
                return <span style={{ fontSize: 10, fontWeight: 700, padding: '2px 8px', borderRadius: 4, background: bc + '15', color: bc, fontFamily: 'var(--font-mono)', border: `1px solid ${bc}25`, letterSpacing: '0.04em' }}>{briefing.brand_name.toUpperCase()}</span>;
              })()}
              {!isNew && (() => {
                const pct = calcProgress(form);
                const col = pct >= 80 ? 'var(--ai-500, #3859D0)' : pct >= 40 ? 'var(--text-muted, #4a5e7a)' : 'var(--text-dim, #6b7fa3)';
                const qs  = apreciation?.quality_score ?? 0;
                const qCol = qs >= 80 ? 'var(--ai-500, #3859D0)' : qs >= 60 ? 'var(--text-muted, #4a5e7a)' : qs > 0 ? 'var(--text-dim, #6b7fa3)' : 'var(--text-dim)';
                return (
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10, flex: 1, flexWrap: 'wrap' }}>
                    {/* Preenchimento */}
                    <div style={{ display: 'flex', alignItems: 'center', gap: 5 }} title="Percentagem de campos obrigatórios preenchidos">
                      <span style={{ fontSize: 9, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', textTransform: 'uppercase', letterSpacing: '0.06em', whiteSpace: 'nowrap' }}>Preenchimento</span>
                      <div style={{ width: 60, height: 4, borderRadius: 99, background: 'var(--border)', overflow: 'hidden' }}>
                        <div style={{ width: `${pct}%`, height: '100%', borderRadius: 99, background: col, transition: 'width 0.4s ease' }} />
                      </div>
                      <span style={{ fontSize: 10, color: col, fontFamily: 'var(--font-mono)', fontWeight: 700, whiteSpace: 'nowrap' }}>{pct}%</span>
                    </div>
                    {/* Separador */}
                    {qs > 0 && <span style={{ color: 'var(--border)', fontSize: 10 }}>·</span>}
                    {/* Qualidade */}
                    {qs > 0 && (
                      <div style={{ display: 'flex', alignItems: 'center', gap: 5 }} title="Score de qualidade estratégica — mínimo 80 para submeter">
                        <span style={{ fontSize: 9, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', textTransform: 'uppercase', letterSpacing: '0.06em', whiteSpace: 'nowrap' }}>Qualidade</span>
                        <div style={{ width: 60, height: 4, borderRadius: 99, background: 'var(--border)', overflow: 'hidden' }}>
                          <div style={{ width: `${qs}%`, height: '100%', borderRadius: 99, background: qCol, transition: 'width 0.4s ease' }} />
                        </div>
                        <span style={{ fontSize: 10, color: qCol, fontFamily: 'var(--font-mono)', fontWeight: 700, whiteSpace: 'nowrap' }}>{qs}/100</span>
                      </div>
                    )}
                  </div>
                );
              })()}
            </div>
            {saveError && <div style={{ fontSize: 11, color: 'var(--danger)', marginTop: 4, fontFamily: 'var(--font-body)' }}>{saveError}</div>}
          </div>

          {/* Actions */}
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 5, flexShrink: 0, marginTop: 4 }}>
            <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
              <button className="btn" style={{ height: 30, padding: '0 12px', fontSize: 12 }} onClick={onCancel}>← Voltar</button>
              {!isReadOnly && !isApproved && (
                <button className="btn" onClick={handleSave} disabled={saving}
                  style={{ height: 30, padding: '0 14px', fontSize: 12,
                    ...(justSaved ? { background: 'var(--success, #22c55e)', borderColor: 'var(--success, #22c55e)', color: '#fff' } : {}) }}>
                  {saving ? 'A guardar...' : justSaved ? '✓ Guardado' : 'Guardar'}
                </button>
              )}
              {canSubmit && (
                <button data-tutorial-step="submit" className="btn btn-ai" style={{ height: 30, padding: '0 14px', fontSize: 12 }} 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>
              )}
              {isApproved && canEditApproved && (
                editingApproved
                  ? <button className="btn" onClick={handleSave} disabled={saving}
                      style={{ height: 30, padding: '0 14px', fontSize: 12,
                        ...(justSaved ? { background: 'var(--success, #22c55e)', borderColor: 'var(--success, #22c55e)', color: '#fff' } : {}) }}>
                      {saving ? 'A guardar...' : justSaved ? '✓ Guardado' : 'Guardar'}
                    </button>
                  : <button className="btn" style={{ height: 30, padding: '0 14px', fontSize: 12 }}
                      onClick={() => setEditingApproved(true)}>
                      Editar
                    </button>
              )}
              {isApproved && (
                <button className="btn btn-ai" style={{ height: 30, padding: '0 14px', fontSize: 12 }}
                  onClick={() => onCriarCampanha?.(briefing)}>
                  + Criar Campanha
                </button>
              )}
              {curStatus === 'em_campanha' && (
                <button className="btn btn-ai" style={{ height: 30, padding: '0 14px', fontSize: 12 }}
                  onClick={() => onVerCampanha?.(briefing)}>
                  Ver Campanha →
                </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>
            {/* Saved timestamp */}
            {savedAt && !justSaved && !saving && (
              <span style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)' }}>
                Guardado às {savedAt.toLocaleTimeString('pt-PT', { hour: '2-digit', minute: '2-digit' })}
              </span>
            )}
          </div>
        </div>

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

        {/* Banner — briefing em campanha (read-only) */}
        {curStatus === 'em_campanha' && (
          <div style={{ margin: '16px 0 0', padding: '10px 14px', borderRadius: 7, background: 'color-mix(in srgb, #3859D0 8%, transparent)', border: '1px solid color-mix(in srgb, #3859D0 25%, transparent)', display: 'flex', alignItems: 'center', gap: 12, fontSize: 13, color: 'var(--text)' }}>
            <span style={{ fontSize: 16 }}>ℹ</span>
            <span style={{ flex: 1 }}>Este briefing está em campanha e não pode ser editado. Para correcções, apaga a campanha primeiro.</span>
            <button className="btn" style={{ height: 28, padding: '0 12px', fontSize: 12, flexShrink: 0 }}
              onClick={() => onVerCampanha?.(briefing)}>
              Ver Campanha →
            </button>
          </div>
        )}

        {/* Stepper dinâmico — BriefingForm */}
        {(() => {
          const BLOCK_SUBS = BLOCK_LABELS.map((_, i) => {
            if (i === 5) return 'apreciação AI';
            const pct = Math.round(blockDone[i] * 100);
            if (pct === 0) return 'por preencher';
            if (pct === 100) return 'completo';
            return `${pct}% preenchido`;
          });
          return (
            <div style={{ display: 'flex', alignItems: 'flex-start', overflowX: 'auto' }}>
              {BLOCK_LABELS.map((label, i) => {
                const isSelected  = activeBlock === i + 1;
                const isCompleted = blockDone[i] >= 1;
                const isPartial   = blockDone[i] > 0 && blockDone[i] < 1;
                const isAI        = i === 5;
                const blockKey    = `block${i + 1}`;
                const hasImproved = i < 5 && Object.keys(aiImproved).some(k => k.startsWith(blockKey + '.'));
                const circleBg    = isCompleted ? 'rgba(21,128,61,.1)' : isPartial ? 'rgba(245,158,11,.1)' : isSelected ? 'var(--ai-500, #3859D0)' : 'transparent';
                const circleBdr   = isCompleted ? '1.5px solid rgba(21,128,61,.3)' : isPartial ? '1.5px solid rgba(245,158,11,.3)' : isSelected ? 'none' : '1.5px solid #e2e8f0';
                const circleText  = isCompleted ? '✓' : isAI ? '✦' : String(i + 1);
                const circleColor = isCompleted ? '#15803d' : isPartial ? '#f59e0b' : isSelected ? '#fff' : '#94a3b8';
                const labelColor  = isSelected ? 'var(--text, #283252)' : (isCompleted || isPartial) ? 'var(--text-muted, #64748b)' : '#94a3b8';
                return (
                  <React.Fragment key={label}>
                    <button onClick={() => {
                        // Auto-save silencioso ao entrar no Bloco 6 se briefing não tem ID
                        if (i + 1 === 6 && !briefing?.id) {
                          const bid = isAll ? selectedBrand : brandId;
                          if (bid) window.MktBriefingsAPI.create({ brand_id: bid, created_by_name: currentUserName || '', ...form })
                            .then(r => { if (r?.id) setSelected && setSelected(s => ({ ...(s||{}), id: r.id })); })
                            .catch(() => {});
                        }
                        setActiveBlock(i + 1);
                      }} style={{ display: 'flex', alignItems: 'flex-start', gap: 8, padding: '10px 16px 12px', background: 'none', border: 'none', borderBottom: `2px solid ${isSelected ? 'var(--ai-500, #3859D0)' : 'transparent'}`, cursor: 'pointer', fontFamily: 'inherit', flexShrink: 0 }}>
                      <div style={{ position: 'relative', width: 20, height: 20, flexShrink: 0, marginTop: 1 }}>
                        <div style={{ width: 20, height: 20, borderRadius: '50%', background: circleBg, border: circleBdr, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                          <span style={{ fontSize: 10, fontWeight: 700, color: circleColor, fontFamily: 'var(--font-body, Inter, sans-serif)', lineHeight: 1 }}>{circleText}</span>
                        </div>
                        {hasImproved && <div style={{ position: 'absolute', top: -2, right: -2, width: 7, height: 7, borderRadius: '50%', background: '#f59e0b', border: '1.5px solid #fff' }} />}
                      </div>
                      <div style={{ display: 'flex', flexDirection: 'column', gap: 1, textAlign: 'left' }}>
                        <span style={{ fontSize: 13, fontWeight: 600, color: labelColor, fontFamily: 'var(--font-display, Montserrat, sans-serif)', lineHeight: 1.2 }}>{`${i + 1}. ${label}`}</span>
                        <span style={{ fontSize: 10.5, color: '#94a3b8', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{BLOCK_SUBS[i]}</span>
                      </div>
                    </button>
                    {i < BLOCK_LABELS.length - 1 && (
                      <div style={{ width: 20, height: 1, flexShrink: 0, alignSelf: 'flex-start', marginTop: 20, background: blockDone[i] >= 1 ? 'rgba(21,128,61,.25)' : '#e2e8f0' }} />
                    )}
                  </React.Fragment>
                );
              })}
            </div>
          );
        })()}
      </div>

      {/* Conteúdo do bloco */}
      <div className="scrollbar" style={{ flex: 1, minHeight: 0, overflowY: 'auto', padding: isMobile ? 12 : '40px 40px 64px', background: 'var(--bg, #f8fafc)' }}>
        <div>

          {/* Painel de aprovações */}
          {isSubmitted && (
            <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>
          )}

          {/* Banner campos melhorados por AI — aparece no bloco activo */}
          {(() => {
            const blockKey = `block${activeBlock}`;
            const improved = Object.entries(aiImproved).filter(([k]) => k.startsWith(blockKey + '.'));
            if (!improved.length || activeBlock === 6) return null;
            return (
              <div style={{ marginBottom: 16, padding: '8px 14px', borderRadius: 8, background: 'rgba(245,158,11,.07)', border: '1px solid rgba(245,158,11,.25)', display: 'flex', alignItems: 'center', gap: 10 }}>
                <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
                <span style={{ fontSize: 11, color: '#92400e', flex: 1 }}>
                  <strong>Melhorado por AI:</strong> {improved.map(([, label]) => label).join(' · ')}
                </span>
                <button onClick={() => handleDismissBlockImproved(blockKey)} style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#92400e', fontSize: 12, padding: '0 2px', lineHeight: 1, opacity: 0.6 }}>
                  <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
                </button>
              </div>
            );
          })()}

          <div style={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 && (() => {
              const b4Empty = !form.block4.objective && !form.block4.key_message && !(form.block4.channels || []).length;
              return (<>
                {b4Empty && !b4BannerDismissed && (
                  <div style={{ marginBottom:12, padding:'12px 14px', borderRadius:8, background:'color-mix(in srgb, var(--ai-500,#3859D0) 5%, transparent)', border:'1px solid color-mix(in srgb, var(--ai-500,#3859D0) 18%, transparent)', display:'flex', alignItems:'center', gap:12 }}>
                    <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="var(--ai-500,#3859D0)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{flexShrink:0}}><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
                    <div style={{ flex:1, fontSize:12, color:'var(--text-muted)' }}>
                      <strong style={{color:'var(--text)'}}>Bloco 4 — Campanha não definida.</strong> Gera a estratégia de campanha com AI ou preenche manualmente.
                    </div>
                    <div style={{ display:'flex', gap:6, flexShrink:0 }}>
                      <button className="btn btn-ai" style={{fontSize:11}} onClick={handleSuggestB4B5FromForm} disabled={suggestingB4B5}>
                        {suggestingB4B5 ? 'A gerar...' : 'Gerar com AI'}
                      </button>
                      <button className="btn" style={{fontSize:11}} onClick={() => setB4BannerDismissed(true)}>Preencher manualmente</button>
                    </div>
                  </div>
                )}
                <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 && (() => {
              const b5Empty = !(form.block5.prohibited_claims || []).some(v => v?.trim()) && !(form.block5.off_brand_messages || []).some(v => v?.trim());
              return (<>
                {b5Empty && !b5BannerDismissed && (
                  <div style={{ marginBottom:12, padding:'12px 14px', borderRadius:8, background:'color-mix(in srgb, var(--ai-500,#3859D0) 5%, transparent)', border:'1px solid color-mix(in srgb, var(--ai-500,#3859D0) 18%, transparent)', display:'flex', alignItems:'center', gap:12 }}>
                    <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="var(--ai-500,#3859D0)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{flexShrink:0}}><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
                    <div style={{ flex:1, fontSize:12, color:'var(--text-muted)' }}>
                      <strong style={{color:'var(--text)'}}>Bloco 5 — Restrições não definidas.</strong> Gera os guardrails com AI ou preenche manualmente.
                    </div>
                    <div style={{ display:'flex', gap:6, flexShrink:0 }}>
                      <button className="btn btn-ai" style={{fontSize:11}} onClick={handleSuggestB4B5FromForm} disabled={suggestingB4B5}>
                        {suggestingB4B5 ? 'A gerar...' : 'Gerar com AI'}
                      </button>
                      <button className="btn" style={{fontSize:11}} onClick={() => setB5BannerDismissed(true)}>Preencher manualmente</button>
                    </div>
                  </div>
                )}
                <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}
              form={form}
              brandName={selectedBrand || briefing?.brand_slug || ''}
              onApplyImprovement={handleApplyImprovement}
              onNavigateToBlock={setActiveBlock}
            />
          )}
        </div>
      </div>

    </div>
  );
};

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

const BriefingDetail = ({ briefing, onEdit, onBack, onApproved, onRejected, onGenerateStrategy, onCriarCampanha, onVerCampanha, 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 = ['submetido','validado_manager','validado_ceo'].includes(briefing.status);
  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 data-theme="light" style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0, '--bg': '#f8fafc', '--bg-elev': '#ffffff', '--bg-hover': '#f1f5f9', '--bg-sunken': '#e8edf5', '--border': '#dde3ef', '--border-strong': '#bec9de', '--text': '#112954', '--text-muted': '#4a5e7a', '--text-dim': '#6b7fa3', '--ai-500': '#3859D0' }}>

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

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

        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16 }}>
          <div style={{ minWidth: 0 }}>
            <h1 style={{ margin: 0, fontSize: 21, fontWeight: 700, color: 'var(--text)', fontFamily: 'var(--font-display)', letterSpacing: '-0.01em', lineHeight: 1.2 }}>
              {src.commercial_name || src.product_name || 'Briefing'}
            </h1>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6, flexWrap: 'wrap' }}>
              {(() => { const cfg = STATUS_CONFIG[briefing.status || 'rascunho']; return <span style={{ fontSize: 11, fontWeight: 600, padding: '2px 8px', borderRadius: 99, background: cfg.bg, color: cfg.color, fontFamily: 'var(--font-body)' }}>{cfg.label}</span>; })()}
              {src.brand_name && (() => {
                const BRAND_COLORS = { mimaki: '#0066CC', biond: '#E8A000', decal: '#00A86B', alldecor: '#7C3AED', sensek: '#DC2626', netscreen: '#0EA5E9', digidelta: '#3859D0' };
                const bc = src.brand_color || BRAND_COLORS[src.brand_slug] || 'var(--ai-500)';
                return <span style={{ fontSize: 11, fontWeight: 600, padding: '2px 8px', borderRadius: 99, background: bc + '18', color: bc, border: `1px solid ${bc}30`, fontFamily: 'var(--font-body)' }}>{src.brand_name}</span>;
              })()}
              {(() => {
                const pct = calcProgress(form);
                return (
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                    <div style={{ width: 60, height: 4, borderRadius: 99, background: 'var(--border)', overflow: 'hidden' }}>
                      <div style={{ width: `${pct}%`, height: '100%', borderRadius: 99, background: pct === 100 ? 'var(--success, #22c55e)' : pct >= 80 ? 'var(--ai-500)' : 'var(--warning, #f59e0b)', transition: 'width 0.4s ease' }} />
                    </div>
                    <span style={{ fontSize: 11, color: 'var(--text-muted)', fontFamily: 'var(--font-body)' }}>{pct}%</span>
                  </div>
                );
              })()}
              <span style={{ fontSize: 11, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>v{briefing.version_number || 1}</span>
            </div>
          </div>
          <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexShrink: 0, marginTop: 2 }}>
            <button className="btn" style={{ height: 30, padding: '0 12px', fontSize: 12 }} onClick={onBack}>← Voltar</button>
            {(briefing.status === 'rascunho' || briefing.status === 'rejeitado') && (
              <button className="btn" style={{ height: 30, padding: '0 12px', fontSize: 12 }} onClick={() => onEdit(src)}>Editar</button>
            )}
            {briefing.status === 'aprovado' && (
              <button data-tutorial-step="approved" className="btn btn-ai" style={{ height: 30, padding: '0 14px', fontSize: 12 }} onClick={() => onCriarCampanha?.(briefing)}>+ Criar Campanha</button>
            )}
            {briefing.status === 'em_campanha' && (
              <button className="btn btn-ai" style={{ height: 30, padding: '0 14px', fontSize: 12 }} onClick={() => onVerCampanha?.(briefing)}>Ver Campanha →</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>

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

        {/* Stepper — mesmo padrão BriefingForm */}
        {(() => {
          const BLOCK_SUBS = BLOCK_LABELS.map((_, i) => {
            if (i === 5) return 'apreciação AI';
            const pct = Math.round(blockDone[i] * 100);
            if (pct === 0) return 'por preencher';
            if (pct === 100) return 'completo';
            return `${pct}% preenchido`;
          });
          return (
            <div style={{ display: 'flex', alignItems: 'flex-start', overflowX: 'auto' }}>
              {BLOCK_LABELS.map((label, i) => {
                const isSelected  = activeBlock === i + 1;
                const isCompleted = blockDone[i] >= 1;
                const isPartial   = blockDone[i] > 0 && blockDone[i] < 1;
                const isAI        = i === 5;
                const circleBg    = isCompleted ? 'rgba(21,128,61,.1)' : isPartial ? 'rgba(245,158,11,.1)' : isSelected ? 'var(--ai-500, #3859D0)' : 'transparent';
                const circleBdr   = isCompleted ? '1.5px solid rgba(21,128,61,.3)' : isPartial ? '1.5px solid rgba(245,158,11,.3)' : isSelected ? 'none' : '1.5px solid #e2e8f0';
                const circleText  = isCompleted ? '✓' : isAI ? '✦' : String(i + 1);
                const circleColor = isCompleted ? '#15803d' : isPartial ? '#f59e0b' : isSelected ? '#fff' : '#94a3b8';
                const labelColor  = isSelected ? 'var(--text, #283252)' : (isCompleted || isPartial) ? 'var(--text-muted, #64748b)' : '#94a3b8';
                return (
                  <React.Fragment key={label}>
                    <button onClick={() => setActiveBlock(i + 1)} data-tutorial-step={['fill_block1','fill_block2_block3','fill_block2_block3','fill_block4',null,'ai_analysis'][i] || undefined} style={{ display: 'flex', alignItems: 'flex-start', gap: 8, padding: '10px 16px 12px', background: 'none', border: 'none', borderBottom: `2px solid ${isSelected ? 'var(--ai-500, #3859D0)' : 'transparent'}`, cursor: 'pointer', fontFamily: 'inherit', flexShrink: 0 }}>
                      <div style={{ width: 20, height: 20, borderRadius: '50%', flexShrink: 0, marginTop: 1, background: circleBg, border: circleBdr, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                        <span style={{ fontSize: 10, fontWeight: 700, color: circleColor, fontFamily: 'var(--font-body, Inter, sans-serif)', lineHeight: 1 }}>{circleText}</span>
                      </div>
                      <div style={{ display: 'flex', flexDirection: 'column', gap: 1, textAlign: 'left' }}>
                        <span style={{ fontSize: 13, fontWeight: 600, color: labelColor, fontFamily: 'var(--font-display, Montserrat, sans-serif)', lineHeight: 1.2 }}>{`${i + 1}. ${label}`}</span>
                        <span style={{ fontSize: 10.5, color: '#94a3b8', fontFamily: 'var(--font-body, Inter, sans-serif)' }}>{BLOCK_SUBS[i]}</span>
                      </div>
                    </button>
                    {i < BLOCK_LABELS.length - 1 && (
                      <div style={{ width: 20, height: 1, flexShrink: 0, alignSelf: 'flex-start', marginTop: 20, background: blockDone[i] >= 1 ? 'rgba(21,128,61,.25)' : '#e2e8f0' }} />
                    )}
                  </React.Fragment>
                );
              })}
            </div>
          );
        })()}
      </div>

      {/* Conteúdo */}
      <div className="scrollbar" style={{ flex: 1, minHeight: 0, overflowY: 'auto', padding: isMobile ? 12 : '40px 40px 64px', background: 'var(--bg, #f8fafc)' }}>
        <div>

          {/* 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' && (
            <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>

      <div style={{ display: 'none' }}> {/* footer removido — stepper faz a navegação */}
      </div>
    </div>
  );
};

const IMPORT_STEPS = [
  { step: 1, label: 'Fontes recebidas' },
  { step: 2, label: 'Extracção de texto' },
  { step: 3, label: 'Envio para Digi 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' },
  ]},
  { 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([]);
  const [urlList, setUrlList]         = React.useState([]);
  const [suggestPhase, setSuggestPhase] = React.useState('idle');
  const [suggestMsgs, setSuggestMsgs]   = React.useState([]);
  const [suggestError, setSuggestError] = React.useState('');
  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);
  const doneDataRef  = React.useRef(null);
  React.useEffect(() => { doneDataRef.current = doneData; }, [doneData]);

  const totalSources = fileList.length + urlList.length;
  const loadingPct   = Math.round((currentStep / 5) * 100);

  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 = '';
    let _lastStepAt = 0;
    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') {
          const wait = 500 - (Date.now() - _lastStepAt);
          if (wait > 0 && _lastStepAt > 0) await new Promise(r => setTimeout(r, wait));
          setCurrentStep(evt.step); setMessages(m => [...m, evt.message]);
          _lastStepAt = Date.now();
        }
        else if (evt.type === 'done') {
          setCurrentStep(6); setDoneData(evt.data); setDoneSources(evt.sources || []); setPhase('done');
          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 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);
  };

  const handleSaveProd = 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 handleOpenForm = async () => {
    if (openingForm) return;
    setOpeningForm(true);
    const aprec = await Promise.race([aprecPromise.current || Promise.resolve(null), new Promise(r => setTimeout(() => r(null), 8000))]);
    onDone({ ...(doneDataRef.current || doneData), ai_apreciation: aprec || undefined }, doneSources);
  };

  // Auto-suggest removido — utilizador decide se quer gerar B4/B5 no modal ou no formulário

  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();
        // Processar value ANTES de verificar done — o último chunk pode conter o evento final
        if (value) {
          buf += dec.decode(value, { stream: !done });
          const lines = buf.split('\n');
          buf = done ? '' : (lines.pop() ?? '');
          for (const line of lines) {
            if (!line.startsWith('data: ')) continue;
            let evt; try { evt = JSON.parse(line.slice(6)); } catch { continue; }
            if (evt.type === 'status') 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'); }
          }
        }
        if (done) {
          // Flush buffer residual
          if (buf.startsWith('data: ')) {
            try { const evt = JSON.parse(buf.slice(6)); if (evt.type === 'done') { setDoneData(prev => ({ ...prev, block4: evt.data.block4, block5: evt.data.block5, _b4_suggested: true, _b5_suggested: true })); setSuggestPhase('done'); } } catch {}
          }
          break;
        }
      }
    } catch (e) { setSuggestError(e.message); setSuggestPhase('error'); }
  };

  // ── SVG icons ──
  const IcUpload = () => <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="#3859D0" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>;
  const IcClose  = () => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>;
  const IcFile   = () => <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#64748b" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>;
  const IcLink   = () => <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#64748b" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71"/></svg>;
  const IcCheck  = (c) => <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke={c||'#10b981'} strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>;
  const IcX      = (c) => <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke={c||'#ef4444'} strokeWidth="3" strokeLinecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>;
  const IcWarn   = () => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" strokeWidth="2.5" strokeLinecap="round"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>;
  const IcLightning = () => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#3859D0" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>;

  const inpStyle = { width: '100%', boxSizing: 'border-box', padding: '8px 10px', borderRadius: 7, fontSize: 12, border: '1px solid #e2e8f0', color: '#1d2e38', outline: 'none', fontFamily: 'Inter, sans-serif', background: '#fff' };
  const labelStyle = { fontSize: 10, fontWeight: 600, color: '#64748b', letterSpacing: '0.04em', display: 'block', marginBottom: 5 };

  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24 }}>
      <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
      <div style={{ background: '#fff', borderRadius: 14, width: '100%', maxWidth: 560, maxHeight: 'calc(100vh - 48px)', boxShadow: '0 24px 80px rgba(0,0,0,.2)', display: 'flex', flexDirection: 'column' }}>

        {/* Header */}
        <div style={{ padding: '18px 24px 14px', borderBottom: '1px solid #f1f5f9', flexShrink: 0 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <div style={{ width: 30, height: 30, borderRadius: 8, background: 'rgba(56,89,208,.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
              <IcUpload />
            </div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 14, fontWeight: 700, color: '#1d2e38', fontFamily: 'Montserrat, sans-serif' }}>
                {addStep ? 'Adicionar produto' : 'Importar com IA'}
              </div>
              <div style={{ fontSize: 11, color: '#94a3b8' }}>
                {phase === 'pick'    && 'Ficheiros ou URLs — a Digi AI analisa e preenche o briefing'}
                {phase === 'loading' && 'a extrair e analisar o conteúdo...'}
                {phase === 'done'    && !addStep && 'extracção concluída — revê antes de abrir'}
                {phase === 'done'    && addStep  && 'regista o produto na base de dados'}
                {phase === 'error'   && 'ocorreu um erro na extracção'}
              </div>
            </div>
            {phase === 'loading'
              ? <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexShrink: 0 }}>
                  <span style={{ fontSize: 11, fontWeight: 600, color: '#3859D0', fontFamily: 'monospace' }}>{loadingPct}%</span>
                  <div style={{ width: 18, height: 18, borderRadius: '50%', border: '2px solid #e2e8f0', borderTopColor: '#3859D0', animation: 'spin 0.8s linear infinite' }} />
                </div>
              : <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#94a3b8', padding: 4, lineHeight: 1, display: 'flex' }}>
                  <IcClose />
                </button>
            }
          </div>
        </div>

        {/* Body */}
        <div style={{ padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 14, overflowY: 'auto', flex: 1, minHeight: 0 }}>

          {/* ── Pick ── */}
          {phase === 'pick' && (
            <>
              <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 ? '#3859D0' : '#e2e8f0'}`,
                  borderRadius: 10, padding: '24px', textAlign: 'center',
                  background: dragging ? 'rgba(56,89,208,.04)' : '#fafbfc',
                  transition: 'all 0.15s', cursor: 'pointer',
                  display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8,
                }}
              >
                <div style={{ width: 36, height: 36, borderRadius: 10, background: dragging ? 'rgba(56,89,208,.1)' : '#f1f5f9', display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'all 0.15s' }}>
                  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke={dragging ? '#3859D0' : '#94a3b8'} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                    <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/>
                  </svg>
                </div>
                <div style={{ fontSize: 13, fontWeight: 500, color: '#1d2e38' }}>Arrasta ficheiros aqui ou clica para escolher</div>
                <div style={{ fontSize: 11, color: '#94a3b8' }}>PDF ou DOCX · múltiplos ficheiros · máx. 60 MB cada</div>
              </div>

              <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={{ ...inpStyle, flex: 1 }} />
                <button className="btn" onClick={addUrl} disabled={!urlInput.trim().startsWith('http')} style={{ flexShrink: 0, fontSize: 12 }}>+ URL</button>
              </div>

              {totalSources > 0 && (
                <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                  <div style={{ fontSize: 10, fontWeight: 600, color: '#94a3b8', letterSpacing: '0.06em', textTransform: 'uppercase', marginBottom: 2, fontFamily: 'monospace' }}>
                    {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: '7px 10px', borderRadius: 7, background: '#f8fafc', border: '1px solid #f1f5f9' }}>
                      <IcFile />
                      <span style={{ flex: 1, fontSize: 12, color: '#1d2e38', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{f.name}</span>
                      <span style={{ fontSize: 10, color: '#94a3b8', fontFamily: 'monospace', 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: '#94a3b8', padding: 2, lineHeight: 1, display: 'flex' }}>
                        <IcClose />
                      </button>
                    </div>
                  ))}
                  {urlList.map((u, i) => (
                    <div key={u} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '7px 10px', borderRadius: 7, background: '#f8fafc', border: '1px solid #f1f5f9' }}>
                      <IcLink />
                      <span style={{ flex: 1, fontSize: 12, color: '#1d2e38', 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: '#94a3b8', padding: 2, lineHeight: 1, display: 'flex' }}>
                        <IcClose />
                      </button>
                    </div>
                  ))}
                </div>
              )}

              {errorMsg && <div style={{ fontSize: 12, color: '#ef4444', background: 'rgba(239,68,68,.06)', borderRadius: 6, padding: '8px 12px' }}>{errorMsg}</div>}
            </>
          )}

          {/* ── Loading ── */}
          {phase === 'loading' && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 16, padding: '8px 0' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <div style={{ width: 32, height: 32, borderRadius: 8, background: 'rgba(56,89,208,.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                  <IcLightning />
                </div>
                <div>
                  <div style={{ fontSize: 13, fontWeight: 700, color: '#1d2e38', fontFamily: 'Montserrat, sans-serif' }}>Digi AI</div>
                  <div style={{ fontSize: 11, color: '#94a3b8' }}>a extrair e analisar o conteúdo...</div>
                </div>
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                {messages.map((msg, i) => (
                  <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                    <div style={{ width: 18, height: 18, borderRadius: '50%', background: i < messages.length - 1 ? 'rgba(16,185,129,.15)' : 'rgba(56,89,208,.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                      {i < messages.length - 1 ? <IcCheck /> : <div style={{ width: 6, height: 6, borderRadius: '50%', background: '#3859D0' }} />}
                    </div>
                    <span style={{ fontSize: 12, color: i < messages.length - 1 ? '#94a3b8' : '#1d2e38', fontWeight: i < messages.length - 1 ? 400 : 500 }}>
                      {msg}
                      {i === messages.length - 1 && extractProgress > 0 && extractProgress < 100 && (
                        <span style={{ marginLeft: 6, color: '#3859D0', fontWeight: 600, fontFamily: 'monospace' }}>{extractProgress}%</span>
                      )}
                    </span>
                  </div>
                ))}
                {messages.length === 0 && (
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                    <div style={{ width: 18, height: 18, borderRadius: '50%', background: 'rgba(56,89,208,.1)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                      <div style={{ width: 6, height: 6, borderRadius: '50%', background: '#3859D0' }} />
                    </div>
                    <span style={{ fontSize: 12, color: '#1d2e38', fontWeight: 500 }}>A processar as fontes...</span>
                  </div>
                )}
              </div>
            </div>
          )}

          {/* ── Done — completeness ── */}
          {phase === 'done' && doneData && !addStep && (() => {
            const completeness   = calcCompleteness(doneData);
            const productMissing = !doneData.product_id;
            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;
            return (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                {completeness.map(b => {
                  const full  = b.missing.length === 0;
                  const empty = b.filled === 0;
                  const color = full ? '#10b981' : empty ? '#ef4444' : '#f59e0b';
                  const bg    = full ? 'rgba(16,185,129,.06)' : empty ? 'rgba(239,68,68,.06)' : 'rgba(245,158,11,.06)';
                  return (
                    <div key={b.id} style={{ borderRadius: 8, padding: '8px 12px', background: bg, border: `1px solid ${full?'rgba(16,185,129,.2)':empty?'rgba(239,68,68,.2)':'rgba(245,158,11,.2)'}` }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                        <div style={{ width: 16, height: 16, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                          {full ? <IcCheck /> : empty ? <IcX /> : <IcWarn />}
                        </div>
                        <span style={{ fontSize: 12, fontWeight: 600, color: '#1d2e38' }}>Bloco {b.id} — {b.label}</span>
                        <span style={{ marginLeft: 'auto', fontSize: 11, fontFamily: 'monospace', color }}>{b.filled}/{b.total}</span>
                      </div>
                      {b.missing.length > 0 && (
                        <div style={{ marginTop: 4, marginLeft: 24, fontSize: 11, color: '#64748b', lineHeight: 1.5 }}>Em falta: {b.missing.join(' · ')}</div>
                      )}
                    </div>
                  );
                })}

                {[{ 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: 8, padding: '8px 12px', background: b.suggested ? 'rgba(56,89,208,.07)' : b.empty ? '#f8fafc' : 'rgba(16,185,129,.06)', border: `1px solid ${b.suggested?'rgba(56,89,208,.2)':b.empty?'#f1f5f9':'rgba(16,185,129,.2)'}` }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                      <div style={{ width: 16, height: 16, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                        {b.suggested ? <IcLightning /> : b.empty
                          ? <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" strokeWidth="2.5" strokeLinecap="round"><line x1="5" y1="12" x2="19" y2="12"/></svg>
                          : <IcCheck />}
                      </div>
                      <span style={{ fontSize: 12, fontWeight: 600, color: '#1d2e38' }}>Bloco {b.id} — {b.label}</span>
                      <span style={{ marginLeft: 'auto', fontSize: 11, color: b.suggested ? '#3859D0' : b.empty ? '#94a3b8' : '#10b981', fontWeight: b.suggested ? 600 : 400 }}>
                        {b.suggested ? 'Sugerido por AI' : b.empty ? 'Não encontrado' : 'Extraído'}
                      </span>
                    </div>
                  </div>
                ))}

                {needsSkill && suggestPhase === 'idle' && (
                  <div style={{ padding: '12px 14px', borderRadius: 8, background: 'rgba(56,89,208,.06)', border: '1px solid rgba(56,89,208,.18)' }}>
                    <div style={{ fontSize: 12, color: '#475569', marginBottom: 10, lineHeight: 1.5 }}>
                      <strong style={{ color: '#3859D0' }}>Bloco{b4Empty && b5Empty ? 's 4 e 5' : b4Empty ? ' 4' : ' 5'} sem dados nas fontes.</strong>{' '}
                      Podes gerar agora com AI ou abrir o formulário e gerar depois.
                    </div>
                    <div style={{ display: 'flex', gap: 8 }}>
                      <button className="btn btn-ai" style={{ fontSize: 12 }} onClick={runSuggest}>
                        Gerar B{b4Empty && b5Empty ? '4 e B5' : b4Empty ? '4' : '5'} com AI
                      </button>
                    </div>
                  </div>
                )}
                {suggestPhase === 'loading' && (
                  <div style={{ padding: '12px 14px', borderRadius: 8, background: 'color-mix(in srgb, var(--ai-500,#3859D0) 6%, transparent)', border: '1px solid color-mix(in srgb, var(--ai-500,#3859D0) 20%, transparent)' }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
                      <div style={{ width: 14, height: 14, borderRadius: '50%', border: '2px solid color-mix(in srgb, var(--ai-500,#3859D0) 30%, transparent)', borderTopColor: 'var(--ai-500,#3859D0)', flexShrink: 0, animation: 'spin 0.8s linear infinite' }} />
                      <span style={{ fontSize: 12, fontWeight: 600, color: 'var(--ai-500,#3859D0)' }}>Digi AI a completar o Bloco 4 — Campanha</span>
                    </div>
                    <div style={{ fontSize: 11, color: '#64748b', lineHeight: 1.5, marginBottom: suggestMsgs.length ? 6 : 0 }}>
                      Alguns campos não foram encontrados nas fontes importadas. A Digi AI está a inferir objectivo, canais, KPIs, datas e oferta comercial com base no produto e ICP extraídos. O formulário abre quando terminar.
                    </div>
                    {suggestMsgs.length > 0 && (
                      <div style={{ fontSize: 10.5, color: '#94a3b8', fontFamily: 'var(--font-mono,monospace)', borderTop: '1px solid rgba(56,89,208,.1)', paddingTop: 5, marginTop: 2 }}>
                        {suggestMsgs[suggestMsgs.length - 1]}
                      </div>
                    )}
                  </div>
                )}
                {suggestPhase === 'done' && (
                  <div style={{ padding: '10px 14px', borderRadius: 8, background: 'color-mix(in srgb, var(--ai-500,#3859D0) 6%, transparent)', border: '1px solid color-mix(in srgb, var(--ai-500,#3859D0) 20%, transparent)', display: 'flex', alignItems: 'center', gap: 8 }}>
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--ai-500,#3859D0)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}><polyline points="20 6 9 17 4 12"/></svg>
                    <span style={{ fontSize: 12, color: 'var(--ai-500,#3859D0)', fontWeight: 500 }}>Bloco 4 completado pela Digi AI — objectivo, canais, KPIs, datas e oferta comercial preenchidos. Revê antes de guardar.</span>
                  </div>
                )}
                {suggestPhase === 'error' && (
                  <div style={{ padding: '10px 14px', borderRadius: 8, background: 'rgba(239,68,68,.06)', border: '1px solid rgba(239,68,68,.2)', display: 'flex', alignItems: 'center', gap: 8 }}>
                    <span style={{ fontSize: 12, color: '#ef4444', flex: 1 }}>{suggestError}</span>
                    <button className="btn" style={{ fontSize: 11 }} onClick={() => setSuggestPhase('idle')}>Tentar novamente</button>
                  </div>
                )}

                {productMissing && (
                  <div style={{ padding: '10px 12px', borderRadius: 8, background: 'rgba(245,158,11,.06)', border: '1px solid rgba(245,158,11,.2)', display: 'flex', gap: 10, alignItems: 'flex-start' }}>
                    <div style={{ flexShrink: 0, marginTop: 1 }}><IcWarn /></div>
                    <div>
                      <div style={{ fontSize: 12, fontWeight: 600, color: '#1d2e38', marginBottom: 3 }}>
                        "{doneData.block1?.commercial_name || 'Produto não detectado'}" não está na base de dados
                      </div>
                      <div style={{ fontSize: 11, color: '#64748b', lineHeight: 1.55 }}>
                        Pode ser um produto novo (ainda não registado) <strong>ou estar descontinuado</strong>. Verifica na tabela de preços antes de adicionar — só adiciona se confirmar que está activo.
                      </div>
                    </div>
                  </div>
                )}
              </div>
            );
          })()}

          {/* ── Done — 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))];
            return (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
                <div><label style={labelStyle}>MARCA</label><input value={newProd.brand_slug} disabled style={{ ...inpStyle, background: '#f8fafc', color: '#94a3b8' }} /></div>
                {hasSegments && (
                  <div><label style={labelStyle}>SEGMENTO</label>
                    <select value={newProd.segment} onChange={e => setNewProd(p => ({ ...p, segment: e.target.value, category: '' }))} style={inpStyle}>
                      <option value="">Escolher segmento...</option>
                      {segments.map(s => <option key={s} value={s}>{s}</option>)}
                    </select>
                  </div>
                )}
                <div><label style={labelStyle}>CATEGORIA</label>
                  <select value={newProd.category} onChange={e => setNewProd(p => ({ ...p, category: e.target.value }))} style={inpStyle}>
                    <option value="">{hasSegments && !newProd.segment ? 'Escolhe o segmento primeiro' : 'Escolher categoria...'}</option>
                    {categories.map(c => <option key={c} value={c}>{c}</option>)}
                  </select>
                </div>
                <div><label style={{ ...labelStyle, color: '#3859D0' }}>NOME DO PRODUTO</label>
                  <input value={newProd.name} onChange={e => setNewProd(p => ({ ...p, name: e.target.value }))} placeholder="Nome do produto" style={inpStyle} />
                </div>
              </div>
            );
          })()}

          {/* ── Error ── */}
          {phase === 'error' && (
            <div style={{ fontSize: 12, color: '#ef4444', background: 'rgba(239,68,68,.06)', borderRadius: 8, padding: '10px 14px' }}>{errorMsg}</div>
          )}
        </div>

        {/* Footer */}
        {phase !== 'loading' && (
          <div style={{ padding: '14px 24px', borderTop: '1px solid #f1f5f9', display: 'flex', gap: 8, justifyContent: 'space-between', alignItems: 'center', flexShrink: 0 }}>
            {phase === 'pick' && (
              <>
                <button onClick={onClose} className="btn" style={{ fontSize: 12 }}>Cancelar</button>
                <button onClick={runExtract} disabled={totalSources === 0} className="btn btn-ai" style={{ fontSize: 12 }}>
                  Analisar {totalSources > 0 ? `${totalSources} fonte${totalSources > 1 ? 's' : ''}` : ''} →
                </button>
              </>
            )}
            {phase === 'done' && !addStep && (
              <>
                <button onClick={resetPick} className="btn" style={{ fontSize: 12 }}>Extrair novamente</button>
                <div style={{ display: 'flex', gap: 8 }}>
                  {!doneData?.product_id && (
                    <button onClick={handleAddProduct} className="btn" style={{ fontSize: 12 }} title="Só adiciona se confirmares que o produto está activo na tabela de preços">+ Produto (verificar primeiro)</button>
                  )}
                  <button onClick={handleOpenForm} disabled={openingForm} className="btn btn-ai" style={{ fontSize: 12 }}>
                    {openingForm ? 'A preparar...' : 'Abrir formulário →'}
                  </button>
                </div>
              </>
            )}
            {phase === 'done' && addStep && (
              <>
                <button onClick={() => setAddStep(false)} className="btn" style={{ fontSize: 12 }}>← Voltar</button>
                <button onClick={handleSaveProd} disabled={savingProd || !newProd.name.trim()} className="btn btn-ai" style={{ fontSize: 12 }}>
                  {savingProd ? 'A guardar...' : 'Adicionar produto'}
                </button>
              </>
            )}
            {phase === 'error' && (
              <>
                <button onClick={onClose} className="btn" style={{ fontSize: 12 }}>Fechar</button>
                <button onClick={resetPick} className="btn btn-ai" style={{ fontSize: 12 }}>Tentar novamente</button>
              </>
            )}
          </div>
        )}
      </div>
    </div>
  );
};

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

const getMktUser = (userTipo, userEmail) => {
  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: null };
  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'  };
  const nome  = window.currentUser?.nome_apresentar || window.currentUser?.nome || null;
  const tipo  = userTipo || window.currentUser?.tipo || '';
  const email = userEmail || window.currentUser?.email || null;
  const role = tipo === 'admin' ? 'admin' : tipo === 'marketing' ? 'strategist' : 'board'; return { nome, role, email };
};

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

const MktBriefingsScreen = ({ userName, userTipo, userEmail, onNavToSub }) => {
  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.useMemo(() => getMktUser(userTipo, userEmail), [userTipo, userEmail]);

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

  const goList   = () => { setSelected(null); setView('list'); };
  const goNew    = (prefill) => {
    const data = prefill?._prefill ? { ...prefill._prefill, brand_slug: prefill._brand || undefined } : null;
    console.log('[goNew] prefill:', prefill, '→ data:', data, 'block1:', data?.block1);
    setSelected(data);
    setView('form');
  };
  const goEdit   = (b) => { if (b) setSelected(b); setView('form'); };
  const goDetail = (b) => { setSelected(b);   setView('detail'); };

  React.useEffect(() => {
    const hash = window.location.hash.slice(1);
    const params = Object.fromEntries(hash.split('&').map(p => p.split('=')));
    const deepId = params.id ? Number(params.id) : null;
    if (!deepId) return;
    window.MktBriefingsAPI.get(deepId)
      .then(b => { if (b?.id) goDetail(b); })
      .catch(() => {});
  }, []);

  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 {
      const res = await fetch('/api/marketing/campanhas', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ briefing_id: b.id, titulo, created_by: mktUser.email }),
      });
      if (res.status === 409) {
        // Já existe — abre a campanha existente como Ver Campanha
        await handleVerCampanha(b);
        return;
      }
      if (!res.ok) {
        const err = await res.json().catch(() => ({}));
        alert(`Erro ao criar campanha: ${err.error || res.status}`);
        return;
      }
      onNavToSub?.('campanhas');
    } catch (e) {
      alert(`Erro de rede: ${e.message}`);
    }
  };

  const handleVerCampanha = async (b) => {
    try {
      const list = await fetch('/api/marketing/campanhas').then(r => r.json());
      const camp = Array.isArray(list) && list.find(c => c.briefing_id === b.id);
      if (camp?.id) {
        try { localStorage.setItem('mkt-open-campanha-id', String(camp.id)); } catch {}
      }
    } catch {}
    onNavToSub?.('campanhas');
  };

  const handleDuplicar = async (b) => {
    try {
      const res = await fetch(`/api/marketing/briefings/${b.id}`, { headers: { 'Content-Type': 'application/json' } });
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      const full = await res.json();
      if (!full?.block1) throw new Error('Briefing incompleto');
      // Remove campos de sistema — abre como novo rascunho no formulário
      const { id, version_id, status, created_at, updated_at, current_version_id, approvals, ...rest } = full;
      setSelected({ ...rest, _duplicate_of: b.id });
      setView('form');
    } catch (e) {
      console.warn('[duplicar] erro:', e.message);
      alert(`Não foi possível duplicar o briefing: ${e.message}`);
    }
  };

  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) {
      const result = await window.MktBriefingsAPI.update(selected.id, { ...formData, ...aprecPayload });
      // Actualizar selected com o novo version_id para que edições seguintes façam PUT (não POST)
      if (result?.version_id) setSelected(s => ({ ...s, ...result }));
    } else {
      const importOrigin = selected?._import_origin || null;
      const created = await window.MktBriefingsAPI.create({ brand_id: bid, created_by_name: currentUserName, created_by_email: mktUser.email || null, import_origin: importOrigin, ...formData, ...aprecPayload });
      // Após criar, actualizar selected com o id real para que saves seguintes façam PUT
      if (created?.id) setSelected(s => ({ ...(s || {}), id: created.id, status: 'rascunho' }));
    }
    // Não fechar — mostrar confirmação inline no form
  };

  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, created_by_email: mktUser.email || null, 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, mktUser.email);
    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}
          onVerCampanha={handleVerCampanha}
          onDuplicar={handleDuplicar}
          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}
          onVerCampanha={handleVerCampanha}
          userRole={userRole}
          userName={currentUserName}
          userEmail={mktUser.email}
        />
      )}
      {view === 'detail' && (
        <BriefingDetail
          briefing={selected}
          onEdit={goEdit}
          onBack={goList}
          onApproved={goList}
          onRejected={goList}
          onGenerateStrategy={handleGenerateStrategy}
          onCriarCampanha={handleCriarCampanha}
          onVerCampanha={handleVerCampanha}
          userRole={userRole}
          userName={currentUserName}
          userEmail={mktUser.email}
        />
      )}
      {/* Tutorial — floating button + drawer */}
      {window.MktTutorial && (
        <window.MktTutorial
          moduleName="briefings"
          moduleLabel="Briefings"
          moduleState={{
            briefing: selected,
            apreciation: selected?.ai_apreciation,
          }}
          userEmail={mktUser.email}
        />
      )}
    </div>
  );
};

window.MktBriefingsScreen = MktBriefingsScreen;
