// ═══════════════════════════════════════════════════════
// BAUKO CRM — Clientes, Propostas, Atividades Screens
// ═══════════════════════════════════════════════════════
/* global React, CRM_DATA, Badge, Panel, Modal, FormField, AtivIcon, fR, fDate, fDateTime */
const { useState: useScreenState } = React;

// ══════════════════════════════════════════════════════
// CLIENTES
// ══════════════════════════════════════════════════════

// Segmentos e status: fonte única em CRM_CONFIG. Fallback inline roda só
// se CRM_CONFIG não carregou (ex: lista SP indisponível).
function getSegmentosCli() {
  return (window.CRM_CONFIG && CRM_CONFIG.get('cliente_segmentos')) || [
    "Construção Civil", "Mineração", "Locação / Prestação de Serviço",
    "Florestal", "Governo", "Indústria", "Agricultura",
  ];
}
function getStatusCli() {
  return (window.CRM_CONFIG && CRM_CONFIG.get('cliente_status')) || ["ativo","prospecto","inativo"];
}
const SEGMENTOS_CLI = getSegmentosCli();
const STATUS_CLI = getStatusCli();

// ── Máscaras de input ──────────────────────────────────────────────────────
function maskCNPJ(v) {
  const d = v.replace(/\D/g, '').slice(0, 14);
  if (d.length <= 2)  return d;
  if (d.length <= 5)  return d.slice(0,2) + '.' + d.slice(2);
  if (d.length <= 8)  return d.slice(0,2) + '.' + d.slice(2,5) + '.' + d.slice(5);
  if (d.length <= 12) return d.slice(0,2) + '.' + d.slice(2,5) + '.' + d.slice(5,8) + '/' + d.slice(8);
  return d.slice(0,2) + '.' + d.slice(2,5) + '.' + d.slice(5,8) + '/' + d.slice(8,12) + '-' + d.slice(12);
}
function maskCEP(v) {
  const d = v.replace(/\D/g, '').slice(0, 8);
  if (d.length <= 5) return d;
  return d.slice(0,5) + '-' + d.slice(5);
}
function maskPhone(v) {
  const d = v.replace(/\D/g, '').slice(0, 11);
  if (d.length === 0) return '';
  if (d.length <= 2)  return '(' + d;
  if (d.length <= 6)  return '(' + d.slice(0,2) + ') ' + d.slice(2);
  if (d.length <= 10) return '(' + d.slice(0,2) + ') ' + d.slice(2,6) + '-' + d.slice(6);
  return '(' + d.slice(0,2) + ') ' + d.slice(2,7) + '-' + d.slice(7);
}
function maskIE(v) {
  // IE varia por estado (SP: 12 dígitos pontuados; outros: formatos próprios)
  // Aceita dígitos, letras, pontos, hifens e barras — remove o resto
  return v.replace(/[^0-9A-Za-z.\-\/]/g, '').slice(0, 18).toUpperCase();
}
function maskCPF(v) {
  const d = v.replace(/\D/g, '').slice(0, 11);
  if (d.length <= 3)  return d;
  if (d.length <= 6)  return d.slice(0,3) + '.' + d.slice(3);
  if (d.length <= 9)  return d.slice(0,3) + '.' + d.slice(3,6) + '.' + d.slice(6);
  return d.slice(0,3) + '.' + d.slice(3,6) + '.' + d.slice(6,9) + '-' + d.slice(9);
}

// ── K-D1: Validações de formato (e-mail, CNPJ check digit, telefone) ─────────
function isEmailValido(s) {
  if (!s) return true; // vazio é OK — só valida se preenchido
  return /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(s.trim());
}
function isFoneValido(s) {
  if (!s) return true; // vazio OK
  var d = s.replace(/\D/g, '');
  return d.length === 10 || d.length === 11; // fixo (10) ou celular (11)
}
function isCNPJValido(s) {
  if (!s) return true; // vazio OK
  var d = s.replace(/\D/g, '');
  if (d.length !== 14) return false;
  if (/^(\d)\1{13}$/.test(d)) return false; // todos iguais
  // Cálculo dos dígitos verificadores
  var calc = function(t) {
    var n = d.substring(0, t);
    var s = 0;
    var p = t - 7;
    for (var i = t; i >= 1; i--) {
      s += parseInt(n.charAt(t - i), 10) * p--;
      if (p < 2) p = 9;
    }
    var r = s % 11;
    return r < 2 ? 0 : 11 - r;
  };
  return calc(12) === parseInt(d.charAt(12), 10) && calc(13) === parseInt(d.charAt(13), 10);
}
function isCPFValido(s) {
  if (!s) return true;
  var d = s.replace(/\D/g, '');
  if (d.length !== 11) return false;
  if (/^(\d)\1{10}$/.test(d)) return false;
  var sum = 0;
  for (var i = 0; i < 9; i++) sum += parseInt(d.charAt(i), 10) * (10 - i);
  var r = (sum * 10) % 11;
  if (r === 10) r = 0;
  if (r !== parseInt(d.charAt(9), 10)) return false;
  sum = 0;
  for (var j = 0; j < 10; j++) sum += parseInt(d.charAt(j), 10) * (11 - j);
  var r2 = (sum * 10) % 11;
  if (r2 === 10) r2 = 0;
  return r2 === parseInt(d.charAt(10), 10);
}

// ── Mock data — empresas para a busca CNPJ (ClienteModal · só PJ) ────────────
// Será substituído por chamadas reais a BAUKO_AUTH.CNPJ_API_URL quando o
// serviço local estiver disponível. Filtra por razão social ou CNPJ (case
// insensitive). Mantém os mesmos campos esperados da API real.
const MOCK_EMPRESAS_CNPJ = [
  {
    cnpj: '12345678000195', cnpj_formatado: '12.345.678/0001-95',
    razao_social: 'EMPRESA EXEMPLO LTDA', nome_fantasia: 'Exemplo',
    municipio_nome: 'São Paulo', uf: 'SP',
    logradouro: 'RUA EXEMPLO', numero: '100', complemento: '', bairro: 'CENTRO',
    cep: '01310100', telefone: '1133334444', email: 'contato@exemplo.com.br'
  },
  {
    cnpj: '11222333000144', cnpj_formatado: '11.222.333/0001-44',
    razao_social: 'CONSTRUTORA HORIZONTE S.A.', nome_fantasia: 'Horizonte Engenharia',
    municipio_nome: 'Campinas', uf: 'SP',
    logradouro: 'AV BRASIL', numero: '2500', complemento: 'SALA 401', bairro: 'JARDIM CHAPADAO',
    cep: '13070178', telefone: '1933221100', email: 'compras@horizonte.com.br'
  },
  {
    cnpj: '22333444000155', cnpj_formatado: '22.333.444/0001-55',
    razao_social: 'MINERADORA SERRA AZUL LTDA', nome_fantasia: 'Serra Azul Mineração',
    municipio_nome: 'Itabira', uf: 'MG',
    logradouro: 'RODOVIA MG-129 KM 12', numero: 'SN', complemento: '', bairro: 'ZONA RURAL',
    cep: '35900000', telefone: '3138888100', email: 'operacoes@serraazul.com.br'
  },
  {
    cnpj: '33444555000166', cnpj_formatado: '33.444.555/0001-66',
    razao_social: 'AGRO PLANALTO PARTICIPACOES LTDA', nome_fantasia: 'Agro Planalto',
    municipio_nome: 'Rio Verde', uf: 'GO',
    logradouro: 'BR-060 KM 25', numero: '500', complemento: '', bairro: 'DISTRITO INDUSTRIAL',
    cep: '75909999', telefone: '6433556677', email: 'comercial@agroplanalto.com.br'
  },
  {
    cnpj: '44555666000177', cnpj_formatado: '44.555.666/0001-77',
    razao_social: 'FLORESTAL VALE VERDE LTDA', nome_fantasia: 'Vale Verde Reflorestamento',
    municipio_nome: 'Telêmaco Borba', uf: 'PR',
    logradouro: 'ESTRADA DOS PINHEIROS', numero: '1234', complemento: '', bairro: 'COLONIA',
    cep: '84260000', telefone: '4232334455', email: 'contato@valeverde.com.br'
  },
  {
    cnpj: '55666777000188', cnpj_formatado: '55.666.777/0001-88',
    razao_social: 'LOCADORA MAQUINAS PESADAS BRASIL LTDA', nome_fantasia: 'LMPB Locações',
    municipio_nome: 'Rio de Janeiro', uf: 'RJ',
    logradouro: 'AV DAS AMERICAS', numero: '6800', complemento: 'BLOCO 2', bairro: 'BARRA DA TIJUCA',
    cep: '22793081', telefone: '2122334455', email: 'frota@lmpb.com.br'
  },
  {
    cnpj: '66777888000199', cnpj_formatado: '66.777.888/0001-99',
    razao_social: 'GOVERNO MUNICIPAL DE SERRA DOURADA', nome_fantasia: 'Prefeitura Serra Dourada',
    municipio_nome: 'Serra', uf: 'ES',
    logradouro: 'PRACA DA MATRIZ', numero: '1', complemento: '', bairro: 'CENTRO',
    cep: '29160000', telefone: '2733998877', email: 'licitacao@serradourada.es.gov.br'
  },
  {
    cnpj: '77888999000110', cnpj_formatado: '77.888.999/0001-10',
    razao_social: 'INDUSTRIA METALURGICA ATLANTICA LTDA', nome_fantasia: 'Atlântica Metais',
    municipio_nome: 'Joinville', uf: 'SC',
    logradouro: 'RUA DO COMERCIO', numero: '850', complemento: '', bairro: 'DISTRITO INDUSTRIAL',
    cep: '89219501', telefone: '4734221199', email: 'industrial@atlanticametais.com.br'
  }
];

// ════════════════════════════════════════════════════════════════════════════
// DADOS PÚBLICOS — mock + helpers + componentes da aba do detalhe do cliente
// ════════════════════════════════════════════════════════════════════════════
// Retorno simulado do endpoint GET {CNPJ_API_URL}/empresa/{cnpj}.
// Mesmo schema da API real (BAUKO_AUTH.CNPJ_API_URL — serviço local).
// TODO: substituir mock por fetch real quando a API estiver disponível
//       (ver função fetchEmpresaCNPJ abaixo — bloco com o TODO em destaque).
const MOCK_EMPRESA_DETALHE = {
  cnpj_raw: '12345678000195',
  cnpj_formatado: '12.345.678/0001-95',
  razao_social: 'EMPRESA EXEMPLO LTDA',
  nome_fantasia: 'Exemplo',
  situacao_cadastral: '02', // 02 = Ativa
  data_inicio_atividade: '20100315',
  porte: '03',              // 03 = EPP
  capital_social: 150000.00,
  cnae_principal: '4212000',
  cnae_descricao: 'Construção de obras de arte especiais',
  cnae_secundarios: '4211101;4213800',
  natureza_juridica: '2062',
  natureza_descricao: 'Sociedade Empresária Limitada',
  logradouro: 'RUA EXEMPLO',
  numero: '100',
  complemento: 'SALA 1',
  bairro: 'CENTRO',
  cep: '01310100',
  municipio_nome: 'São Paulo',
  uf: 'SP',
  telefone: '1133334444',
  email: 'contato@exemplo.com.br',
  matriz_filial: '1',
  opcao_simples: 'S',
  opcao_mei: 'N',
  filiais: [
    { cnpj_formatado: '12.345.678/0002-76', municipio_nome: 'Campinas', uf: 'SP', situacao_cadastral: '02' }
  ],
  socios: [
    { nome_socio: 'JOÃO DA SILVA',    qualificacao: '49', data_entrada: '20100315', faixa_etaria: '5' },
    { nome_socio: 'MARIA OLIVEIRA',   qualificacao: '22', data_entrada: '20150601', faixa_etaria: '4' }
  ]
};

// Tabelas de códigos da Receita
const SITUACAO_CADASTRAL_PUB = {
  '01': { label: 'Nula',     cor: '#737373', bg: '#f5f5f5' },
  '02': { label: 'Ativa',    cor: '#15803d', bg: '#dcfce7' },
  '03': { label: 'Suspensa', cor: '#a16207', bg: '#fef9c3' },
  '04': { label: 'Inapta',   cor: '#c2410c', bg: '#ffedd5' },
  '08': { label: 'Baixada',  cor: '#b91c1c', bg: '#fee2e2' },
};
const PORTE_EMPRESA_PUB = {
  '00': 'Não informado',
  '01': 'MEI',
  '03': 'EPP',
  '05': 'Demais',
};
// Subset mais comum das qualificações de sócio (RFB). Cód. não mapeado vira "Cód. XX".
const QUALIFICACAO_SOCIO_PUB = {
  '05': 'Administrador',
  '08': 'Conselheiro de Administração',
  '10': 'Diretor',
  '16': 'Presidente',
  '17': 'Procurador',
  '22': 'Sócio',
  '37': 'Sócio Ostensivo',
  '49': 'Sócio-Administrador',
  '52': 'Sócio Comanditado',
  '54': 'Sócio Comanditário',
  '65': 'Titular Pessoa Física Residente ou Domiciliado no Brasil',
};
const FAIXA_ETARIA_PUB = {
  '0': 'Não informado',
  '1': 'Até 12 anos',
  '2': '13 a 20 anos',
  '3': '21 a 30 anos',
  '4': '31 a 40 anos',
  '5': '41 a 50 anos',
  '6': '51 a 60 anos',
  '7': '61 a 70 anos',
  '8': '71 a 80 anos',
  '9': 'Maiores de 80 anos',
};

// ── Formatadores ────────────────────────────────────────────────────────────
function fmtDataReceitaPub(s) {
  if (!s || s.length !== 8) return s || '—';
  return s.slice(6,8) + '/' + s.slice(4,6) + '/' + s.slice(0,4);
}
function anosDesdePub(s) {
  if (!s || s.length !== 8) return null;
  const dt = new Date(parseInt(s.slice(0,4),10), parseInt(s.slice(4,6),10) - 1, parseInt(s.slice(6,8),10));
  const hoje = new Date();
  let anos = hoje.getFullYear() - dt.getFullYear();
  const m = hoje.getMonth() - dt.getMonth();
  if (m < 0 || (m === 0 && hoje.getDate() < dt.getDate())) anos--;
  return anos;
}
function fmtMoedaBRPub(n) {
  const num = Number(n);
  if (n == null || n === '' || isNaN(num)) return '—';
  return num.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL', minimumFractionDigits: 2 });
}
function fmtCEPRawPub(s) {
  const d = (s || '').replace(/\D/g, '');
  if (d.length !== 8) return s || '—';
  return d.slice(0,5) + '-' + d.slice(5);
}
// Range de combining diacritical marks (̀-ͯ) construído via fromCharCode
// para evitar caracteres combining inline no source — mais seguro com Babel CDN.
const DIACRITICOS_RE = new RegExp('[' + String.fromCharCode(0x0300) + '-' + String.fromCharCode(0x036f) + ']', 'g');
function normalizaNomePub(s) {
  return (s || '')
    .toUpperCase()
    .normalize('NFD').replace(DIACRITICOS_RE, '')
    .replace(/\s+/g, ' ').trim();
}

// ── fetchEmpresaCNPJ — busca dados públicos por CNPJ ────────────────────────
// Hoje retorna mock. Quando BAUKO_AUTH.CNPJ_API_URL estiver no ar, trocar o
// bloco MOCK pelo bloco REAL (já comentado abaixo).
async function fetchEmpresaCNPJ(cnpj) {
  const digitos = (cnpj || '').replace(/\D/g, '');
  if (!digitos) throw new Error('CNPJ inválido');

  const base = window.BAUKO_AUTH && window.BAUKO_AUTH.CNPJ_API_URL;
  if (!base || base.includes('SEU_IP')) throw new Error('CNPJ_API_URL não configurado em auth-config.js');

  // CNPJ completo tem 14 dígitos. A Receita às vezes armazena o cpf_cnpj_socio
  // de sócios PJ sem leading zeros (ex: "303987" = basico "00303987"). Pads para
  // 14 dígitos com zeros à esquerda e tenta; se a API ainda retornar 404, propaga.
  const cnpjBusca = digitos.length < 14 ? digitos.padStart(14, '0') : digitos;

  const resp = await fetch(base.replace(/\/$/, '') + '/empresa/' + cnpjBusca);
  if (resp.status === 404) throw new Error('NOT_FOUND');
  if (!resp.ok) throw new Error('HTTP ' + resp.status);
  return await resp.json();
}

// ── fetchSociosDoCPFOuCNPJ — empresas onde a pessoa/empresa aparece como sócio
// Hoje retorna mock. Quando BAUKO_AUTH.CNPJ_API_URL estiver no ar, trocar pelo
// fetch real abaixo.
async function fetchSociosDoCPFOuCNPJ(docRaw, nomeFallback) {
  const base = window.BAUKO_AUTH && window.BAUKO_AUTH.CNPJ_API_URL;
  if (!base || base.includes('SEU_IP')) throw new Error('CNPJ_API_URL não configurado em auth-config.js');
  if (!docRaw) throw new Error('CPF/CNPJ ausente');
  // CPF mascarado (***123456**) precisa de encodeURIComponent para preservar os '*'
  // CNPJ de PJ: só dígitos, sem risco na URL
  const lookup = docRaw.includes('*') ? docRaw : docRaw.replace(/\D/g, '');
  if (!lookup) throw new Error('CPF/CNPJ ausente');
  const url = base.replace(/\/$/, '') + '/socios/' + encodeURIComponent(lookup)
            + (nomeFallback ? '?nome=' + encodeURIComponent(nomeFallback) : '');
  const resp = await fetch(url);
  if (resp.status === 404) return { empresas: [], capital_total: 0 };
  if (!resp.ok) throw new Error('HTTP ' + resp.status);
  const json = await resp.json();
  const empresas = json.empresas || [];
  const capital_total = empresas.reduce((s, e) => s + (Number(e.capital_social) || 0), 0);
  return { empresas, capital_total };
}

// ── Cache de sessão para a Teia Societária ───────────────────────────────────
// Armazena Promises (não dados): segunda chamada ao mesmo CNPJ reutiliza o
// fetch em andamento; chamadas após resolve são instantâneas. Em caso de erro
// o item é removido para que um retry funcione normalmente.
var _teiaCacheEmpresa = new Map();  // cnpj14 -> Promise<dados>
var _teiaCacheSocios  = new Map();  // lookup  -> Promise<{empresas,capital_total}>

function _teiaFetchEmpresa(cnpj) {
  var raw = (cnpj || '').replace(/\D/g, '');
  // CGC legado: numeração antiga com < 8 dígitos — não existe na base CNPJ RF
  if (raw.length < 8) {
    var skipErr = new Error('CGC legado ignorado (' + raw + ')');
    skipErr.code = 'CGC_SKIP';
    return Promise.reject(skipErr);
  }
  var key = raw.padStart(14, '0');
  if (!_teiaCacheEmpresa.has(key)) {
    _teiaCacheEmpresa.set(key, fetchEmpresaCNPJ(key).catch(function(e) {
      _teiaCacheEmpresa.delete(key);
      throw e;
    }));
  }
  return _teiaCacheEmpresa.get(key);
}

function _teiaFetchSocios(docRaw, nomeFallback) {
  var lookup = (docRaw || '').includes('*') ? docRaw : (docRaw || '').replace(/\D/g, '');
  if (!lookup) return fetchSociosDoCPFOuCNPJ(docRaw, nomeFallback);
  var key = lookup + '|' + (nomeFallback || '');
  if (!_teiaCacheSocios.has(key)) {
    _teiaCacheSocios.set(key, fetchSociosDoCPFOuCNPJ(docRaw, nomeFallback).catch(function(e) {
      _teiaCacheSocios.delete(key);
      throw e;
    }));
  }
  return _teiaCacheSocios.get(key);
}

// ── Helpers de mutação do grafo Teia ─────────────────────────────────────────

var _TEIA_LAYOUT_OPTS = {
  name: 'cose', animate: false, padding: 48,
  idealEdgeLength: 140, nodeRepulsion: 8192, nodeOverlap: 40,
  gravity: 0.15, numIter: 3000, coolingFactor: 0.95, minTemp: 1.0,
};

// Adiciona nó ou atualiza label se a nova versão for mais longa
// (Receita trunca razão social de formas diferentes conforme a tabela)
function _addTeiaNo(cy, id, dados) {
  var n = cy.getElementById(id);
  if (!n.empty()) {
    if ((dados.label || '').length > (n.data('label') || '').length) {
      n.data('label', dados.label);
    }
    return false; // já existia
  }
  cy.add({ group: 'nodes', data: Object.assign({ id: id, grau: 1 }, dados) });
  return true; // novo
}

// Adiciona aresta ou mescla qualificação se já existir aresta entre os mesmos nós
// (elimina arestas paralelas, ex: "Sócio" + "Administrador" → "Sócio, Administrador")
function _addTeiaAresta(cy, source, target, qualificacao) {
  var eid    = 'e_' + source + '_' + target;
  var eidRev = 'e_' + target + '_' + source;
  var ex = cy.getElementById(eid);
  if (ex.empty()) ex = cy.getElementById(eidRev);
  if (!ex.empty()) {
    var cur = ex.data('label') || '';
    var q   = qualificacao || '';
    if (q && cur.split(', ').indexOf(q) === -1) {
      ex.data('label', cur ? cur + ', ' + q : q);
      ex.data('peso', (ex.data('peso') || 1) + 1);
    }
  } else {
    cy.add({ group: 'edges', data: { id: eid, source: source, target: target,
                                     label: qualificacao || '', peso: 1 } });
  }
}

// Recalcula grau (degree) de todos os nós — chamado após cada mutação do grafo
function _atualizarGraus(cy) {
  cy.nodes().forEach(function(n) {
    n.data('grau', Math.max(1, n.degree(false)));
  });
}

// Layout incremental: roda cose completo para grafos pequenos;
// para grafos grandes posiciona novos nós radialmente e só dá fit.
function _teiaLayout(cy, parentNode, novosIds) {
  _atualizarGraus(cy);
  if (cy.nodes().length <= 30) {
    cy.layout(_TEIA_LAYOUT_OPTS).run();
    return;
  }
  if (novosIds && novosIds.length > 0) {
    var pos = parentNode.position ? parentNode.position() : { x: 0, y: 0 };
    var r = 120;
    novosIds.forEach(function(nid, i) {
      var n = cy.getElementById(nid);
      if (n.empty()) return;
      var angle = (2 * Math.PI * i) / novosIds.length - Math.PI / 2;
      n.position({ x: pos.x + r * Math.cos(angle), y: pos.y + r * Math.sin(angle) });
    });
  }
  try { cy.fit(cy.nodes(), 48); } catch(e) {}
}

// ── TeiaSocietaria — grafo Cytoscape lazy (só monta ao clicar "Ver teia") ───
function TeiaSocietaria({ empresa, onNav }) {
  const [aberta,     setAberta]     = useScreenState(false);
  const [expandindo, setExpandindo] = useScreenState(null);
  // Painel lateral do sócio selecionado — exibido sobre o canvas Cytoscape
  const [socio,         setSocio]         = useScreenState(null);  // { nome, doc, qualificacao, data_entrada, tipo }
  const [socioLoading,  setSocioLoading]  = useScreenState(false);
  const [socioErro,     setSocioErro]     = useScreenState(null);
  const [socioEmpresas, setSocioEmpresas] = useScreenState(null);  // { empresas, capital_total, _mock? }
  const [teiaExpandida, setTeiaExpandida] = useScreenState(false); // tela cheia
  const [rfExpandido,   setRfExpandido]   = useScreenState({});    // { [cnpj_raw]: dadosRF | 'loading' | 'erro:...' }
  const containerRef = React.useRef(null);
  const cyRef        = React.useRef(null);
  // Refs pros handlers Cytoscape verem sempre o state/onNav mais recente
  const onNavRef     = React.useRef(onNav);
  React.useEffect(() => { onNavRef.current = onNav; }, [onNav]);

  // Re-aplica ícones lucide quando o painel abre/atualiza ou expansão muda
  React.useEffect(() => {
    try { if (window.lucide) window.lucide.createIcons({ attrs: { 'stroke-width': 1.75 } }); }
    catch(e) {}
  });

  // Resize Cytoscape ao expandir/recolher teia (aguarda DOM atualizar)
  React.useEffect(() => {
    var cy = cyRef.current;
    if (!cy) return;
    var t = setTimeout(function() { try { cy.resize(); cy.fit(); } catch(e){} }, 60);
    return function() { clearTimeout(t); };
  }, [teiaExpandida]);

  // ESC fecha teia expandida
  React.useEffect(() => {
    if (!teiaExpandida) return;
    function onEsc(e) { if (e.key === 'Escape') setTeiaExpandida(false); }
    window.addEventListener('keydown', onEsc);
    return function() { window.removeEventListener('keydown', onEsc); };
  }, [teiaExpandida]);

  React.useEffect(() => {
    if (!aberta || !containerRef.current) return;
    if (!window.cytoscape) {
      console.warn('[CRM] cytoscape não carregado — adicione o <script> do Cytoscape ao index.html');
      return;
    }

    // ── Modelo: nó = empresa (cnpj_basico, 8 dígitos), não unidade. ──────────
    // QSA pertence à empresa, não ao estabelecimento (matriz e filiais
    // compartilham o mesmo quadro societário na RF). Por isso a chave do nó
    // é o cnpj_basico — matriz e filiais colapsam num único nó.
    // O cnpj_raw 14 dígitos (representativo) vai no data.cnpj para uso
    // em chamadas /empresa/{cnpj} durante expansão.
    const _rootRaw  = (empresa.cnpj_raw || '').replace(/\D/g, '');
    const _rootCnpj = _rootRaw.length > 0 && _rootRaw.length < 14
      ? _rootRaw.padStart(14, '0')
      : _rootRaw;
    const rootId    = _rootCnpj.substring(0, 8);  // cnpj_basico → chave do nó

    // ── Constrói elementos iniciais com deduplicação ──────────────────────────
    // Usa Maps para garantir nó único por CPF/CNPJ e aresta única por par de nós
    const nodesMap = new Map();
    const edgesMap = new Map();

    function _initNo(id, dados) {
      if (nodesMap.has(id)) {
        var ex = nodesMap.get(id);
        if ((dados.label || '').length > (ex.data.label || '').length)
          ex.data.label = dados.label;
      } else {
        nodesMap.set(id, { data: Object.assign({ id: id, grau: 1 }, dados) });
      }
    }
    function _initAresta(src, tgt, qual) {
      var eid    = 'e_' + src + '_' + tgt;
      var eidRev = 'e_' + tgt + '_' + src;
      var key = edgesMap.has(eid) ? eid : (edgesMap.has(eidRev) ? eidRev : eid);
      if (edgesMap.has(key)) {
        var ex = edgesMap.get(key);
        var q  = qual || '';
        if (q && ex.data.label.split(', ').indexOf(q) === -1)
          ex.data.label = ex.data.label ? ex.data.label + ', ' + q : q;
        ex.data.peso = (ex.data.peso || 1) + 1;
      } else {
        edgesMap.set(eid, { data: { id: eid, source: src, target: tgt, label: qual || '', peso: 1 } });
      }
    }

    _initNo(rootId, {
      label: empresa.razao_social, tipo: 'empresa',
      qualificacao: 'Empresa atual',
      cnpj:   _rootCnpj,   // 14 dígitos representativo (matriz/unidade que abriu a teia)
      basico: rootId,      // 8 dígitos (mesma do id)
      data_entrada: '', expandido: 'true',
    });

    (empresa.socios || []).forEach(function(s) {
      var ehPJ    = s.identificador_socio === '1';  // '1'=PJ '2'=PF (Receita)
      var docRaw  = s.cpf_cnpj_socio || '';
      var doc     = docRaw.replace(/\D/g, '');
      // CGC legado: numeração pré-CNPJ com < 8 dígitos — não existe na RF, skip
      if (ehPJ && doc.length > 0 && doc.length < 8) return;
      // PJ: chave de nó = cnpj_basico (8 dig). Matriz e filiais colapsam num único nó.
      // PF: chave = 'pf_' + docRaw (mesmo CPF aparece como mesmo nó em outras empresas).
      var cnpjPJ      = ehPJ ? (doc.length < 14 ? doc.padStart(14, '0') : doc) : null;
      var basicoPJ    = ehPJ && cnpjPJ ? cnpjPJ.substring(0, 8) : null;
      var sid         = ehPJ ? basicoPJ : ('pf_' + docRaw);
      var qual    = QUALIFICACAO_SOCIO_PUB[s.qualificacao_socio] || ('Cód. ' + (s.qualificacao_socio || ''));
      _initNo(sid, {
        label: s.nome_socio, tipo: ehPJ ? 'pj' : 'pf',
        qualificacao: qual,
        cnpj:   cnpjPJ,    // 14 dig representativo para fetch /empresa
        basico: basicoPJ,  // 8 dig (chave)
        doc: doc, doc_raw: docRaw,
        data_entrada: s.data_entrada_sociedade || '',
        expandido: 'false',
      });
      _initAresta(rootId, sid, qual);
    });

    // Pré-computa grau inicial para sizing proporcional
    var nodesArr = Array.from(nodesMap.values());
    var edgesArr = Array.from(edgesMap.values());
    var degMap   = {};
    nodesArr.forEach(function(n) { degMap[n.data.id] = 0; });
    edgesArr.forEach(function(e) {
      degMap[e.data.source] = (degMap[e.data.source] || 0) + 1;
      degMap[e.data.target] = (degMap[e.data.target] || 0) + 1;
    });
    nodesArr.forEach(function(n) { n.data.grau = Math.max(1, degMap[n.data.id] || 1); });

    // Pré-fetch silencioso dos sócios PJ do nó raiz ('1'=PJ '2'=PF na Receita)
    (empresa.socios || []).forEach(function(s) {
      if (s.identificador_socio === '1') {
        var doc = (s.cpf_cnpj_socio || '').replace(/\D/g, '');
        if (doc && doc.length >= 8) _teiaFetchEmpresa(doc).catch(function(){});
      }
    });

    // ── Instância Cytoscape ───────────────────────────────────────────────────
    const cy = window.cytoscape({
      container: containerRef.current,
      elements: nodesArr.concat(edgesArr),
      style: [
        // Base de todos os nós: tamanho proporcional ao grau
        // Wrap padrão (em espaços) — sem 'anywhere', que fazia o canvas elidir
        // o glyph do espaço ao quebrar dentro de palavras (gerava "BAUKOMAQUINASS/A" colado).
        { selector: 'node', style: {
            'label': 'data(label)',
            'text-valign': 'bottom', 'text-margin-y': 8,
            'font-size': 10, 'font-family': 'system-ui, sans-serif', 'color': '#404040',
            'width':  'mapData(grau, 1, 10, 26, 58)',
            'height': 'mapData(grau, 1, 10, 26, 58)',
            'border-width': 2, 'border-color': '#ffffff',
            'text-wrap': 'wrap', 'text-max-width': 140,
        }},
        // PF — cinza escuro
        { selector: 'node[tipo="pf"]', style: {
            'background-color': '#525252',
        }},
        // PJ (sócio pessoa jurídica) — azul corporativo
        { selector: 'node[tipo="pj"]', style: {
            'background-color': '#2563eb',
        }},
        // Empresa raiz — azul escuro, tamanho mínimo maior
        { selector: 'node[tipo="empresa"]', style: {
            'background-color': '#1e3a8a',
            'width':  'mapData(grau, 1, 10, 52, 70)',
            'height': 'mapData(grau, 1, 10, 52, 70)',
            'font-weight': 'bold', 'font-size': 10,
        }},
        // Empresa relacionada (via /socios) — azul médio, borda tracejada
        { selector: 'node[tipo="empresa_rel"]', style: {
            'background-color': '#0369a1',
            'border-style': 'dashed', 'border-width': 2, 'border-color': '#7dd3fc',
        }},
        // Nós já expandidos — borda verde sólida
        { selector: 'node[expandido="true"][tipo!="empresa"]', style: {
            'border-color': '#15803d', 'border-width': 4, 'border-style': 'solid',
        }},
        // Arestas: espessura proporcional ao número de papéis (peso)
        { selector: 'edge', style: {
            'width': 'mapData(peso, 1, 3, 1.5, 4)',
            'line-color': '#d4d4d8', 'curve-style': 'bezier',
            'target-arrow-shape': 'none',
        }},
        // Label da aresta quando há papel definido
        { selector: 'edge[label != ""]', style: {
            'label': 'data(label)',
            'font-size': 8, 'color': '#71717a',
            'text-rotation': 'autorotate', 'text-margin-y': -8,
            'text-background-color': '#ffffff', 'text-background-opacity': 0.75,
            'text-background-padding': '2px',
        }},
      ],
      layout: { name: 'preset' },   // posicao inicial no centro; layout real roda apos paint
      wheelSensitivity: 0.2,
    });

    // Roda o layout apos o React pintar o container (container tem altura 0 antes do paint)
    var _layoutTimer = setTimeout(function() {
      try { _teiaLayout(cy, cy.getElementById(rootId), []); cy.fit(cy.nodes(), 48); } catch(e) {}
    }, 80);

    // ── Tooltip ao passar o mouse ─────────────────────────────────────────────
    let tip = null;
    function moveTip(e) {
      if (!tip) return;
      tip.style.left = (e.clientX + 12) + 'px';
      tip.style.top  = (e.clientY + 12) + 'px';
    }
    cy.on('mouseover', 'node', (evt) => {
      const d = evt.target.data();
      tip = document.createElement('div');
      tip.style.cssText = 'position:fixed;background:#171717;color:#fff;font:11px/1.4 system-ui;padding:6px 10px;border-radius:6px;pointer-events:none;z-index:9999;max-width:260px;box-shadow:0 4px 12px rgba(0,0,0,0.2)';
      var html = '<strong>' + d.label + '</strong>';
      if (d.qualificacao) html += '<br><span style="opacity:.75">' + d.qualificacao + '</span>';
      if (d.data_entrada) html += '<br><span style="opacity:.6">Entrou em: ' + fmtDataReceitaPub(d.data_entrada) + '</span>';
      if (d.cnpj)         html += '<br><span style="opacity:.5;font-size:10px">' + d.cnpj + '</span>';
      tip.innerHTML = html;
      document.body.appendChild(tip);
      document.addEventListener('mousemove', moveTip);
    });
    cy.on('mouseout', 'node', () => {
      document.removeEventListener('mousemove', moveTip);
      if (tip) { try { tip.remove(); } catch(e){} tip = null; }
    });

    // ── Painel lateral ao clicar em qualquer nó ───────────────────────────────
    cy.on('tap', 'node', (evt) => {
      const d = evt.target.data();
      const docRaw = d.doc_raw || d.doc || d.cnpj || '';
      const socioInfo = {
        nome: d.label, tipo: d.tipo,
        qualificacao: d.qualificacao || '',
        data_entrada: d.data_entrada || '',
        doc: d.doc || d.cnpj || '',
        doc_raw: docRaw,
      };
      setSocio(socioInfo);
      setSocioEmpresas(null);
      setSocioErro(null);

      if (d.tipo === 'empresa') {
        setSocioEmpresas({ empresas: [], capital_total: Number(empresa.capital_social) || 0, _proprio: true });
        return;
      }

      if (!docRaw) { setSocioLoading(false); return; }
      setSocioLoading(true);
      _teiaFetchSocios(docRaw, socioInfo.nome)
        .then(r => {
          setSocioEmpresas(r);
          var novosNosSocios = [];
          (r.empresas || []).forEach(function(emp) {
            var empCnpjRaw = (emp.cnpj_raw || '').replace(/\D/g, '');
            if (!empCnpjRaw) return;
            var empCnpj14  = empCnpjRaw.length < 14 ? empCnpjRaw.padStart(14, '0') : empCnpjRaw;
            var empBasico  = empCnpj14.substring(0, 8);
            if (empBasico === rootId) return;  // mesma empresa que a raiz (matriz ou outra filial)
            var ehNovo = _addTeiaNo(cy, empBasico, {
              label: emp.razao_social || empBasico,
              tipo: 'empresa_rel',
              cnpj:   empCnpj14,
              basico: empBasico,
              doc: empBasico, doc_raw: empBasico,
              expandido: 'false',
            });
            // Aresta entre o nó clicado e a empresa relacionada
            _addTeiaAresta(cy, d.id, empBasico, '');
            if (ehNovo) novosNosSocios.push({ basico: empBasico, cnpj14: empCnpj14 });
          });
          if (novosNosSocios.length > 0) {
            _teiaLayout(cy, evt.target, novosNosSocios.map(function(x){ return x.basico; }));
          }
          // Pre-fetch usa o CNPJ representativo (14 dig) — backend resolve via cnpj_basico
          novosNosSocios.forEach(function(x) {
            _teiaFetchEmpresa(x.cnpj14).catch(function(){});
          });
        })
        .catch(e => { setSocioErro(e.message || 'ERRO'); })
        .finally(() => { setSocioLoading(false); });
    });

    // ── Expansão de QSA ao clicar em nó PJ ou empresa_rel ────────────────────
    cy.on('tap', 'node[tipo="pj"], node[tipo="empresa_rel"]', async (evt) => {
      const node     = evt.target;
      if (node.data('expandido') === 'true') return;
      const cnpjAlvo = node.data('cnpj');   // 14 dig representativo
      const sourceId = node.id();           // 8 dig (cnpj_basico — chave do nó)
      if (!cnpjAlvo || cnpjAlvo.replace(/\D/g, '').length < 8) return; // CGC legado
      try {
        setExpandindo(node.id());
        node.data('expandido', 'true');
        // _teiaFetchEmpresa: cache de Promise → instantâneo se pré-fetch já rodou
        const dadosFilho = await _teiaFetchEmpresa(cnpjAlvo);
        const novosNos = [];
        (dadosFilho.socios || []).forEach(function(s) {
          var ehPJ    = s.identificador_socio === '1';  // '1'=PJ '2'=PF (Receita)
          var docRaw  = s.cpf_cnpj_socio || '';
          var doc     = docRaw.replace(/\D/g, '');
          // CGC legado: numeração pré-CNPJ com < 8 dígitos — não existe na RF, skip
          if (ehPJ && doc.length > 0 && doc.length < 8) return;
          var cnpjPJ      = ehPJ ? (doc.length < 14 ? doc.padStart(14, '0') : doc) : null;
          var basicoPJ    = ehPJ && cnpjPJ ? cnpjPJ.substring(0, 8) : null;
          var sid         = ehPJ ? basicoPJ : ('pf_' + docRaw);  // cnpj_basico (PJ) ou pf_doc (PF)
          var qual    = QUALIFICACAO_SOCIO_PUB[s.qualificacao_socio] || ('Cód. ' + (s.qualificacao_socio || ''));
          var ehNovo  = _addTeiaNo(cy, sid, {
            label: s.nome_socio, tipo: ehPJ ? 'pj' : 'pf',
            qualificacao: qual,
            cnpj:   cnpjPJ,    // 14 dig representativo
            basico: basicoPJ,  // 8 dig (chave)
            doc: doc, doc_raw: docRaw,
            data_entrada: s.data_entrada_sociedade || '',
            expandido: 'false',
          });
          _addTeiaAresta(cy, sourceId, sid, qual);   // source = id do nó (cnpj_basico)
          if (ehNovo && ehPJ && cnpjPJ) novosNos.push(cnpjPJ);
        });
        _teiaLayout(cy, node, novosNos);
        novosNos.forEach(function(cnpjFilho) {
          _teiaFetchEmpresa(cnpjFilho).catch(function(){});
        });
      } catch (e) {
        console.warn('[CRM] expansão de teia falhou:', e.message);
        node.data('expandido', 'false'); // permite retry
      } finally {
        setExpandindo(null);
      }
    });

    cyRef.current = cy;
    return () => {
      clearTimeout(_layoutTimer);
      document.removeEventListener('mousemove', moveTip);
      if (tip) { try { tip.remove(); } catch(e){} }
      try { cy.destroy(); } catch(e){}
      cyRef.current = null;
    };
  }, [aberta, empresa.cnpj_raw]);

  if (!aberta) {
    return (
      <div style={{ padding: '28px 16px', textAlign: 'center' }}>
        <button className="btn btn-secondary" onClick={() => setAberta(true)}>
          <i data-lucide="git-fork" style={{width:14,height:14}}></i> Ver teia societária
        </button>
        <div style={{ marginTop: 8, font: '400 11px/1.4 var(--ff-body)', color: 'var(--tx-3)' }}>
          Grafo de sócios e (futuramente) empresas relacionadas. Carregamento sob demanda.
        </div>
      </div>
    );
  }

  // Ao clicar em "Ver empresa": tenta achar cliente Bauko por CNPJ. Se achar,
  // dispara CustomEvent + navega para a tela Clientes (listener no Clientes
  // pré-seleciona o registro). Se não achar, alerta o usuário.
  function verEmpresa(cnpjFormatado) {
    var digits = (cnpjFormatado || '').replace(/\D/g, '');
    var match  = (CRM_DATA.clientes || []).find(function(c) {
      return (c.cnpj || '').replace(/\D/g, '') === digits;
    });
    if (match) {
      try { window.dispatchEvent(new CustomEvent('crm_open_cliente', { detail: { id: match.id } })); } catch(e){}
      if (typeof onNavRef.current === 'function') onNavRef.current('clientes');
    } else {
      window.CRM_TOAST('Esta empresa não está cadastrada como cliente Bauko.', 'warn');
    }
  }

  function fecharPainelSocio() {
    setSocio(null); setSocioEmpresas(null); setSocioErro(null); setSocioLoading(false);
  }

  return (
    <div>
      <div style={teiaExpandida ? {
        position: 'fixed', inset: 0, zIndex: 1000,
        background: 'var(--surface)', padding: 8
      } : { position: 'relative' }}>
        {/* Botão expandir / recolher */}
        <button onClick={() => setTeiaExpandida(function(v){ return !v; })}
                title={teiaExpandida ? 'Recolher (Esc)' : 'Expandir teia em tela cheia'}
                style={{ position: 'absolute', top: teiaExpandida ? 16 : 8, left: teiaExpandida ? 16 : 8,
                         zIndex: 10, all: 'unset', cursor: 'pointer',
                         padding: '4px 8px', background: 'var(--surface)',
                         border: '1px solid var(--border)', borderRadius: 6,
                         color: 'var(--tx-2)', display: 'inline-flex', alignItems: 'center', gap: 4,
                         font: '400 11px/1 var(--ff-body)',
                         boxShadow: '0 1px 4px rgba(0,0,0,0.08)' }}>
          <i data-lucide={teiaExpandida ? 'minimize-2' : 'maximize-2'} style={{ width: 12, height: 12 }}></i>
          {teiaExpandida ? 'Recolher' : 'Expandir'}
        </button>
        <div ref={containerRef}
             style={{ width: '100%', height: teiaExpandida ? '100%' : 400,
                      background: 'var(--surface-2)',
                      border: '1px solid var(--border)', borderRadius: 'var(--r-md)' }} />
        {socio && (
          <div style={{
            position: 'absolute', top: 0, right: 0, width: 340, height: '100%',
            background: 'var(--surface)', borderLeft: '1px solid var(--border)',
            borderRadius: '0 var(--r-md) var(--r-md) 0',
            boxShadow: '-4px 0 12px rgba(0,0,0,0.06)',
            display: 'flex', flexDirection: 'column', overflow: 'hidden'
          }}>
            {/* Header do painel */}
            <div style={{ padding: '12px 14px', borderBottom: '1px solid var(--border)',
                          display: 'flex', alignItems: 'flex-start', gap: 10 }}>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ font: '600 13px/1.3 var(--ff-body)', color: 'var(--tx)',
                              wordBreak: 'break-word' }}>
                  {socio.nome}
                </div>
                {socio.qualificacao && (
                  <div style={{ font: '400 11px/1.3 var(--ff-body)', color: 'var(--tx-3)', marginTop: 2 }}>
                    {socio.qualificacao}
                    {socio.data_entrada && (
                      <span> · entrou em {fmtDataReceitaPub(socio.data_entrada)}</span>
                    )}
                  </div>
                )}
              </div>
              <button onClick={fecharPainelSocio} title="Fechar"
                      style={{ all: 'unset', cursor: 'pointer', padding: 4, color: 'var(--tx-3)' }}>
                <i data-lucide="x" style={{ width: 16, height: 16 }}></i>
              </button>
            </div>

            {/* Body do painel — lista de empresas onde aparece como sócio */}
            <div style={{ flex: 1, overflowY: 'auto', padding: '10px 14px' }}>
              {socioLoading && (
                <div style={{ padding: '24px 0', textAlign: 'center', color: 'var(--tx-3)',
                              font: '400 12px/1.4 var(--ff-body)' }}>
                  <i data-lucide="loader" style={{ width: 16, height: 16, display: 'block', margin: '0 auto 8px' }}></i>
                  Buscando empresas...
                </div>
              )}
              {socioErro && !socioLoading && (
                <div style={{ padding: '12px', background: '#fee2e2', color: '#b91c1c',
                              border: '1px solid #fecaca', borderRadius: 'var(--r-md)',
                              font: '400 12px/1.4 var(--ff-body)' }}>
                  Falha ao buscar empresas: {socioErro}
                </div>
              )}
              {!socioLoading && !socioErro && socioEmpresas && (
                <React.Fragment>
                  {socioEmpresas._mock && (
                    <div style={{ padding: '6px 10px', background: '#fef9c3', color: '#713f12',
                                  border: '1px solid #fde68a', borderRadius: 6,
                                  font: '400 11px/1.4 var(--ff-body)', marginBottom: 10 }}>
                      Dados simulados — substituir por API real (ver TODO em fetchSociosDoCPFOuCNPJ)
                    </div>
                  )}
                  <div style={{ padding: '8px 10px', background: 'var(--grn-050)',
                                border: '1px solid var(--grn-100)', borderRadius: 'var(--r-md)',
                                marginBottom: 10 }}>
                    <div style={{ font: '600 10px/1 var(--ff-body)', textTransform: 'uppercase',
                                  letterSpacing: '.06em', color: 'var(--tx-3)' }}>
                      {socioEmpresas._proprio ? 'Capital social' : 'Capital social somado'}
                    </div>
                    <div style={{ font: '600 16px/1.2 var(--ff-display)', color: 'var(--grn)', marginTop: 4 }}>
                      {fmtMoedaBRPub(socioEmpresas.capital_total)}
                    </div>
                    {!socioEmpresas._proprio && (
                      <div style={{ font: '400 11px/1.3 var(--ff-body)', color: 'var(--tx-3)', marginTop: 2 }}>
                        em {socioEmpresas.empresas.length} {socioEmpresas.empresas.length === 1 ? 'empresa' : 'empresas'}
                      </div>
                    )}
                  </div>

                  {socioEmpresas.empresas.length === 0 ? (
                    <div style={{ padding: '24px 0', textAlign: 'center', color: 'var(--tx-3)',
                                  font: '400 12px/1.4 var(--ff-body)' }}>
                      Nenhuma empresa encontrada.
                    </div>
                  ) : (
                    <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                      {socioEmpresas.empresas.map(function(e, i) {
                        var qual = QUALIFICACAO_SOCIO_PUB[e.qualificacao] || ('Cód. ' + e.qualificacao);
                        var cnpjDig = (e.cnpj_raw || '').replace(/\D/g, '');
                        var clienteCRM = (CRM_DATA.clientes || []).find(function(c) {
                          return (c.cnpj || '').replace(/\D/g, '') === cnpjDig;
                        });
                        var isCadastrado = !!clienteCRM;
                        var rfKey = e.cnpj_raw || e.cnpj_formatado;
                        var rfVal = rfExpandido[rfKey];
                        var rfLoading = rfVal === 'loading';
                        var rfErro = rfVal && typeof rfVal === 'string' && rfVal.startsWith('erro:');
                        var rfOk = rfVal && typeof rfVal === 'object';
                        function carregarRF() {
                          setRfExpandido(function(prev) { return Object.assign({}, prev, {[rfKey]: 'loading'}); });
                          _teiaFetchEmpresa(cnpjDig)
                            .then(function(d) { setRfExpandido(function(prev) { return Object.assign({}, prev, {[rfKey]: d}); }); })
                            .catch(function(err) { setRfExpandido(function(prev) { return Object.assign({}, prev, {[rfKey]: 'erro:' + err.message}); }); });
                        }
                        function ocultarRF() {
                          setRfExpandido(function(prev) { var n = Object.assign({}, prev); delete n[rfKey]; return n; });
                        }
                        function cadastrarCliente() {
                          var prefill = rfOk ? rfVal : { cnpj_raw: cnpjDig, cnpj_formatado: e.cnpj_formatado, razao_social: e.razao_social };
                          try { window.dispatchEvent(new CustomEvent('crm_new_cliente_prefill', { detail: prefill })); } catch(ex){}
                          if (typeof onNavRef.current === 'function') onNavRef.current('clientes');
                        }
                        return (
                          <div key={i} style={{ padding: '10px 12px', background: 'var(--surface-2)',
                                                border: '1px solid var(--border)',
                                                borderRadius: 'var(--r-md)' }}>
                            {/* Razão social + badge */}
                            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 6 }}>
                              <div style={{ font: '500 12px/1.3 var(--ff-body)', color: 'var(--tx)',
                                            wordBreak: 'break-word', flex: 1 }}>
                                {e.razao_social}
                              </div>
                              <span style={{ flexShrink: 0, fontSize: 10, fontWeight: 600,
                                             fontFamily: 'var(--ff-body)',
                                             padding: '2px 6px', borderRadius: 4,
                                             background: isCadastrado ? 'var(--grn-050)' : 'var(--surface)',
                                             color: isCadastrado ? 'var(--grn)' : 'var(--tx-3)',
                                             border: isCadastrado ? '1px solid var(--grn-100)' : '1px solid var(--border)' }}>
                                {isCadastrado ? '✓ Cliente' : 'Não cadastrado'}
                              </span>
                            </div>
                            <div style={{ font: '400 11px/1 var(--ff-mono)', color: 'var(--tx-3)', marginTop: 3 }}>
                              {e.cnpj_formatado}
                            </div>
                            <div style={{ display: 'flex', justifyContent: 'space-between',
                                          marginTop: 6, gap: 6 }}>
                              <div style={{ font: '400 11px/1.3 var(--ff-body)', color: 'var(--tx-2)' }}>
                                {qual}
                                {e.data_entrada && (<span style={{ color: 'var(--tx-3)' }}> · {fmtDataReceitaPub(e.data_entrada)}</span>)}
                              </div>
                              <div style={{ font: '500 11px/1 var(--ff-mono)', color: 'var(--tx)',
                                            whiteSpace: 'nowrap' }}>
                                {fmtMoedaBRPub(e.capital_social)}
                              </div>
                            </div>
                            {/* Dados RF expandidos inline */}
                            {rfOk && (
                              <div style={{ marginTop: 8, padding: '8px 10px', background: 'var(--surface)',
                                            border: '1px solid var(--border)', borderRadius: 6,
                                            font: '400 11px/1.6 var(--ff-body)', color: 'var(--tx-2)' }}>
                                {rfVal.cnae_descricao && <div><b>CNAE:</b> {rfVal.cnae_descricao}</div>}
                                {(rfVal.municipio_nome || rfVal.uf) && <div><b>Cidade:</b> {[rfVal.municipio_nome, rfVal.uf].filter(Boolean).join(' – ')}</div>}
                                {rfVal.situacao_cadastral && <div><b>Situação:</b> {rfVal.situacao_cadastral === '02' ? '✓ Ativa' : rfVal.situacao_cadastral}</div>}
                                {rfVal.capital_social && <div><b>Capital:</b> {fmtMoedaBRPub(rfVal.capital_social)}</div>}
                              </div>
                            )}
                            {rfErro && (
                              <div style={{ marginTop: 6, font: '400 11px/1.4 var(--ff-body)', color: '#b91c1c' }}>
                                Erro ao carregar dados da RF
                              </div>
                            )}
                            {/* Botões de ação */}
                            <div style={{ marginTop: 8, display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                              {isCadastrado ? (
                                <button onClick={() => verEmpresa(e.cnpj_formatado)}
                                        style={{ padding: '4px 10px', fontSize: 11,
                                                 background: 'var(--surface)', border: '1px solid var(--border)',
                                                 borderRadius: 6, cursor: 'pointer', color: 'var(--tx-2)',
                                                 display: 'inline-flex', alignItems: 'center', gap: 4 }}>
                                  <i data-lucide="external-link" style={{ width: 11, height: 11 }}></i>
                                  Ver empresa
                                </button>
                              ) : (
                                <React.Fragment>
                                  <button onClick={rfLoading ? null : (rfOk ? ocultarRF : carregarRF)}
                                          disabled={rfLoading}
                                          style={{ padding: '4px 10px', fontSize: 11,
                                                   background: 'var(--surface)', border: '1px solid var(--border)',
                                                   borderRadius: 6, cursor: rfLoading ? 'wait' : 'pointer',
                                                   color: 'var(--tx-2)', opacity: rfLoading ? 0.6 : 1,
                                                   display: 'inline-flex', alignItems: 'center', gap: 4 }}>
                                    <i data-lucide={rfLoading ? 'loader' : 'file-search'} style={{ width: 11, height: 11 }}></i>
                                    {rfLoading ? 'Carregando…' : rfOk ? 'Ocultar RF' : 'Ver dados RF'}
                                  </button>
                                  <button onClick={cadastrarCliente}
                                          style={{ padding: '4px 10px', fontSize: 11,
                                                   background: 'var(--grn-050)', border: '1px solid var(--grn-100)',
                                                   borderRadius: 6, cursor: 'pointer', color: 'var(--grn)',
                                                   display: 'inline-flex', alignItems: 'center', gap: 4 }}>
                                    <i data-lucide="user-plus" style={{ width: 11, height: 11 }}></i>
                                    Cadastrar cliente
                                  </button>
                                </React.Fragment>
                              )}
                            </div>
                          </div>
                        );
                      })}
                    </div>
                  )}
                </React.Fragment>
              )}
            </div>
          </div>
        )}
      </div>
      <div style={{ marginTop: 10, font: '400 11px/1.4 var(--ff-body)', color: 'var(--tx-3)',
                    display: 'flex', gap: 18, flexWrap: 'wrap', alignItems: 'center' }}>
        <span><span style={{ display: 'inline-block', width: 10, height: 10, background: '#2563eb', borderRadius: '50%', marginRight: 6, verticalAlign: 'middle' }}></span>Empresa atual</span>
        <span><span style={{ display: 'inline-block', width: 10, height: 10, background: '#f97316', borderRadius: '50%', marginRight: 6, verticalAlign: 'middle' }}></span>Sócio PF</span>
        <span><span style={{ display: 'inline-block', width: 10, height: 10, background: '#737373', borderRadius: '50%', marginRight: 6, verticalAlign: 'middle' }}></span>Sócio PJ</span>
        <span><span style={{ display: 'inline-block', width: 10, height: 10, background: '#0891b2', borderRadius: '50%', marginRight: 6, verticalAlign: 'middle' }}></span>Empresa relacionada</span>
        <span style={{ color: 'var(--tx-3)' }}>· Clique em qualquer nó para expandir ramificações</span>
        <span><span style={{ display: 'inline-block', width: 10, height: 10, background: 'transparent', border: '2px solid #15803d', borderRadius: '50%', marginRight: 6, verticalAlign: 'middle' }}></span>Já expandido</span>
        <span style={{ color: 'var(--tx-3)', fontStyle: 'italic' }}>Clique em um nó para ver detalhes</span>
        {expandindo && <span style={{ color: 'var(--tx-2)' }}><i data-lucide="loader" style={{width:11,height:11,marginRight:4,verticalAlign:'middle'}}></i> Expandindo…</span>}
      </div>
    </div>
  );
}

// ── DadosPublicosTab — aba completa renderizada na tela de detalhe ──────────
// ── Popup compacto: empresas onde uma pessoa física participa ───────────────
// Aberto ao clicar num sócio PF na tabela QSA. Busca via _teiaFetchSocios
// (cache compartilhado). Clicar numa empresa da lista chama onDrillEmpresa
// para navegar dentro da própria aba Dados Públicos.
function SocioPFPopup({ socio, onClose, onDrillEmpresa }) {
  const [loading,   setLoading]   = useScreenState(true);
  const [erro,      setErro]      = useScreenState(null);
  const [resultado, setResultado] = useScreenState(null);  // { empresas, capital_total }

  React.useEffect(() => {
    if (!socio || !socio.doc_raw) {
      setLoading(false); setErro('CPF não disponível'); return;
    }
    let cancel = false;
    setLoading(true); setErro(null); setResultado(null);
    _teiaFetchSocios(socio.doc_raw, socio.nome || '')
      .then(r => { if (!cancel) setResultado(r); })
      .catch(e => { if (!cancel) setErro(e.message || 'ERRO'); })
      .finally(() => { if (!cancel) setLoading(false); });
    return () => { cancel = true; };
  }, [socio && socio.doc_raw]);

  // Re-aplica ícones lucide quando o popup abre/atualiza
  React.useEffect(() => {
    try { if (window.lucide) window.lucide.createIcons({ attrs: { 'stroke-width': 1.75 } }); }
    catch(e) {}
  });

  // ESC fecha popup
  React.useEffect(() => {
    function onEsc(e) { if (e.key === 'Escape') onClose && onClose(); }
    window.addEventListener('keydown', onEsc);
    return () => window.removeEventListener('keydown', onEsc);
  }, [onClose]);

  if (!socio) return null;

  const empresas = (resultado && resultado.empresas) || [];
  const capTotal = (resultado && resultado.capital_total) || 0;

  return (
    <div
      onClick={function(e){ if (e.target === e.currentTarget) onClose && onClose(); }}
      style={{
        position: 'fixed', inset: 0, zIndex: 9000,
        background: 'rgba(0,0,0,.35)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        padding: 20
      }}>
      <div style={{
        background: 'var(--surface)', borderRadius: 'var(--r-lg)',
        boxShadow: '0 12px 40px rgba(0,0,0,.25)',
        width: 'min(520px, 100%)', maxHeight: '85vh',
        display: 'flex', flexDirection: 'column', overflow: 'hidden'
      }}>
        {/* Header */}
        <div style={{ padding: '16px 20px', borderBottom: '1px solid var(--border)',
                      display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 12 }}>
          <div>
            <div style={{ font: '600 15px/1.2 var(--ff-body)', color: 'var(--tx)' }}>{socio.nome}</div>
            <div style={{ font: '400 11px/1.5 var(--ff-mono)', color: 'var(--tx-3)', marginTop: 4 }}>
              {socio.qualificacao}
              {socio.data_entrada ? ' · entrada ' + fmtDataReceitaPub(socio.data_entrada) : ''}
              {socio.faixa_etaria && socio.faixa_etaria !== '—' ? ' · ' + socio.faixa_etaria : ''}
            </div>
          </div>
          <button onClick={onClose} aria-label="Fechar"
            style={{ background: 'transparent', border: 'none', color: 'var(--tx-3)',
                     cursor: 'pointer', padding: 4, borderRadius: 'var(--r-xs)' }}>
            <i data-lucide="x" style={{ width: 18, height: 18 }}></i>
          </button>
        </div>

        {/* Conteúdo */}
        <div style={{ flex: 1, overflowY: 'auto', padding: '14px 20px' }}>
          <div style={{ font: '600 10px/1 var(--ff-body)', textTransform: 'uppercase',
                        letterSpacing: '.06em', color: 'var(--tx-3)', marginBottom: 10 }}>
            Empresas onde participa
          </div>

          {loading && (
            <div style={{ padding: '24px 0', textAlign: 'center', color: 'var(--tx-3)',
                          font: '400 12px/1.4 var(--ff-body)' }}>
              <i data-lucide="loader" style={{ width: 18, height: 18 }}></i>
              <div style={{ marginTop: 6 }}>Buscando participações…</div>
            </div>
          )}

          {!loading && erro && (
            <div style={{ padding: '20px 0', textAlign: 'center', color: 'var(--tx-3)',
                          font: '400 12px/1.4 var(--ff-body)' }}>
              <i data-lucide="cloud-off" style={{ width: 22, height: 22, display: 'block', margin: '0 auto 6px' }}></i>
              Não foi possível buscar participações.
            </div>
          )}

          {!loading && !erro && empresas.length === 0 && (
            <div style={{ padding: '20px 0', textAlign: 'center', color: 'var(--tx-3)',
                          font: '400 12px/1.4 var(--ff-body)' }}>
              Não há outras empresas vinculadas a esta pessoa na base RF.
            </div>
          )}

          {!loading && !erro && empresas.length > 0 && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              {empresas.map(function(emp, i) {
                var empCnpjRaw = (emp.cnpj_raw || '').replace(/\D/g, '');
                return (
                  <div key={i}
                    onClick={function(){ onDrillEmpresa && onDrillEmpresa(empCnpjRaw); }}
                    style={{ padding: '10px 12px', borderRadius: 'var(--r-sm)',
                             border: '1px solid var(--border)', background: 'var(--surface-2)',
                             cursor: 'pointer', transition: 'all 120ms',
                             display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10 }}
                    onMouseEnter={function(e){
                      e.currentTarget.style.background = 'var(--info-050, #e4eef8)';
                      e.currentTarget.style.borderColor = 'var(--info)';
                    }}
                    onMouseLeave={function(e){
                      e.currentTarget.style.background = 'var(--surface-2)';
                      e.currentTarget.style.borderColor = 'var(--border)';
                    }}>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ font: '500 13px/1.2 var(--ff-body)', color: 'var(--tx)',
                                    overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
                           title={emp.razao_social || ''}>
                        {emp.razao_social || empCnpjRaw}
                      </div>
                      <div style={{ font: '400 11px/1.4 var(--ff-mono)', color: 'var(--tx-3)', marginTop: 3 }}>
                        {emp.cnpj_formatado || empCnpjRaw}
                        {emp.municipio_nome ? ' · ' + emp.municipio_nome + (emp.uf ? '/' + emp.uf : '') : ''}
                      </div>
                    </div>
                    <i data-lucide="arrow-right" style={{ width: 14, height: 14, color: 'var(--tx-3)', flexShrink: 0 }}></i>
                  </div>
                );
              })}
            </div>
          )}
        </div>

        {/* Rodapé — capital total */}
        {!loading && empresas.length > 0 && (
          <div style={{ padding: '12px 20px', borderTop: '1px solid var(--border)',
                        background: 'var(--surface-2)', display: 'flex',
                        alignItems: 'center', justifyContent: 'space-between' }}>
            <span style={{ font: '600 10px/1 var(--ff-body)', textTransform: 'uppercase',
                           letterSpacing: '.06em', color: 'var(--tx-3)' }}>
              Capital total acumulado
            </span>
            <span style={{ font: '500 14px/1 var(--ff-mono)', color: 'var(--tx)' }}>
              {capTotal.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' })}
            </span>
          </div>
        )}
      </div>
    </div>
  );
}

function DadosPublicosTab({ cnpj, onNav }) {
  const [loading,     setLoading]     = useScreenState(true);
  const [erro,        setErro]        = useScreenState(null);
  const [dados,       setDados]       = useScreenState(null);
  const [verCnaes,    setVerCnaes]    = useScreenState(false);
  // ── Drill-in: clica em filial ou sócio PJ navega para os dados RF dessa entidade
  const [cnpjDrill,   setCnpjDrill]   = useScreenState(null);   // CNPJ navegado (null = matriz original)
  const [dadosMatriz, setDadosMatriz] = useScreenState(null);   // preservado para breadcrumb
  // ── Popup PF: clica num sócio PF abre painel compacto com empresas que participa
  const [pfPopup,     setPfPopup]     = useScreenState(null);   // { nome, qualificacao, faixa_etaria, data_entrada, doc_raw }

  // Normaliza cnpj prop (pode vir formatado "62.092.754/0005-08") para 14 dígitos
  const _cnpjRaw14 = (function() {
    var d = (cnpj || '').replace(/\D/g, '');
    if (!d) return '';
    return d.length < 14 ? d.padStart(14, '0') : d;
  })();
  const cnpjEfetivo = cnpjDrill || _cnpjRaw14;
  const drillAtivo  = !!cnpjDrill && cnpjDrill !== _cnpjRaw14;

  // Reset drill ao trocar de cliente (cnpj prop muda)
  React.useEffect(() => {
    setCnpjDrill(null);
    setPfPopup(null);
    setDadosMatriz(null);
    let cancel = false;
    _teiaFetchEmpresa(cnpj)
      .then(d => { if (!cancel) setDadosMatriz(d); })
      .catch(function() {});  // matriz não-carregada não bloqueia drill
    return () => { cancel = true; };
  }, [cnpj]);

  // Fetch do CNPJ efetivo (matriz ou drill). Usa _teiaFetchEmpresa (cache compartilhado com Teia).
  React.useEffect(() => {
    let cancel = false;
    setLoading(true); setErro(null); setDados(null); setVerCnaes(false);
    _teiaFetchEmpresa(cnpjEfetivo)
      .then(d => { if (!cancel) setDados(d); })
      .catch(e => { if (!cancel) setErro(e.code === 'CGC_SKIP' ? 'NOT_FOUND' : (e.message || 'ERRO')); })
      .finally(() => { if (!cancel) setLoading(false); });
    return () => { cancel = true; };
  }, [cnpjEfetivo]);

  // Handler: drill em CNPJ (filial ou sócio PJ)
  function drillEm(cnpjAlvo) {
    var d = String(cnpjAlvo || '').replace(/\D/g, '');
    if (!d || d.length < 8) return;
    var d14 = d.length < 14 ? d.padStart(14, '0') : d;
    setCnpjDrill(d14);
    setPfPopup(null);
    // Scroll suave para o topo do modal (UX)
    setTimeout(function() {
      try { var sc = document.querySelector('.cli-modal-scroll, .modal__body'); if (sc) sc.scrollTop = 0; } catch(e) {}
    }, 50);
  }
  function voltarMatriz() { setCnpjDrill(null); }

  // Reaplica ícones lucide a cada render (mesmo padrão da tela Clientes)
  React.useEffect(() => {
    try { if (window.lucide) window.lucide.createIcons({ attrs: { 'stroke-width': 1.75 } }); }
    catch(e) {}
  });

  if (loading) {
    return (
      <Panel title="Dados Públicos" meta="Receita Federal">
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          {[1,2,3,4].map(i => (
            <div key={i} style={{ height: 56, background: 'var(--surface-2)',
                                  border: '1px solid var(--border)', borderRadius: 'var(--r-md)',
                                  position: 'relative', overflow: 'hidden' }}>
              <div style={{ position: 'absolute', inset: 0,
                            background: 'linear-gradient(90deg, transparent, rgba(0,0,0,0.04), transparent)',
                            animation: 'sklPub 1.4s linear infinite' }}/>
            </div>
          ))}
          <style>{`@keyframes sklPub { 0%{transform:translateX(-100%)} 100%{transform:translateX(100%)} }`}</style>
        </div>
      </Panel>
    );
  }

  if (erro === 'NOT_FOUND') {
    return (
      <Panel title="Dados Públicos">
        <div style={{ padding: '32px 0', textAlign: 'center', color: 'var(--tx-3)' }}>
          <i data-lucide="search-x" style={{ width: 26, height: 26, display: 'block', margin: '0 auto 10px' }}></i>
          <div style={{ font: '500 13px/1.4 var(--ff-body)', color: 'var(--tx-2)' }}>
            CNPJ não encontrado na base da Receita Federal.
          </div>
        </div>
      </Panel>
    );
  }

  if (erro) {
    return (
      <Panel title="Dados Públicos">
        <div style={{ padding: '32px 0', textAlign: 'center', color: 'var(--tx-3)' }}>
          <i data-lucide="cloud-off" style={{ width: 26, height: 26, display: 'block', margin: '0 auto 10px' }}></i>
          <div style={{ font: '500 13px/1.4 var(--ff-body)', color: 'var(--tx-2)' }}>
            Não foi possível carregar dados da Receita Federal.
          </div>
          <div style={{ font: '400 12px/1.5 var(--ff-body)', color: 'var(--tx-3)', marginTop: 6 }}>
            Verifique se a API local está rodando.
          </div>
        </div>
      </Panel>
    );
  }
  if (!dados) return null;

  // ── Cruzamento grupo empresarial ─────────────────────────────────────────
  // Compara cada sócio desta empresa (nome normalizado) com `razao` e `contato`
  // dos clientes em CRM_DATA.clientes. Sócio PJ (com CNPJ) também é cruzado
  // contra o `cnpj` dos clientes.
  const cnpjAtual    = (dados.cnpj_raw || '').replace(/\D/g, '');
  const sociosNorm   = (dados.socios || []).map(s => ({
    raw:  s,
    nome: normalizaNomePub(s.nome_socio),
    cnpj: s.identificador_socio === '1' ? (s.cpf_cnpj_socio || '').replace(/\D/g, '') : ''  // '1'=PJ
  })).filter(s => s.nome);
  const matches = [];
  (CRM_DATA.clientes || []).forEach(c => {
    const cnpjC = (c.cnpj || '').replace(/\D/g, '');
    if (cnpjC && cnpjC === cnpjAtual) return; // mesma empresa, ignora
    const nomeRazao   = normalizaNomePub(c.razao);
    const nomeContato = normalizaNomePub(c.contato);
    sociosNorm.forEach(s => {
      const bateNome = s.nome && (s.nome === nomeRazao || s.nome === nomeContato);
      const bateCNPJ = s.cnpj && cnpjC && s.cnpj === cnpjC;
      if (bateNome || bateCNPJ) {
        matches.push({ cliente: c, nomeSocio: s.raw.nome_socio });
      }
    });
  });

  // ── Dados derivados para renderização ────────────────────────────────────
  const sit          = SITUACAO_CADASTRAL_PUB[dados.situacao_cadastral] || { label: 'Desconhecida', cor: '#737373', bg: '#f5f5f5' };
  const cnaesSec     = (dados.cnae_secundarios || '').split(';').map(s => s.trim()).filter(Boolean);
  const anosMercado  = anosDesdePub(dados.data_inicio_atividade);
  const enderecoLin1 = [dados.logradouro, dados.numero].filter(Boolean).join(', ');
  const enderecoLin2 = [dados.complemento, dados.bairro].filter(Boolean).join(' · ');
  const enderecoLin3 = [fmtCEPRawPub(dados.cep), [dados.municipio_nome, dados.uf].filter(Boolean).join(' - ')]
                        .filter(Boolean).join(' · ');

  const cardStyle    = { background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', padding: '10px 12px' };
  const lblStyle     = { font: '600 10px/1 var(--ff-body)', textTransform: 'uppercase', letterSpacing: '.06em', color: 'var(--tx-3)', marginBottom: 6 };
  const valStyle     = { font: '400 12px/1.3 var(--ff-mono)', color: 'var(--tx)' };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
      {/* Breadcrumb — quando navegou para filial/sócio PJ via drill */}
      {drillAtivo && (function() {
        // Mesmo cnpj_basico = outra unidade do mesmo grupo; senão = empresa relacionada
        var ehMesmaEmpresa = cnpjDrill && _cnpjRaw14 && cnpjDrill.substring(0,8) === _cnpjRaw14.substring(0,8);
        var labelOrigem = ehMesmaEmpresa
          ? ((dados && dados.matriz_filial === '1') ? 'Matriz de' : 'Outra unidade de')
          : 'Empresa relacionada a';
        var nomeOrigem  = (dadosMatriz && dadosMatriz.razao_social) ||
                          (_cnpjRaw14 ? _cnpjRaw14.replace(/^(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})$/, '$1.$2.$3/$4-$5') : '');
        return (
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 12px',
                        background: 'var(--info-050, #e4eef8)', border: '1px solid #c1d6ec',
                        borderRadius: 'var(--r-md)', font: '400 12px/1.4 var(--ff-body)', flexWrap: 'wrap' }}>
            <button onClick={voltarMatriz}
              style={{ background: 'transparent', border: 'none', color: 'var(--info)',
                       cursor: 'pointer', font: '600 12px/1 var(--ff-body)', padding: 0,
                       display: 'inline-flex', alignItems: 'center', gap: 4 }}>
              <i data-lucide="arrow-left" style={{ width: 13, height: 13 }}></i>
              Voltar
            </button>
            <span style={{ color: 'var(--tx-3)' }}>·</span>
            <span style={{ color: 'var(--tx-3)' }}>{labelOrigem}</span>
            <strong style={{ color: 'var(--tx)' }}>{nomeOrigem}</strong>
          </div>
        );
      })()}

      {/* Banner grupo empresarial */}
      {matches.length > 0 && (
        <div style={{ padding: '12px 14px', background: '#fef9c3', border: '1px solid #fde68a',
                      borderRadius: 'var(--r-md)', display: 'flex', gap: 10, alignItems: 'flex-start' }}>
          <i data-lucide="alert-triangle" style={{ width: 18, height: 18, color: '#a16207', flexShrink: 0, marginTop: 1 }}></i>
          <div style={{ font: '400 12px/1.5 var(--ff-body)', color: '#713f12' }}>
            <strong>Sócio desta empresa também aparece em cliente Bauko:</strong>
            <ul style={{ margin: '6px 0 0', paddingLeft: 18 }}>
              {matches.map((m, i) => (
                <li key={i} style={{ marginBottom: 2 }}>
                  <strong>{m.nomeSocio}</strong> — já cliente Bauko como <strong>{m.cliente.razao}</strong>
                </li>
              ))}
            </ul>
          </div>
        </div>
      )}

      {/* 1. Situação Cadastral */}
      <Panel title="Situação Cadastral">
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(170px, 1fr))', gap: 10 }}>
          <div style={cardStyle}>
            <div style={lblStyle}>Situação</div>
            <span style={{ display: 'inline-block', padding: '4px 10px', borderRadius: 999,
                           background: sit.bg, color: sit.cor, font: '600 11px/1 var(--ff-body)' }}>
              {sit.label}
            </span>
          </div>
          <div style={cardStyle}>
            <div style={lblStyle}>Abertura</div>
            <div style={valStyle}>{fmtDataReceitaPub(dados.data_inicio_atividade)}</div>
            {anosMercado != null && (
              <div style={{ font: '400 11px/1 var(--ff-body)', color: 'var(--tx-3)', marginTop: 4 }}>
                {anosMercado} {anosMercado === 1 ? 'ano' : 'anos'} de mercado
              </div>
            )}
          </div>
          <div style={cardStyle}>
            <div style={lblStyle}>Porte</div>
            <div style={valStyle}>{PORTE_EMPRESA_PUB[dados.porte] || ('Cód. ' + dados.porte)}</div>
          </div>
          <div style={cardStyle}>
            <div style={lblStyle}>Capital social</div>
            <div style={valStyle}>{fmtMoedaBRPub(dados.capital_social)}</div>
          </div>
        </div>
        <div style={{ marginTop: 10, display: 'flex', gap: 6, flexWrap: 'wrap' }}>
          {dados.opcao_simples === 'S' && (
            <span style={{ padding: '3px 9px', background: '#dcfce7', color: '#15803d', font: '600 11px/1 var(--ff-body)', borderRadius: 999 }}>Simples Nacional</span>
          )}
          {dados.opcao_mei === 'S' && (
            <span style={{ padding: '3px 9px', background: '#dbeafe', color: '#1d4ed8', font: '600 11px/1 var(--ff-body)', borderRadius: 999 }}>MEI</span>
          )}
          {dados.matriz_filial === '1' && (
            <span style={{ padding: '3px 9px', background: '#f5f5f5', color: '#525252', font: '600 11px/1 var(--ff-body)', borderRadius: 999 }}>Matriz</span>
          )}
          {dados.matriz_filial === '2' && (
            <span style={{ padding: '3px 9px', background: '#f5f5f5', color: '#525252', font: '600 11px/1 var(--ff-body)', borderRadius: 999 }}>Filial</span>
          )}
        </div>
      </Panel>

      {/* 2. Atividade */}
      <Panel title="Atividade">
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          <div style={cardStyle}>
            <div style={lblStyle}>CNAE principal</div>
            <div style={{ font: '400 13px/1.4 var(--ff-body)', color: 'var(--tx)' }}>
              <code style={{ background: 'var(--bg, #f5f5f5)', padding: '1px 6px', borderRadius: 4, fontFamily: 'var(--ff-mono)', fontSize: 11, marginRight: 8 }}>{dados.cnae_principal}</code>
              {dados.cnae_descricao}
            </div>
          </div>
          {cnaesSec.length > 0 && (
            <div style={cardStyle}>
              <button onClick={() => setVerCnaes(!verCnaes)}
                      style={{ all: 'unset', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 4,
                               font: '600 10px/1 var(--ff-body)', textTransform: 'uppercase', letterSpacing: '.06em', color: 'var(--tx-2)' }}>
                <i data-lucide={verCnaes ? 'chevron-down' : 'chevron-right'} style={{ width: 12, height: 12 }}></i>
                CNAEs secundários ({cnaesSec.length}) — {verCnaes ? 'Ver menos' : 'Ver mais'}
              </button>
              {verCnaes && (
                <div style={{ marginTop: 8, display: 'flex', flexDirection: 'column', gap: 4 }}>
                  {cnaesSec.map(c => (
                    <code key={c} style={{ font: '400 11px/1.4 var(--ff-mono)', color: 'var(--tx-2)' }}>{c}</code>
                  ))}
                </div>
              )}
            </div>
          )}
          <div style={cardStyle}>
            <div style={lblStyle}>Natureza jurídica</div>
            <div style={{ font: '400 13px/1.4 var(--ff-body)', color: 'var(--tx)' }}>
              <code style={{ background: 'var(--bg, #f5f5f5)', padding: '1px 6px', borderRadius: 4, fontFamily: 'var(--ff-mono)', fontSize: 11, marginRight: 8 }}>{dados.natureza_juridica}</code>
              {dados.natureza_descricao}
            </div>
          </div>
        </div>
      </Panel>

      {/* 3. Endereço (Receita) */}
      <Panel title="Endereço (Receita)">
        <div style={{ font: '400 13px/1.5 var(--ff-body)', color: 'var(--tx)' }}>
          {enderecoLin1 && <div>{enderecoLin1}</div>}
          {enderecoLin2 && <div style={{ color: 'var(--tx-2)' }}>{enderecoLin2}</div>}
          {enderecoLin3 && <div style={{ color: 'var(--tx-2)' }}>{enderecoLin3}</div>}
        </div>
        <div style={{ marginTop: 10, font: '400 11px/1.4 var(--ff-body)', color: 'var(--tx-3)', fontStyle: 'italic' }}>
          Endereço do cadastro fiscal — pode diferir do endereço operacional.
        </div>
      </Panel>

      {/* 4. Filiais */}
      <Panel title="Filiais" meta={`${(dados.filiais || []).length}`}>
        {(dados.filiais || []).length === 0 ? (
          <div style={{ color: 'var(--tx-3)', font: '400 13px/1 var(--ff-body)' }}>Sem filiais cadastradas.</div>
        ) : (
          <div className="dt-wrap">
            <table className="dt">
              <thead><tr><th>CNPJ</th><th>Cidade · UF</th><th>Situação</th></tr></thead>
              <tbody>
                {dados.filiais.map((f, i) => {
                  const fs = SITUACAO_CADASTRAL_PUB[f.situacao_cadastral] || { label: 'Desconhecida', cor: '#737373', bg: '#f5f5f5' };
                  const fCnpj    = (f.cnpj_raw || f.cnpj_formatado || '').replace(/\D/g, '');
                  const fIsAtual = fCnpj && fCnpj === (dados.cnpj_raw || '').replace(/\D/g, '');
                  return (
                    <tr key={i}
                        onClick={fIsAtual ? undefined : function(){ drillEm(fCnpj); }}
                        style={{
                          cursor: fIsAtual ? 'default' : 'pointer',
                          background: fIsAtual ? 'var(--surface-2)' : 'transparent',
                          transition: 'background 120ms'
                        }}
                        onMouseEnter={function(e){ if (!fIsAtual) e.currentTarget.style.background = 'var(--info-050, #e4eef8)'; }}
                        onMouseLeave={function(e){ if (!fIsAtual) e.currentTarget.style.background = 'transparent'; }}
                        title={fIsAtual ? 'Você está aqui' : 'Clique para ver os dados públicos desta unidade'}>
                      <td style={{ font: '400 12px/1 var(--ff-mono)' }}>
                        {f.cnpj_formatado}
                        {fIsAtual && <span style={{ marginLeft: 6, font: '600 9px/1 var(--ff-body)', color: 'var(--tx-3)', textTransform: 'uppercase', letterSpacing: '.04em' }}>· Você está aqui</span>}
                      </td>
                      <td>{[f.municipio_nome, f.uf].filter(Boolean).join(' · ')}</td>
                      <td>
                        <span style={{ display: 'inline-block', padding: '3px 9px', borderRadius: 999,
                                       background: fs.bg, color: fs.cor, font: '600 10px/1 var(--ff-body)' }}>
                          {fs.label}
                        </span>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        )}
      </Panel>

      {/* 5. Quadro Societário (QSA) */}
      <Panel title="Quadro Societário (QSA)" meta={`${(dados.socios || []).length} sócio(s)`}>
        {(dados.socios || []).length === 0 ? (
          <div style={{ color: 'var(--tx-3)', font: '400 13px/1 var(--ff-body)' }}>
            Quadro societário não disponível.
          </div>
        ) : (
          <div className="dt-wrap">
            <table className="dt">
              <thead><tr><th>Nome</th><th>Qualificação</th><th>Entrada</th><th>Faixa etária</th></tr></thead>
              <tbody>
                {dados.socios.map((s, i) => {
                  var ehPJ   = s.identificador_socio === '1';  // '1'=PJ '2'=PF
                  var sDoc   = (s.cpf_cnpj_socio || '').replace(/\D/g, '');
                  // PJ com 8+ dígitos → drill na empresa. PF → popup com participações.
                  var podeClicarPJ = ehPJ && sDoc.length >= 8;
                  var podeClicarPF = !ehPJ;
                  var clicavel = podeClicarPJ || podeClicarPF;
                  function onClickSocio() {
                    if (podeClicarPJ) {
                      drillEm(sDoc);
                    } else if (podeClicarPF) {
                      setPfPopup({
                        nome:         s.nome_socio,
                        qualificacao: QUALIFICACAO_SOCIO_PUB[s.qualificacao_socio || s.qualificacao] || ('Cód. ' + (s.qualificacao_socio || s.qualificacao || '?')),
                        faixa_etaria: FAIXA_ETARIA_PUB[s.faixa_etaria] || '—',
                        data_entrada: s.data_entrada_sociedade || s.data_entrada,
                        doc_raw:      s.cpf_cnpj_socio || '',
                      });
                    }
                  }
                  return (
                    <tr key={i}
                        onClick={clicavel ? onClickSocio : undefined}
                        style={{
                          cursor: clicavel ? 'pointer' : 'default',
                          transition: 'background 120ms'
                        }}
                        onMouseEnter={function(e){ if (clicavel) e.currentTarget.style.background = 'var(--info-050, #e4eef8)'; }}
                        onMouseLeave={function(e){ if (clicavel) e.currentTarget.style.background = 'transparent'; }}
                        title={
                          podeClicarPJ ? 'Clique para ver os dados públicos desta empresa sócia'
                          : podeClicarPF ? 'Clique para ver outras empresas em que ' + (s.nome_socio || 'esta pessoa') + ' participa'
                          : ''
                        }>
                      <td style={{ font: '500 13px/1.2 var(--ff-body)' }}>
                        {s.nome_socio}
                        {ehPJ && <span style={{ marginLeft: 6, font: '600 9px/1 var(--ff-body)', color: 'var(--info)', background: 'var(--info-050, #e4eef8)', padding: '2px 6px', borderRadius: 999, letterSpacing: '.04em' }}>PJ</span>}
                      </td>
                      <td className="tx2" style={{ fontSize: 12 }}>{QUALIFICACAO_SOCIO_PUB[s.qualificacao_socio || s.qualificacao] || ('Cód. ' + (s.qualificacao_socio || s.qualificacao || '?'))}</td>
                      <td style={{ font: '400 12px/1 var(--ff-mono)' }}>{fmtDataReceitaPub(s.data_entrada_sociedade || s.data_entrada)}</td>
                      <td className="tx2" style={{ fontSize: 12 }}>{FAIXA_ETARIA_PUB[s.faixa_etaria] || '—'}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        )}
      </Panel>

      {/* 6. Teia Societária */}
      <Panel title="Teia Societária">
        <TeiaSocietaria empresa={dados} onNav={onNav} />
      </Panel>

      {/* Popup PF — empresas onde a pessoa física participa */}
      {pfPopup && (
        <SocioPFPopup
          socio={pfPopup}
          onClose={function(){ setPfPopup(null); }}
          onDrillEmpresa={function(c){ setPfPopup(null); drillEm(c); }}
        />
      )}
    </div>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// FROTA DO CLIENTE — aba "Frota" da tela de detalhe (PF e PJ)
// ════════════════════════════════════════════════════════════════════════════
// Lista de máquinas cadastradas pelo cliente. CRUD persistido na lista SP
// "CRM Frota Cliente" via CRM_API. Fabricantes e modelos vêm da TB_Equivalencia
// do Excel "Vendas Perdidas" — mesma fonte de verdade do vendas_perdidas.html.

// Gera id local da máquina (formato MAQ-XXXXXX). Usa Math.random — colisão
// improvável (36^6 ~ 2 bi). Persistência confirma unicidade no SP via Title.
function gerarMaqId() {
  var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  var s = '';
  for (var i = 0; i < 6; i++) s += chars[Math.floor(Math.random() * chars.length)];
  return 'MAQ-' + s;
}

function maq_resumo(m) {
  if (!m) return '';
  return [m.fabricante, m.modelo].filter(Boolean).join(' · ');
}

function MaquinaFrotaModal({ open, onClose, onSave, clienteId, inicial }) {
  const editing = !!inicial;
  const [fabricante, setFabricante] = useScreenState("");
  const [modelo,     setModelo]     = useScreenState("");
  const [ano,        setAno]        = useScreenState("");
  const [horimetro,  setHorimetro]  = useScreenState("");
  const [serie,      setSerie]      = useScreenState("");
  const [tipo,       setTipo]       = useScreenState("representada");
  const [saving,     setSaving]     = useScreenState(false);
  const [erro,       setErro]       = useScreenState(null);
  // Busca Komtrax
  const [showKom,    setShowKom]    = useScreenState(false);
  const [komBusca,   setKomBusca]   = useScreenState("");
  const [komSel,     setKomSel]     = useScreenState(null);   // máquina KOM selecionada para vincular
  const [komRows,    setKomRows]    = useScreenState(null);   // null=não carregado, []=carregado
  const [komLoading, setKomLoading] = useScreenState(false);  // carregando CSV do SharePoint
  // Base de fabricantes/modelos vinda do SP (TB_Equivalencia)
  // Inicializa do cache em window para evitar "Carregando..." ao reabrir o modal
  const [modelosBase, setModelosBase] = useScreenState(window._crmModelosCache || null);

  React.useEffect(() => {
    if (!open) return;
    if (editing && inicial) {
      setFabricante(inicial.fabricante || "");
      setModelo(inicial.modelo || "");
      setAno(inicial.ano != null ? String(inicial.ano) : "");
      setHorimetro(inicial.horimetro != null ? String(inicial.horimetro) : "");
      setSerie(inicial.serie || "");
      setTipo(inicial.tipo || "representada");
    } else {
      setFabricante(""); setModelo(""); setAno(""); setHorimetro(""); setSerie(""); setTipo("representada");
    }
    setErro(null); setSaving(false);
    setShowKom(false); setKomBusca(""); setKomSel(null); setKomRows(null); setKomLoading(false);
  }, [open, inicial]);

  // Carrega MODELOS/MODEL_INFO do SP (com cache compartilhado em localStorage)
  React.useEffect(() => {
    if (!open) return;
    if (modelosBase) return; // já carregado
    if (!window.CRM_API || typeof window.CRM_API.loadModelosFromSP !== 'function') {
      // Fallback mínimo se CRM_API não tem o loader (ex: dev seed)
      setModelosBase({ MODELOS: {}, MODEL_INFO: {} });
      return;
    }
    let cancel = false;
    window.CRM_API.loadModelosFromSP()
      .then(r => {
        const result = r || { MODELOS: {}, MODEL_INFO: {} };
        window._crmModelosCache = result; // persiste para próximas aberturas do modal
        if (!cancel) setModelosBase(result);
      })
      .catch(() => { if (!cancel) setModelosBase({ MODELOS: {}, MODEL_INFO: {} }); });
    return () => { cancel = true; };
  }, [open]);

  function onFabricanteChange(v) {
    setFabricante(v);
    setModelo("");
  }

  React.useEffect(() => {
    try { if (window.lucide) window.lucide.createIcons({ attrs: { 'stroke-width': 1.75 } }); }
    catch(e) {}
  });

  if (!open) return null;

  const podeSalvar = !!(fabricante && modelo) && !saving;
  const fabricantes  = modelosBase ? Object.keys(modelosBase.MODELOS || {}).sort() : [];
  const modelosDoFab = (modelosBase && fabricante && modelosBase.MODELOS[fabricante]) || [];

  async function salvar() {
    if (!podeSalvar) return;
    setSaving(true); setErro(null);
    var anoNum = ano ? parseInt(ano, 10) : null;
    var horiNum = horimetro ? parseInt(String(horimetro).replace(/\D/g,''), 10) : null;
    var maq = editing ? Object.assign({}, inicial) : {
      id: gerarMaqId(),
      cliente_id: clienteId
    };
    maq.fabricante = fabricante;
    maq.modelo     = modelo;
    maq.ano        = (anoNum && !isNaN(anoNum)) ? anoNum : null;
    maq.horimetro  = (horiNum != null && !isNaN(horiNum)) ? horiNum : null;
    maq.serie      = serie.trim() || null;
    maq.tipo       = tipo || "representada";

    try {
      if (editing) {
        if (window.CRM_API && maq._spId) {
          await window.CRM_API.updateItem('FrotaCliente', maq._spId, {
            fabricante: maq.fabricante, modelo: maq.modelo,
            ano: maq.ano, horimetro: maq.horimetro, serie: maq.serie, tipo: maq.tipo
          });
        }
        var idx = (CRM_DATA.frotaCliente || []).findIndex(m => m.id === maq.id);
        if (idx >= 0) CRM_DATA.frotaCliente[idx] = maq;
      } else if (komSel) {
        if (komSel._spId) {
          // Máquina já estava em frotaCliente como órfã → PATCH para vincular ao cliente
          maq._spId = komSel._spId;
          maq.id    = komSel.id;
          if (window.CRM_API) {
            await window.CRM_API.updateItem('FrotaCliente', maq._spId, {
              cliente_id: clienteId,
              fabricante: maq.fabricante, modelo: maq.modelo,
              ano: maq.ano, horimetro: maq.horimetro, serie: maq.serie, tipo: maq.tipo
            });
          }
          if (!CRM_DATA.frotaCliente) CRM_DATA.frotaCliente = [];
          var komIdx = CRM_DATA.frotaCliente.findIndex(function(m){ return m.id === maq.id; });
          if (komIdx >= 0) CRM_DATA.frotaCliente[komIdx] = Object.assign({}, CRM_DATA.frotaCliente[komIdx], maq);
          else CRM_DATA.frotaCliente.push(maq);
        } else {
          // Máquina veio direto do CSV (nunca importada) → cria entrada já vinculada ao cliente
          maq.id         = komSel.komId;
          maq.cliente_id = clienteId;
          if (window.CRM_API) {
            var csvCreated = await window.CRM_API.addItem('FrotaCliente', maq);
            if (csvCreated && csvCreated.id) maq._spId = String(csvCreated.id);
          }
          if (!CRM_DATA.frotaCliente) CRM_DATA.frotaCliente = [];
          CRM_DATA.frotaCliente.push(maq);
        }
      } else {
        if (window.CRM_API) {
          var created = await window.CRM_API.addItem('FrotaCliente', maq);
          if (created && created.id) maq._spId = String(created.id);
        }
        if (!CRM_DATA.frotaCliente) CRM_DATA.frotaCliente = [];
        CRM_DATA.frotaCliente.push(maq);
      }
      onSave && onSave(maq);
      onClose && onClose();
    } catch (e) {
      console.error('[FrotaCliente] salvar falhou:', e);
      setErro(e.message || 'Falha ao salvar');
    } finally {
      setSaving(false);
    }
  }

  return (
    <Modal open={open} onClose={onClose}
      title={editing ? "Editar máquina" : "Adicionar máquina"}
      subtitle={editing ? maq_resumo(inicial) : "Cadastro da frota do cliente"}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
        <div className="f">
          <label>Fabricante <span style={{ color: '#dc2626' }}>*</span></label>
          {!modelosBase ? (
            <div style={{ padding: '8px 10px', font: '400 12px/1.4 var(--ff-body)', color: 'var(--tx-3)' }}>
              Carregando lista de fabricantes...
            </div>
          ) : (
            <select value={fabricante} onChange={e => onFabricanteChange(e.target.value)}>
              <option value="">— Selecione —</option>
              {fabricantes.map(f => <option key={f} value={f}>{f}</option>)}
            </select>
          )}
        </div>
        <div className="f">
          <label>Modelo <span style={{ color: '#dc2626' }}>*</span></label>
          <select value={modelo} onChange={e => setModelo(e.target.value)}
                  disabled={!fabricante || !modelosBase}>
            <option value="">
              {!fabricante ? "— Selecione o fabricante primeiro —" :
                (modelosDoFab.length === 0 ? "— Nenhum modelo cadastrado para este fabricante —" : "— Selecione —")}
            </option>
            {modelosDoFab.map(m => <option key={m} value={m}>{m}</option>)}
          </select>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          <div className="f">
            <label>Ano <span style={{ font: '400 11px/1 var(--ff-body)', color: 'var(--tx-3)' }}>· opcional</span></label>
            <input type="number" min="1950" max="2099" placeholder="Ex: 2018"
                   value={ano} onChange={e => setAno(e.target.value)} />
          </div>
          <div className="f">
            <label>Horímetro (h) <span style={{ font: '400 11px/1 var(--ff-body)', color: 'var(--tx-3)' }}>· opcional</span></label>
            <input type="number" min="0" placeholder="Ex: 5400"
                   value={horimetro} onChange={e => setHorimetro(e.target.value)} />
          </div>
        </div>
        <div className="f">
          <label>Número de série <span style={{ font: '400 11px/1 var(--ff-body)', color: 'var(--tx-3)' }}>· opcional</span></label>
          <input type="text" placeholder="Ex: PC200-8-K45321"
                 value={serie} onChange={e => setSerie(e.target.value)}
                 style={{ fontFamily: 'var(--ff-mono)', textTransform: 'uppercase' }}
                 onBlur={e => setSerie(e.target.value.trim().toUpperCase())} />
        </div>
        <div className="f">
          <label>Tipo de frota</label>
          <select value={tipo} onChange={e => setTipo(e.target.value)}>
            <option value="representada">Representada (marcas Bauko)</option>
            <option value="concorrente">Concorrente</option>
          </select>
        </div>

        {/* Painel "Do Komtrax" — sempre visível em modo criação */}
        {!editing && (function() {
          // Lista mesclada: órfãs já em frotaCliente + máquinas do CSV ainda não importadas
          var frotaIdx = {};
          (CRM_DATA.frotaCliente || []).forEach(function(m) {
            if (m.id) frotaIdx[m.id.toLowerCase()] = m;
          });
          var orphans = (CRM_DATA.frotaCliente || []).filter(function(m) {
            return m.id && m.id.toLowerCase().startsWith('kom-') && !m.cliente_id;
          });
          // CSV: só as que ainda não estão em frotaCliente (evita duplicata com a lista de órfãs)
          var csvOnly = (komRows || []).filter(function(r) {
            return !frotaIdx[r.komId.toLowerCase()];
          });
          var lista = orphans.concat(csvOnly);

          var bN = komBusca.trim().toLowerCase();
          var filtrados = bN
            ? lista.filter(function(m) {
                var key = [m.fabricante, m.modelo, m.serie || m.komId].filter(Boolean).join(' ');
                return key.toLowerCase().includes(bN);
              })
            : lista;

          async function carregarKomtrax() {
            setKomLoading(true); setKomSel(null); setKomBusca('');
            try {
              var rows = await window.CRM_API.loadKomtraxRows();
              setKomRows(rows);
            } catch(e) {
              setErro('Não foi possível carregar o Komtrax.csv: ' + e.message);
              setKomRows([]);
            } finally { setKomLoading(false); }
          }

          return (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              {/* Botão toggle principal */}
              <button type="button"
                onClick={function(){ setShowKom(function(v){ return !v; }); if(showKom){ setKomSel(null); setKomBusca(''); } }}
                style={{
                  display: 'inline-flex', alignItems: 'center', gap: 7,
                  padding: '7px 14px', borderRadius: 'var(--r-md)', cursor: 'pointer',
                  font: '500 12px/1 var(--ff-body)', transition: 'background 120ms',
                  background: showKom ? '#1a4a8a' : 'var(--info, #3370b7)',
                  color: '#fff', border: 'none', alignSelf: 'flex-start'
                }}>
                {/* SVG inline (cpu): Lucide não toca → evita conflito de DOM ao remontar */}
                <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
                  <rect x="4" y="4" width="16" height="16" rx="2"/>
                  <rect x="9" y="9" width="6" height="6"/>
                  <path d="M15 2v2M15 20v2M2 15h2M2 9h2M20 15h2M20 9h2M9 2v2M9 20v2"/>
                </svg>
                Do Komtrax
                {lista.length > 0 && (
                  <span style={{ background: 'rgba(255,255,255,.25)', borderRadius: 'var(--r-pill)',
                                 padding: '1px 7px', font: '600 10px/1.4 var(--ff-mono)' }}>
                    {lista.length}
                  </span>
                )}
              </button>

              {/* Painel expandido */}
              {showKom && (
                <div style={{ border: '1px solid var(--border)', borderRadius: 'var(--r-md)',
                              overflow: 'hidden', background: 'var(--surface-2)' }}>
                  <div style={{ padding: '10px 12px', display: 'flex', flexDirection: 'column', gap: 8 }}>

                    {/* Botão carregar CSV + spinner */}
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                      <button type="button" onClick={carregarKomtrax} disabled={komLoading}
                        className="btn btn-secondary"
                        style={{ fontSize: 11, padding: '5px 10px', display: 'flex', alignItems: 'center', gap: 6 }}>
                        {/* SVG inline: ícone muda condicionalmente → uso SVG p/ evitar conflito Lucide */}
                        {komLoading ? (
                          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ animation: 'bdrSpin 1s linear infinite', flexShrink: 0 }}>
                            <line x1="12" y1="2" x2="12" y2="6"/><line x1="12" y1="18" x2="12" y2="22"/>
                            <line x1="4.93" y1="4.93" x2="7.76" y2="7.76"/><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"/>
                            <line x1="2" y1="12" x2="6" y2="12"/><line x1="18" y1="12" x2="22" y2="12"/>
                            <line x1="4.93" y1="19.07" x2="7.76" y2="16.24"/><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"/>
                          </svg>
                        ) : (
                          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
                            <path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/>
                          </svg>
                        )}
                        {komLoading ? 'Carregando...' : 'Carregar lista do Komtrax'}
                      </button>
                      {komRows !== null && !komLoading && (
                        <span style={{ font: '400 11px/1 var(--ff-mono)', color: 'var(--tx-3)' }}>
                          {lista.length} {lista.length === 1 ? 'máquina' : 'máquinas'} disponíveis
                        </span>
                      )}
                    </div>

                    {/* Campo de busca — só aparece quando há dados */}
                    {lista.length > 0 && (
                      <input className="inp" value={komBusca}
                        onChange={function(e){ setKomBusca(e.target.value); }}
                        placeholder="Buscar por fabricante, modelo ou série…"
                        style={{ fontSize: 12 }} />
                    )}

                    {/* Lista de máquinas */}
                    {lista.length === 0 && komRows !== null && !komLoading && (
                      <div style={{ font: '400 12px/1.5 var(--ff-body)', color: 'var(--tx-3)',
                                    textAlign: 'center', padding: '12px 0' }}>
                        Nenhuma máquina disponível para vincular.
                      </div>
                    )}

                    {filtrados.length > 0 && (
                      <div style={{ maxHeight: 220, overflowY: 'auto', display: 'flex', flexDirection: 'column', gap: 4 }}>
                        {filtrados.length < lista.length && bN && (
                          <div style={{ font: '400 10px/1 var(--ff-mono)', color: 'var(--tx-3)', paddingBottom: 4 }}>
                            {filtrados.length} de {lista.length}
                          </div>
                        )}
                        {filtrados.map(function(m) {
                          var mId   = m.id || m.komId;
                          var isSel = komSel && (komSel.id === mId || komSel.komId === mId);
                          var isOrphan = !!m._spId; // já estava em frotaCliente
                          return (
                            <div key={mId}
                              onClick={function(){
                                setKomSel(m);
                                setFabricante(m.fabricante || "");
                                setModelo(m.modelo || "");
                                setAno(m.ano ? String(m.ano) : "");
                                setHorimetro(m.smr != null ? String(Math.round(m.smr)) : (m.horimetro ? String(m.horimetro) : ""));
                                setSerie(m.serie || "");
                              }}
                              style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 10px',
                                       borderRadius: 'var(--r-sm)', cursor: 'pointer',
                                       background: isSel ? 'var(--grn-050)' : 'var(--surface)',
                                       border: '1px solid ' + (isSel ? 'var(--grn)' : 'var(--border)'),
                                       transition: 'all 120ms' }}>
                              <div style={{ flex: 1, minWidth: 0 }}>
                                <div style={{ font: '500 12px/1.2 var(--ff-body)', color: 'var(--tx)' }}>
                                  {[m.fabricante, m.modelo].filter(Boolean).join(' ') || mId}
                                </div>
                                <div style={{ font: '400 10px/1 var(--ff-mono)', color: 'var(--tx-3)', marginTop: 3, display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                                  {m.serie && <span>Série {m.serie}</span>}
                                  {m.ano   && <span>· {m.ano}</span>}
                                  {(m.smr || m.horimetro) && <span>· {Number(m.smr || m.horimetro || 0).toLocaleString('pt-BR')}h</span>}
                                  {!isOrphan && <span style={{ color: 'var(--info)', marginLeft: 2 }}>· CSV</span>}
                                </div>
                              </div>
                              {isSel
                                ? <span style={{ font: '600 10px/1 var(--ff-body)', color: 'var(--grn)',
                                                 background: 'var(--grn-050)', padding: '3px 8px',
                                                 borderRadius: 'var(--r-pill)', border: '1px solid var(--grn)',
                                                 whiteSpace: 'nowrap' }}>✓ Selecionada</span>
                                : (
                                  /* SVG inline (circle): renderizado condicionalmente → evita conflito Lucide */
                                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" style={{ color: 'var(--border)', flexShrink: 0 }}>
                                    <circle cx="12" cy="12" r="10"/>
                                  </svg>
                                )
                              }
                            </div>
                          );
                        })}
                      </div>
                    )}

                    {komSel && (
                      <div style={{ font: '400 11px/1.4 var(--ff-body)', color: 'var(--grn)',
                                    background: 'var(--grn-050)', padding: '8px 10px',
                                    borderRadius: 'var(--r-sm)', border: '1px solid var(--grn-100)' }}>
                        {komSel._spId
                          ? '✓ Máquina já importada — será vinculada a este cliente ao salvar, sem criar duplicata.'
                          : '✓ Máquina do Komtrax — será cadastrada na frota e vinculada a este cliente ao salvar.'}
                      </div>
                    )}
                  </div>
                </div>
              )}
            </div>
          );
        })()}

        {erro && (
          <div style={{ padding: '8px 12px', background: '#fee2e2', color: '#b91c1c',
                        border: '1px solid #fecaca', borderRadius: 'var(--r-md)',
                        font: '400 12px/1.4 var(--ff-body)' }}>
            {erro}
          </div>
        )}

        <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 4 }}>
          <button className="btn btn-secondary" onClick={onClose} disabled={saving}>Cancelar</button>
          <button className="btn btn-primary" onClick={salvar} disabled={!podeSalvar}>
            {saving ? "Salvando..." : (editing ? "Salvar alterações" : (komSel ? "Vincular ao cliente" : "Adicionar"))}
          </button>
        </div>
      </div>
    </Modal>
  );
}

// ── ContatosTab — aba de contatos do cliente ──────────
function toWaHref(fone) {
  var d = (fone || '').replace(/\D/g, '');
  if (!d) return '#';
  // Se já tem DDI 55 e comprimento correto (12=fixo, 13=cel), usa direto
  if (d.startsWith('55') && (d.length === 12 || d.length === 13)) return 'https://wa.me/' + d;
  // Senão prepend Brazil code
  return 'https://wa.me/55' + d;
}

function ContatosTab({ cli }) {
  const [contatos, setContatos] = useScreenState(function(){
    try { return JSON.parse(cli.contatos_json || '[]'); } catch(e) { return []; }
  });
  const [modal,      setModal]      = useScreenState(null); // null | {tipo:'add'} | {tipo:'edit',idx:N}
  const [salvando,   setSalvando]   = useScreenState(false);
  const [rfModal,    setRfModal]    = useScreenState(false);
  const [rfLoading,  setRfLoading]  = useScreenState(false);
  const [rfSocios,   setRfSocios]   = useScreenState([]);
  const [rfErro,     setRfErro]     = useScreenState(null);

  React.useEffect(function(){
    try { if (window.lucide) window.lucide.createIcons({ attrs: { 'stroke-width': 1.75 } }); }
    catch(e) {}
  });

  // Salva array de contatos no SP e em CRM_DATA
  async function salvarContatos(novos) {
    setSalvando(true);
    try {
      var json = JSON.stringify(novos);
      if (cli._spId && typeof CRM_API !== 'undefined') {
        await CRM_API.updateItem('Clientes', cli._spId, { contatos_json: json });
      }
      // Atualiza CRM_DATA em memória
      var cliData = window.CRM_DATA && window.CRM_DATA.clientes &&
                    window.CRM_DATA.clientes.find(function(c){ return c.id === cli.id; });
      if (cliData) cliData.contatos_json = json;
      setContatos(novos);
    } catch(e) {
      window.CRM_TOAST('Erro ao salvar contatos: ' + e.message, 'error');
    } finally {
      setSalvando(false);
    }
  }

  async function handleSaveContato(dados) {
    var id = 'CTT-' + Date.now().toString(36).toUpperCase();
    var novo = Object.assign({ id: id, criado_em: new Date().toISOString() }, dados);
    var novos;
    if (modal && modal.tipo === 'edit') {
      var ok = await window.CRM_CONFIRM('Salvar alterações neste contato?', { confirmLabel: 'Salvar', danger: false });
      if (!ok) return;
      novos = contatos.map(function(c, i){ return i === modal.idx ? Object.assign({}, c, dados) : c; });
    } else {
      // K-D12: confirmação também ao adicionar (consistência com editar/remover)
      var okAdd = await window.CRM_CONFIRM('Adicionar este contato?', { confirmLabel: 'Adicionar', danger: false });
      if (!okAdd) return;
      novos = contatos.concat([novo]);
    }
    setModal(null);
    salvarContatos(novos);
  }

  async function handleRemover(idx) {
    if (!await window.CRM_CONFIRM('Remover este contato?', { danger: true, confirmLabel: 'Remover' })) return;
    salvarContatos(contatos.filter(function(_,i){ return i !== idx; }));
  }

  // Enriquecimento com RF
  async function carregarRF() {
    var base = window.BAUKO_AUTH && window.BAUKO_AUTH.CNPJ_API_URL;
    if (!base || base.includes('SEU_IP')) {
      setRfErro('API da Receita Federal não configurada.');
      setRfModal(true);
      return;
    }
    var cnpjDigitos = (cli.cnpj || '').replace(/\D/g,'');
    if (cnpjDigitos.length !== 14) {
      setRfErro('CNPJ do cliente invalido ou ausente.');
      setRfModal(true);
      return;
    }
    setRfLoading(true);
    setRfErro(null);
    setRfModal(true);
    try {
      var resp = await fetch(base.replace(/\/$/, '') + '/empresa/' + cnpjDigitos);
      if (!resp.ok) throw new Error('HTTP ' + resp.status);
      var data = await resp.json();
      setRfSocios((data.socios || []).filter(function(s){ return s.identificador_socio === '2'; }));
    } catch(e) {
      setRfErro('Erro ao consultar RF: ' + e.message);
    } finally {
      setRfLoading(false);
    }
  }

  function adicionarSocioComoContato(socio) {
    var QUALIF = {
      '05':'Administrador','08':'Presidente','10':'Diretor','22':'Socio-Gerente',
      '49':'Socio-Administrador','65':'Titular MEI',
    };
    var funcao = QUALIF[socio.qualificacao_socio] || ('Socio q.' + socio.qualificacao_socio);
    var jaExiste = contatos.some(function(c){
      return (c.nome || '').trim().toUpperCase() === (socio.nome_socio || '').trim().toUpperCase();
    });
    if (jaExiste) {
      window.CRM_TOAST((socio.nome_socio || '') + ' já está na lista de contatos.', 'warn');
      return;
    }
    var id = 'CTT-' + Date.now().toString(36).toUpperCase();
    var novo = {
      id: id, nome: socio.nome_socio || '', telefone: '', funcao: funcao,
      email: '', origem: 'receita_federal', criado_em: new Date().toISOString(),
    };
    var novos = contatos.concat([novo]);
    setContatos(novos);
    salvarContatos(novos);
  }

  var podeCnpj = (cli.tipo_cliente !== 'PF') && !!(cli.cnpj && cli.cnpj.trim());

  var labelStyle = { display: 'block', font: '500 11px/1 var(--ff-body)', color: 'var(--tx-2)', marginBottom: 5 };
  var inputStyleLocal = { width: '100%', padding: '7px 10px', border: '1px solid var(--border-2)', borderRadius: 'var(--r-sm)', background: 'var(--surface)', fontFamily: 'var(--ff-body)', fontSize: 13, color: 'var(--tx)', outline: 'none', boxSizing: 'border-box' };

  return (
    <div>
      {/* Header */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
        <div style={{ font: '600 13px/1 var(--ff-body)', color: 'var(--tx-2)' }}>
          {contatos.length} contato{contatos.length !== 1 ? 's' : ''}
          {salvando && <span style={{ marginLeft: 8, font: '400 11px/1 var(--ff-body)', color: 'var(--tx-3)' }}>Salvando...</span>}
        </div>
        <div style={{ display: 'flex', gap: 8 }}>
          {podeCnpj && (
            <button className="btn btn-secondary" onClick={carregarRF}
              style={{ fontSize: 12, padding: '5px 10px' }}>
              <i data-lucide="building-2" style={{ width: 12, height: 12 }}></i> Sócios RF
            </button>
          )}
          <button className="btn btn-primary" onClick={function(){ setModal({ tipo: 'add' }); }}
            style={{ fontSize: 12, padding: '5px 10px' }}>
            <i data-lucide="plus" style={{ width: 12, height: 12 }}></i> Novo contato
          </button>
        </div>
      </div>

      {/* Lista */}
      {contatos.length === 0 ? (
        <div style={{ textAlign: 'center', padding: '32px 0', color: 'var(--tx-3)', font: '400 13px/1.5 var(--ff-body)' }}>
          <i data-lucide="users" style={{ width: 28, height: 28, display: 'block', margin: '0 auto 10px' }}></i>
          Nenhum contato cadastrado.
          {podeCnpj && <div style={{ marginTop: 8, fontSize: 12 }}>Use "Socios RF" para importar socios da Receita Federal.</div>}
        </div>
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          {contatos.map(function(c, idx){
            return (
              <div key={c.id || idx} style={{ padding: '10px 14px', background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12 }}>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ font: '600 13px/1.2 var(--ff-body)', color: 'var(--tx)', marginBottom: 3 }}>
                    {c.nome || '(sem nome)'}
                    {c.origem === 'receita_federal' && (
                      <span style={{ marginLeft: 6, padding: '1px 6px', background: 'var(--info-050)', border: '1px solid var(--info)', borderRadius: 99, font: '500 9px/1 var(--ff-mono)', color: 'var(--info)', verticalAlign: 'middle' }}>RF</span>
                    )}
                  </div>
                  {c.funcao && <div style={{ font: '400 11px/1 var(--ff-body)', color: 'var(--tx-3)', marginBottom: 4 }}>{c.funcao}</div>}
                  <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
                    {c.telefone && (
                      <a href={toWaHref(c.telefone)} target="_blank" rel="noopener" title="Abrir no WhatsApp"
                        style={{ font: '400 12px/1 var(--ff-mono)', color: 'var(--info)', textDecoration: 'none' }}>
                        <i data-lucide="phone" style={{ width: 11, height: 11, verticalAlign: '-1px', marginRight: 3 }}></i>{c.telefone}
                      </a>
                    )}
                    {c.email && (
                      <a href={"mailto:" + c.email} style={{ font: '400 12px/1 var(--ff-body)', color: 'var(--info)', textDecoration: 'none' }}>
                        <i data-lucide="mail" style={{ width: 11, height: 11, verticalAlign: '-1px', marginRight: 3 }}></i>{c.email}
                      </a>
                    )}
                  </div>
                </div>
                <div style={{ display: 'flex', gap: 4, flexShrink: 0 }}>
                  <button className="btn btn-ghost" onClick={function(){ setModal({ tipo: 'edit', idx: idx }); }}
                    style={{ padding: '4px 7px' }} title="Editar">
                    <i data-lucide="pencil" style={{ width: 12, height: 12 }}></i>
                  </button>
                  <button className="btn btn-ghost" onClick={function(){ handleRemover(idx); }}
                    style={{ padding: '4px 7px', color: 'var(--danger)' }} title="Remover">
                    <i data-lucide="trash-2" style={{ width: 12, height: 12 }}></i>
                  </button>
                </div>
              </div>
            );
          })}
        </div>
      )}

      {/* Modal form contato */}
      {modal && (
        <ContatoFormModal
          open={!!modal}
          inicial={modal.tipo === 'edit' ? contatos[modal.idx] : null}
          onClose={function(){ setModal(null); }}
          onSave={handleSaveContato}
        />
      )}

      {/* Modal sócios RF */}
      <Modal open={rfModal} onClose={function(){ setRfModal(false); }} title="Socios na Receita Federal"
        subtitle={cli.razao}>
        <div style={{ marginTop: 12 }}>
          {rfLoading && <div style={{ textAlign: 'center', padding: '24px 0', color: 'var(--tx-3)' }}>Consultando RF...</div>}
          {rfErro && <div style={{ padding: '10px 14px', background: '#fef2f2', border: '1px solid var(--danger)', borderRadius: 'var(--r-sm)', font: '400 13px/1.4 var(--ff-body)', color: 'var(--tx)' }}>{rfErro}</div>}
          {!rfLoading && !rfErro && rfSocios.length === 0 && (
            <div style={{ textAlign: 'center', padding: '20px 0', color: 'var(--tx-3)', font: '400 13px/1.5 var(--ff-body)' }}>Nenhum socio encontrado.</div>
          )}
          {!rfLoading && rfSocios.length > 0 && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {rfSocios.map(function(s, i){
                var jaExiste = contatos.some(function(c){ return (c.nome||'').trim().toUpperCase() === (s.nome_socio||'').trim().toUpperCase(); });
                return (
                  <div key={i} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px 12px', background: jaExiste ? 'var(--grn-050)' : 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 'var(--r-sm)' }}>
                    <div>
                      <div style={{ font: '500 13px/1.2 var(--ff-body)', color: 'var(--tx)' }}>{s.nome_socio}</div>
                      <div style={{ font: '400 11px/1 var(--ff-body)', color: 'var(--tx-3)', marginTop: 3 }}>Qualificacao: {s.qualificacao_socio}</div>
                    </div>
                    {jaExiste ? (
                      <span style={{ font: '500 11px/1 var(--ff-mono)', color: 'var(--grn-600)' }}>ja adicionado</span>
                    ) : (
                      <button className="btn btn-secondary" onClick={function(){ adicionarSocioComoContato(s); }}
                        style={{ fontSize: 12, padding: '4px 10px' }}>
                        <i data-lucide="plus" style={{ width: 12, height: 12 }}></i> Adicionar
                      </button>
                    )}
                  </div>
                );
              })}
            </div>
          )}
          <div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: 16 }}>
            <button className="btn btn-secondary" onClick={function(){ setRfModal(false); }}>Fechar</button>
          </div>
        </div>
      </Modal>
    </div>
  );
}

// ── ContatoFormModal — formulário de novo / editar contato ──
function ContatoFormModal({ open, onClose, onSave, inicial }) {
  const [nome,     setNome]     = useScreenState(inicial ? inicial.nome || '' : '');
  const [telefone, setTelefone] = useScreenState(inicial ? inicial.telefone || '' : '');
  const [funcao,   setFuncao]   = useScreenState(inicial ? inicial.funcao || '' : '');
  const [email,    setEmail]    = useScreenState(inicial ? inicial.email || '' : '');

  // Reset quando modal reabre com dados diferentes
  React.useEffect(function(){
    setNome(inicial ? inicial.nome || '' : '');
    setTelefone(inicial ? inicial.telefone || '' : '');
    setFuncao(inicial ? inicial.funcao || '' : '');
    setEmail(inicial ? inicial.email || '' : '');
  }, [open, inicial]);

  function handleSalvar() {
    if (!(nome || '').trim()) { window.CRM_TOAST('Informe o nome do contato.', 'warn'); return; }
    // K-D10: validações de e-mail e telefone (só se preenchidos)
    if (!isEmailValido(email)) { window.CRM_TOAST('E-mail do contato é inválido.', 'warn'); return; }
    if (!isFoneValido(telefone)) { window.CRM_TOAST('Telefone do contato é inválido (use 10 ou 11 dígitos).', 'warn'); return; }
    onSave({ nome: nome.trim(), telefone: (telefone||'').trim(), funcao: (funcao||'').trim(), email: (email||'').trim() });
  }

  var lbl  = { display: 'block', font: '500 11px/1 var(--ff-body)', color: 'var(--tx-2)', marginBottom: 5 };
  var inp  = { width: '100%', padding: '7px 10px', border: '1px solid var(--border-2)', borderRadius: 'var(--r-sm)', background: 'var(--surface)', fontFamily: 'var(--ff-body)', fontSize: 13, color: 'var(--tx)', outline: 'none', boxSizing: 'border-box' };
  var row  = { marginBottom: 12 };

  return (
    <Modal open={open} onClose={onClose} title={inicial ? 'Editar contato' : 'Novo contato'}>
      <div style={{ marginTop: 14, display: 'flex', flexDirection: 'column', gap: 0 }}>
        <div style={row}>
          <label style={lbl}>Nome <span style={{ color: 'var(--danger)' }}>*</span></label>
          <input style={inp} value={nome} onChange={function(e){ setNome(e.target.value); }} placeholder="Ex: Joao Silva" />
        </div>
        <div style={row}>
          <label style={lbl}>Funcao / Cargo</label>
          <input style={inp} value={funcao} onChange={function(e){ setFuncao(e.target.value); }} placeholder="Ex: Diretor Operacional" />
        </div>
        <div style={row}>
          <label style={lbl}>Telefone / WhatsApp</label>
          <input style={inp} value={telefone} onChange={function(e){ setTelefone(e.target.value); }} placeholder="(11) 99999-9999" type="tel" />
        </div>
        <div style={{ marginBottom: 18 }}>
          <label style={lbl}>E-mail</label>
          <input style={inp} value={email} onChange={function(e){ setEmail(e.target.value); }} placeholder="contato@empresa.com.br" type="email" />
        </div>
        <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
          <button className="btn btn-secondary" onClick={onClose}>Cancelar</button>
          <button className="btn btn-primary" onClick={handleSalvar}>
            <i data-lucide="check"></i> Salvar
          </button>
        </div>
      </div>
    </Modal>
  );
}

function FrotaTab({ cli }) {
  const [, tick] = useScreenState(0);
  const [modalOpen, setModalOpen] = useScreenState(false);
  const [editando,  setEditando]  = useScreenState(null);
  const [excluindo, setExcluindo] = useScreenState(null); // id da máquina em deleção

  React.useEffect(() => {
    try { if (window.lucide) window.lucide.createIcons({ attrs: { 'stroke-width': 1.75 } }); }
    catch(e) {}
  });

  const maquinas         = (CRM_DATA.frotaCliente || []).filter(m => m.cliente_id === cli.id);
  const maqRepresentadas = maquinas.filter(m => m.tipo !== 'concorrente');
  const maqConcorrentes  = maquinas.filter(m => m.tipo === 'concorrente');

  // Diagnóstico: avisa no console se há máquinas no array geral mas nenhuma bate com este cliente
  React.useEffect(() => {
    const total = (CRM_DATA.frotaCliente || []).length;
    if (total > 0 && maquinas.length === 0) {
      console.warn('[FrotaTab] ' + total + ' máquina(s) em CRM_DATA.frotaCliente mas nenhuma com cliente_id === "' + cli.id + '".');
      console.warn('[FrotaTab] Amostra (3 primeiros):', (CRM_DATA.frotaCliente || []).slice(0, 3).map(m => ({ id: m.id, cliente_id: m.cliente_id })));
    }
  }, [cli.id, maquinas.length]);

  function abrirNovo()           { setEditando(null); setModalOpen(true); }
  function abrirEditar(m)        { setEditando(m); setModalOpen(true); }
  function onSaveModal()         { tick(t => t + 1); }

  async function removerMaquina(m) {
    if (!await window.CRM_CONFIRM('Remover a máquina ' + maq_resumo(m) + '?', { danger: true, confirmLabel: 'Remover' })) return;
    setExcluindo(m.id);
    try {
      if (window.CRM_API && m._spId) {
        await window.CRM_API.deleteItem('FrotaCliente', m._spId);
      }
      var arr = CRM_DATA.frotaCliente || [];
      var idx = arr.findIndex(x => x.id === m.id);
      if (idx >= 0) arr.splice(idx, 1);
      tick(t => t + 1);
    } catch (e) {
      console.error('[FrotaCliente] remover falhou:', e);
      window.CRM_TOAST('Falha ao remover: ' + (e.message || 'erro desconhecido'), 'error');
    } finally {
      setExcluindo(null);
    }
  }

  // Linha compartilhada pelas seções Representada / Concorrente
  function renderMaqRow(m) {
    return (
      <tr key={m.id}>
        <td style={{ font: '500 13px/1.2 var(--ff-body)' }}>{m.fabricante || '—'}</td>
        <td className="tx2" style={{ fontSize: 13 }}>{m.modelo || '—'}</td>
        <td style={{ fontSize: 12, fontFamily: 'var(--ff-mono)', color: 'var(--tx-2)' }}>
          {m.serie || <span style={{ color: 'var(--tx-3)' }}>—</span>}
        </td>
        <td className="num">{m.ano || '—'}</td>
        <td className="num">{m.horimetro != null ? (m.horimetro.toLocaleString('pt-BR') + ' h') : '—'}</td>
        <td style={{ textAlign: 'right' }}>
          <button onClick={() => abrirEditar(m)} title="Editar"
                  style={{ all: 'unset', cursor: 'pointer', padding: 6, color: 'var(--tx-2)' }}>
            <i data-lucide="pencil" style={{ width: 14, height: 14, verticalAlign: 'middle' }}></i>
          </button>
          <button onClick={() => removerMaquina(m)} title="Remover"
                  disabled={excluindo === m.id}
                  style={{ all: 'unset', cursor: excluindo === m.id ? 'wait' : 'pointer',
                           padding: 6, color: '#dc2626', marginLeft: 4 }}>
            {/* SVG inline: ícone muda dinamicamente — usando <i data-lucide> causava
                crash de removeChild quando a tabela re-renderizava com nova linha
                (após salvar máquina nova). Isso resetava o cliente selecionado. */}
            {excluindo === m.id ? (
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
                   strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
                   style={{ verticalAlign: 'middle', animation: 'bdrSpin 1s linear infinite' }}>
                <line x1="12" y1="2" x2="12" y2="6"/><line x1="12" y1="18" x2="12" y2="22"/>
                <line x1="4.93" y1="4.93" x2="7.76" y2="7.76"/><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"/>
                <line x1="2" y1="12" x2="6" y2="12"/><line x1="18" y1="12" x2="22" y2="12"/>
                <line x1="4.93" y1="19.07" x2="7.76" y2="16.24"/><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"/>
              </svg>
            ) : (
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
                   strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
                   style={{ verticalAlign: 'middle' }}>
                <polyline points="3 6 5 6 21 6"/>
                <path d="M19 6l-2 14a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2L5 6"/>
                <path d="M10 11v6"/><path d="M14 11v6"/>
                <path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/>
              </svg>
            )}
          </button>
        </td>
      </tr>
    );
  }

  return (
    <Panel title="Frota do cliente" meta={
      [
        maqRepresentadas.length > 0 ? (maqRepresentadas.length + ' representada' + (maqRepresentadas.length === 1 ? '' : 's')) : null,
        maqConcorrentes.length  > 0 ? (maqConcorrentes.length  + ' concorrente' + (maqConcorrentes.length  === 1 ? '' : 's')) : null,
      ].filter(Boolean).join(' · ') || 'nenhuma máquina'
    }
      actions={
        <button className="btn btn-primary" onClick={abrirNovo} style={{ fontSize: 12, padding: '5px 10px' }}>
          <i data-lucide="plus" style={{ width: 12, height: 12 }}></i> Adicionar máquina
        </button>
      }
      noPad
    >
      {maquinas.length === 0 ? (
        <div style={{ padding: '40px 0', textAlign: 'center', color: 'var(--tx-3)' }}>
          <i data-lucide="truck" style={{ width: 26, height: 26, display: 'block', margin: '0 auto 10px' }}></i>
          <div style={{ font: '500 13px/1.4 var(--ff-body)', color: 'var(--tx-2)', marginBottom: 10 }}>
            Nenhuma máquina cadastrada
          </div>
          <button className="btn btn-primary" onClick={abrirNovo} style={{ margin: '0 auto' }}>
            <i data-lucide="plus" style={{ width: 13, height: 13 }}></i> Cadastrar primeira máquina
          </button>
        </div>
      ) : (
        <div>
          {maqRepresentadas.length > 0 && (
            <div>
              <div style={{ padding: '7px 14px', font: '500 10px/1 var(--ff-body)', color: 'var(--tx-3)', letterSpacing: '.08em', textTransform: 'uppercase', background: 'var(--bg-2)', borderBottom: '1px solid var(--border)' }}>
                Representada · {maqRepresentadas.length} máquina{maqRepresentadas.length === 1 ? '' : 's'}
              </div>
              <div className="dt-wrap">
                <table className="dt">
                  <thead><tr>
                    <th>Fabricante</th><th>Modelo</th><th>Série</th>
                    <th style={{ textAlign: 'right' }}>Ano</th>
                    <th style={{ textAlign: 'right' }}>Horímetro</th>
                    <th style={{ width: 90, textAlign: 'right' }}>Ações</th>
                  </tr></thead>
                  <tbody>{maqRepresentadas.map(renderMaqRow)}</tbody>
                </table>
              </div>
            </div>
          )}
          {maqConcorrentes.length > 0 && (
            <div>
              <div style={{ padding: '7px 14px', font: '500 10px/1 var(--ff-body)', color: 'var(--tx-3)', letterSpacing: '.08em', textTransform: 'uppercase', background: 'var(--bg-2)', borderTop: maqRepresentadas.length > 0 ? '2px solid var(--border)' : undefined, borderBottom: '1px solid var(--border)' }}>
                Concorrente · {maqConcorrentes.length} máquina{maqConcorrentes.length === 1 ? '' : 's'}
                <span style={{ fontWeight: 400, textTransform: 'none', letterSpacing: 0, marginLeft: 8 }}>· via Vendas Perdidas</span>
              </div>
              <div className="dt-wrap">
                <table className="dt">
                  <thead><tr>
                    <th>Fabricante</th><th>Modelo</th><th>Série</th>
                    <th style={{ textAlign: 'right' }}>Ano</th>
                    <th style={{ textAlign: 'right' }}>Horímetro</th>
                    <th style={{ width: 90, textAlign: 'right' }}>Ações</th>
                  </tr></thead>
                  <tbody>{maqConcorrentes.map(renderMaqRow)}</tbody>
                </table>
              </div>
            </div>
          )}
        </div>
      )}

      <MaquinaFrotaModal
        open={modalOpen}
        onClose={() => setModalOpen(false)}
        onSave={onSaveModal}
        clienteId={cli.id}
        inicial={editando}
      />
    </Panel>
  );
}

function ClienteModal({ open, onClose, onSave, inicial, empresaPreFill }) {
  const editing = !!inicial;
  const [tipoCliente, setTipoCliente] = useScreenState("PJ"); // 'PJ' | 'PF'
  const [razao,    setRazao]    = useScreenState("");
  const [cnpj,     setCnpj]     = useScreenState("");
  const [cpf,      setCpf]      = useScreenState("");
  const [atividade,setAtividade]= useScreenState("");
  const [segmento, setSegmento] = useScreenState("");
  const [ie,       setIe]       = useScreenState("");
  const [endereco, setEndereco] = useScreenState("");
  const [cep,      setCep]      = useScreenState("");
  const [cidade,   setCidade]   = useScreenState("");
  const [uf,       setUf]       = useScreenState("");
  const [contato,  setContato]  = useScreenState("");
  const [fone,     setFone]     = useScreenState("");
  const [email,    setEmail]    = useScreenState("");
  const [volume,   setVolume]   = useScreenState("");
  const [status,   setStatus]   = useScreenState("prospecto");
  const [classe,   setClasse]   = useScreenState("");
  const [vendedorId, setVendedorId] = useScreenState(""); // email do vendedor responsável (carteira)
  const [regiaoSugerida, setRegiaoSugerida] = useScreenState(null); // { regiao_nome, vendedor_email } da carteira territorial
  const [saving,   setSaving]   = useScreenState(false);
  const [erro,     setErro]     = useScreenState(null);
  const [cnpjDup,  setCnpjDup]  = useScreenState(null); // cliente duplicado pelo CNPJ
  // Busca de empresa (CNPJ API) — só PJ
  const [buscaTermo,      setBuscaTermo]      = useScreenState("");
  const [buscaLoading,    setBuscaLoading]    = useScreenState(false);
  const [buscaResultados, setBuscaResultados] = useScreenState([]);
  const [buscaAberta,     setBuscaAberta]     = useScreenState(false);
  const [autoFilled,      setAutoFilled]      = useScreenState(false);
  const segmentoRef = React.useRef(null);
  // Inativar / Excluir / Reativar
  const [showInativarModal, setShowInativarModal] = useScreenState(false);
  const [inativarMotivo,    setInativarMotivo]    = useScreenState("");
  const [inativarCheck,     setInativarCheck]     = useScreenState(false);
  const [inativarModoDelete,setInativarModoDelete]= useScreenState(false);
  const [showReativarModal, setShowReativarModal] = useScreenState(false);
  const [reativarMotivo,    setReativarMotivo]    = useScreenState("");
  const [reativarCheck,     setReativarCheck]     = useScreenState(false);
  const isGerente = !!(window.CRM_USER && window.CRM_USER.isGerente);

  React.useEffect(() => {
    if (open && inicial) {
      setTipoCliente(inicial.tipo_cliente === 'PF' ? 'PF' : 'PJ');
      setRazao(inicial.razao || "");
      setCnpj(inicial.cnpj || "");
      setCpf(inicial.cpf || "");
      setAtividade(inicial.atividade || "");
      setSegmento(inicial.segmento || "");
      setIe(inicial.ie || "");
      setEndereco(inicial.endereco || "");
      setCep(inicial.cep || "");
      setCidade(inicial.cidade || "");
      setUf(inicial.uf || "");
      setContato(inicial.contato || "");
      setFone(inicial.fone || "");
      setEmail(inicial.email || "");
      setVolume(inicial.volume_anual != null ? String(inicial.volume_anual) : "");
      setStatus(inicial.status || "prospecto");
      setClasse(inicial.classe || "");
      setVendedorId(inicial.vendedor_id || "");
    } else if (open && !inicial) {
      setTipoCliente("PJ");
      setRazao(""); setCnpj(""); setCpf(""); setAtividade("");
      setSegmento(""); setIe(""); setEndereco("");
      setCep(""); setCidade(""); setUf(""); setContato(""); setFone("");
      setEmail(""); setVolume(""); setStatus("prospecto"); setClasse("");
      setVendedorId("");
    }
    // Reset do estado de busca a cada abertura
    setBuscaTermo(""); setBuscaLoading(false);
    setBuscaResultados([]); setBuscaAberta(false); setAutoFilled(false);
    setErro(null); setCnpjDup(null);

    // Pré-fill via prop (ex.: vindo da tela Prospecção). O formato esperado é o
    // mesmo do mock de empresas (cnpj_raw / razao_social / municipio_nome / uf …).
    // Preenche apenas os campos disponíveis — o usuário ainda escolhe Segmento manualmente.
    // Roda APÓS o reset de busca para o `autoFilled = true` não ser sobrescrito.
    if (open && !inicial && empresaPreFill) {
      setTipoCliente('PJ');
      setRazao(empresaPreFill.razao_social || '');
      setCnpj(empresaPreFill.cnpj_formatado || (empresaPreFill.cnpj_raw ? maskCNPJ(empresaPreFill.cnpj_raw) : ''));
      var enderecoMontado = [
        empresaPreFill.logradouro || '',
        empresaPreFill.numero ? ', ' + empresaPreFill.numero : '',
        empresaPreFill.complemento ? ' - ' + empresaPreFill.complemento : '',
        empresaPreFill.bairro ? ' — ' + empresaPreFill.bairro : ''
      ].join('').trim();
      if (enderecoMontado) setEndereco(enderecoMontado);
      if (empresaPreFill.cep) setCep(maskCEP(empresaPreFill.cep));
      setCidade(empresaPreFill.municipio_nome || '');
      setUf((empresaPreFill.uf || '').toUpperCase().slice(0, 2));
      if (empresaPreFill.telefone) setFone(maskPhone(empresaPreFill.telefone));
      if (empresaPreFill.email)    setEmail(String(empresaPreFill.email).toLowerCase());
      setAutoFilled(true);
      setTimeout(function() {
        try { segmentoRef.current && segmentoRef.current.focus(); } catch (e) {}
      }, 50);
    }
  }, [open]);

  // ── Auto-fill territorial: quando cidade+UF mudam, busca a região oficial ──
  // Se cliente ainda não tem vendedor, sugere o da região (Lista SP "Regioes").
  // Não sobrescreve escolha manual — só preenche se vendedorId estiver vazio.
  React.useEffect(() => {
    if (!open) return;
    if (!cidade || !uf || uf.length < 2) {
      setRegiaoSugerida(null);
      return;
    }
    var r = (window.CRM_DATA && typeof CRM_DATA.getRegiaoPorCidadeUF === 'function')
      ? CRM_DATA.getRegiaoPorCidadeUF(cidade, uf)
      : null;
    if (!r) {
      setRegiaoSugerida(null);
      return;
    }
    setRegiaoSugerida({ regiao_nome: r.regiao_nome, vendedor_email: r.vendedor_atual });
    // Sugere apenas quando o campo está vazio — não pisa em escolha consciente
    if (!vendedorId && r.vendedor_atual) {
      setVendedorId(r.vendedor_atual);
    }
  }, [open, cidade, uf]);

  async function handleSave() {
    // Validações explícitas com toast — botão não é disabled, então precisa checar aqui
    if (!razao.trim()) {
      window.CRM_TOAST('Informe a razão social / nome do cliente.', 'warn');
      return;
    }
    if (cnpjDup) {
      window.CRM_TOAST('Este CNPJ já está cadastrado para outro cliente.', 'warn');
      return;
    }
    if (!_docOk) {
      window.CRM_TOAST(
        tipoCliente === 'PJ'
          ? 'CNPJ inválido — verifique os dígitos verificadores.'
          : 'CPF inválido — verifique os dígitos.',
        'warn'
      );
      return;
    }
    if (!_emailOk) {
      window.CRM_TOAST('E-mail inválido — verifique o formato (ex: nome@empresa.com.br).', 'warn');
      return;
    }
    if (!_foneOk) {
      window.CRM_TOAST('Telefone inválido — use 10 ou 11 dígitos, ex: (11) 98765-4321.', 'warn');
      return;
    }
    setSaving(true); setErro(null);
    const obj = {
      id:           inicial?.id || ("C-" + String(Date.now()).slice(-6)),
      tipo_cliente: tipoCliente,
      razao:        razao.trim(),
      // PJ → grava CNPJ; PF → grava CPF. O lado oposto fica vazio (não nulo).
      cnpj:         tipoCliente === 'PJ' ? cnpj.trim() : '',
      cpf:          tipoCliente === 'PF' ? cpf.trim()  : '',
      atividade:    tipoCliente === 'PF' ? atividade.trim() : '',
      segmento:     segmento,
      ie:           ie.trim(),
      endereco:     endereco.trim(),
      cep:          cep.trim(),
      cidade:       cidade.trim(),
      uf:           uf.trim().toUpperCase().slice(0,2),
      contato:      contato.trim(),
      fone:         fone.trim(),
      email:        email.trim().toLowerCase(),
      volume_anual: volume !== "" ? Number(volume) : 0,
      status:       status,
      classe:       classe || "",
      vendedor_id:  vendedorId || "",
    };
    // Captura vendor anterior antes do save (para detectar troca)
    const vendedorAnterior = inicial && inicial.vendedor_id;
    try {
      if (editing && inicial._spId) {
        await CRM_API.updateItem("Clientes", inicial._spId, obj);
        const idx = CRM_DATA.clientes.findIndex(c => c.id === obj.id);
        if (idx >= 0) CRM_DATA.clientes[idx] = { ...CRM_DATA.clientes[idx], ...obj };
      } else {
        const created = await CRM_API.addItem("Clientes", obj);
        const newSpId = created && String(created.id || '');
        CRM_DATA.clientes.push({ ...obj, _spId: newSpId });
      }
      onSave(obj);
      onClose();
      // Cascade de responsável: propõe transferir ops/propostas/pedidos em aberto
      _cascadeVendedor(obj.id, vendedorAnterior, obj.vendedor_id);
    } catch(e) {
      setErro("Erro ao salvar: " + e.message);
    } finally {
      setSaving(false);
    }
  }

  // Após troca de proprietário do cliente, pergunta se quer transferir também
  // as oportunidades, propostas e pedidos em aberto para o novo responsável.
  function _cascadeVendedor(clienteId, vendAnterior, vendNovo) {
    if (!editing || !vendAnterior || !vendNovo || vendAnterior === vendNovo) return;
    var CD = window.CRM_DATA;
    if (!CD) return;

    // Oportunidades em aberto deste cliente
    var opsAbertas = (CD.oportunidades || []).filter(function(op) {
      return op.cliente_id === clienteId
        && !(CD.isOportunidadeCancelada && CD.isOportunidadeCancelada(op))
        && op.etapa !== 'fechado';
    });
    var opIds = new Set(opsAbertas.map(function(op) { return op.id; }));

    // Propostas em aberto dessas oportunidades
    var propostasAbertas = (CD.propostas || []).filter(function(prop) {
      return opIds.has(prop.op_id)
        && !(CD.isPropostaCancelada && CD.isPropostaCancelada(prop))
        && prop.status !== 'perdida';
    });

    // Pedidos em aberto (status emitido) deste cliente
    var pedidosAbertos = (CD.pedidos || []).filter(function(ped) {
      return ped.cliente_id === clienteId
        && !(CD.isPedidoCancelado && CD.isPedidoCancelado(ped))
        && ped.status === 'emitido';
    });

    if (!opsAbertas.length && !propostasAbertas.length && !pedidosAbertos.length) return;

    var novoVend = CD.getUsuario && CD.getUsuario(vendNovo);
    var nomeNovo = novoVend ? novoVend.nome : vendNovo;

    var partes = [];
    if (opsAbertas.length)     partes.push(opsAbertas.length     + ' oportunidade' + (opsAbertas.length     > 1 ? 's' : ''));
    if (propostasAbertas.length) partes.push(propostasAbertas.length + ' proposta'  + (propostasAbertas.length > 1 ? 's' : ''));
    if (pedidosAbertos.length)  partes.push(pedidosAbertos.length  + ' pedido'      + (pedidosAbertos.length  > 1 ? 's' : ''));

    window.CRM_CONFIRM(
      'Transferir também ' + partes.join(', ') + ' em aberto para ' + nomeNovo + '?',
      { confirmLabel: 'Transferir', cancelLabel: 'Só o cliente' }
    ).then(function(ok) {
      if (!ok) return;
      var promises = [];

      opsAbertas.forEach(function(op) {
        var idx = (CD.oportunidades || []).findIndex(function(o) { return o.id === op.id; });
        if (idx >= 0) CD.oportunidades[idx] = Object.assign({}, CD.oportunidades[idx], { vendedor_id: vendNovo });
        if (op._spId) promises.push(
          window.CRM_API.updateItem('Oportunidades', op._spId, { vendedor_id: vendNovo })
            .catch(function(e) { console.warn('[Cascade] Op ' + op.id + ':', e.message); })
        );
      });

      propostasAbertas.forEach(function(prop) {
        var idx = (CD.propostas || []).findIndex(function(p) { return p.id === prop.id; });
        if (idx >= 0) CD.propostas[idx] = Object.assign({}, CD.propostas[idx], { responsavel_id: vendNovo });
        if (prop._spId) promises.push(
          window.CRM_API.updateItem('Propostas', prop._spId, { responsavel_id: vendNovo })
            .catch(function(e) { console.warn('[Cascade] Prop ' + prop.id + ':', e.message); })
        );
      });

      pedidosAbertos.forEach(function(ped) {
        var idx = (CD.pedidos || []).findIndex(function(p) { return p.id === ped.id; });
        if (idx >= 0) CD.pedidos[idx] = Object.assign({}, CD.pedidos[idx], { vendedor_id: vendNovo });
        if (ped._spId) promises.push(
          window.CRM_API.updateItem('CRMPedidos', ped._spId, { vendedor_id: vendNovo })
            .catch(function(e) { console.warn('[Cascade] Ped ' + ped.id + ':', e.message); })
        );
      });

      Promise.all(promises).then(function() {
        window.CRM_TOAST && window.CRM_TOAST(
          'Transferência concluída: ' + partes.join(', ') + ' passados para ' + nomeNovo + '.',
          'ok'
        );
      });
    });
  }

  // ── Verifica duplicidade de CNPJ ────────────────────────────────────────────
  function checkCnpjDup(valor) {
    if (tipoCliente !== 'PJ') { setCnpjDup(null); return; }
    const digits = (valor || cnpj).replace(/\D/g, '');
    if (digits.length < 14) { setCnpjDup(null); return; }
    const currentId = inicial && inicial.id;
    const found = (CRM_DATA.clientes || []).find(function(c) {
      return c.id !== currentId && c.cnpj && c.cnpj.replace(/\D/g, '') === digits;
    });
    setCnpjDup(found || null);
  }

  // ── Busca de empresa (CNPJ API) ─────────────────────────────────────────
  // Hoje filtra um array mock; quando a API local em BAUKO_AUTH.CNPJ_API_URL
  // estiver no ar, basta trocar a parte do "MOCK" por um fetch real.
  async function buscarEmpresa() {
    const termo = buscaTermo.trim();
    if (!termo) return;
    setBuscaLoading(true);
    setBuscaAberta(true);
    setBuscaResultados([]);
    let resultados = [];
    try {
      const base = window.BAUKO_AUTH && window.BAUKO_AUTH.CNPJ_API_URL;
      if (!base || base.includes('SEU_IP')) throw new Error('CNPJ_API_URL não configurado');
      const url  = base.replace(/\/$/, '') + '/buscar?q=' + encodeURIComponent(termo) + '&limit=8';
      const resp = await fetch(url);
      if (!resp.ok) throw new Error('HTTP ' + resp.status);
      resultados = await resp.json();
    } catch (e) {
      console.warn('[CRM] buscarEmpresa:', e.message);
    } finally {
      setBuscaResultados(resultados);
      setBuscaLoading(false);
    }
  }

  function selecionarEmpresa(emp) {
    setRazao(emp.razao_social || '');
    setCnpj(emp.cnpj_formatado || maskCNPJ(emp.cnpj || ''));
    const enderecoMontado = [
      emp.logradouro || '',
      emp.numero ? ', ' + emp.numero : '',
      emp.complemento ? ' - ' + emp.complemento : '',
      emp.bairro ? ' — ' + emp.bairro : ''
    ].join('').trim();
    setEndereco(enderecoMontado);
    setCep(emp.cep ? maskCEP(emp.cep) : '');
    setCidade(emp.municipio_nome || '');
    setUf((emp.uf || '').toUpperCase().slice(0,2));
    setFone(emp.telefone ? maskPhone(emp.telefone) : '');
    setEmail((emp.email || '').toLowerCase());
    setBuscaResultados([]);
    setBuscaAberta(false);
    setBuscaTermo(emp.razao_social || '');
    setAutoFilled(true);
    // Foca em "Segmento" — campo que o vendedor ainda precisa preencher manualmente
    setTimeout(() => {
      try { segmentoRef.current && segmentoRef.current.focus(); } catch (e) {}
    }, 50);
  }

  // K-D1: validações de formato (e-mail, telefone, CNPJ/CPF) — só falham se preenchidos
  const _emailOk = isEmailValido(email);
  const _foneOk  = isFoneValido(fone);
  const _docOk   = tipoCliente === 'PJ' ? isCNPJValido(cnpj) : isCPFValido(cpf);
  const canSave = razao.trim().length > 0 && !saving && !cnpjDup && _emailOk && _foneOk && _docOk;

  // Oportunidades ativas vinculadas ao cliente (bloqueio pra Excluir)
  var oportunidadesAtivas = editing
    ? (CRM_DATA.oportunidades || []).filter(o => o.cliente_id === inicial.id && !o.cancelada_em)
    : [];
  var temOportunidadesAtivas = oportunidadesAtivas.length > 0;

  async function handleInativarCliente(motivo) {
    if (!inicial) return;
    var inativadaEm = new Date().toISOString();
    // Autor do log: usuário REAL (auditoria — não muda com "Visualizar como")
    var autor = (window.CRM_USER && window.CRM_USER.realEmail) || '';
    // Atualiza em memória + SP
    var patch = { status: 'inativo' };
    console.log('[CRM] Cliente inativado:', inicial.id, '· motivo:', motivo, '· por:', autor, '· em:', inativadaEm);
    if (inicial._spId && window.CRM_API) {
      try { await CRM_API.updateItem('Clientes', inicial._spId, patch); }
      catch(e) { console.warn('[CRM] updateItem cliente inativar:', e.message); }
    }
    // K-D2: persiste motivo via Atividade automática (auditoria duradoura)
    if (window.CRM_API && motivo) {
      var atvId = 'ATV-' + Date.now().toString(36).toUpperCase();
      var atividade = {
        id: atvId,
        tipo: 'followup',
        titulo: 'Cliente inativado',
        cliente_id: inicial.id,
        op_id: '',
        vendedor_id: inicial.vendedor_id || autor,
        data: inativadaEm,
        duracao: '—',
        resultado: 'neutro',
        notas: 'Motivo: ' + motivo + ' · Por: ' + autor,
        autor: autor,
      };
      CRM_API.addItem('Atividades', atividade)
        .then(function(created) {
          if (created && created.id) atividade._spId = String(created.id);
          if (window.CRM_DATA && Array.isArray(CRM_DATA.atividades)) CRM_DATA.atividades.unshift(atividade);
        })
        .catch(function(e){ console.warn('[CRM] atv inativacao:', e.message); });
    }
    if (CRM_DATA.clientes) {
      var idx = CRM_DATA.clientes.findIndex(c => c.id === inicial.id);
      if (idx >= 0) CRM_DATA.clientes[idx] = Object.assign({}, CRM_DATA.clientes[idx], patch);
    }
    setShowInativarModal(false);
    if (typeof window.CRM_TOAST === 'function') window.CRM_TOAST('Cliente inativado.', 'ok');
    onSave && onSave(Object.assign({}, inicial, patch));
    onClose && onClose();
  }

  async function handleExcluirCliente() {
    if (!inicial) return;
    if (temOportunidadesAtivas) {
      window.CRM_TOAST('Não é possível excluir: existem ' + oportunidadesAtivas.length + ' oportunidade(s) ativa(s) vinculada(s). Cancele-as antes.', 'warn');
      return;
    }
    // Hard delete — só cliente em si (oportunidades canceladas ficam órfãs mas preservam histórico)
    if (inicial._spId && window.CRM_API) {
      try { await CRM_API.deleteItem('Clientes', inicial._spId); }
      catch(e) { console.warn('[CRM] deleteItem cliente:', e.message); }
    }
    if (CRM_DATA.clientes) {
      var idx = CRM_DATA.clientes.findIndex(c => c.id === inicial.id);
      if (idx >= 0) CRM_DATA.clientes.splice(idx, 1);
    }
    setShowInativarModal(false);
    if (typeof window.CRM_TOAST === 'function') window.CRM_TOAST('Cliente excluído definitivamente.', 'ok');
    onSave && onSave(null);
    onClose && onClose();
  }

  function abrirInativarModal() {
    setInativarMotivo('');
    setInativarCheck(false);
    setInativarModoDelete(false);
    setShowInativarModal(true);
  }

  async function handleReativarCliente(motivo) {
    if (!inicial) return;
    // K-D3: motivo obrigatório na reativação (consistência com inativação)
    if (!motivo || motivo.trim().length < 10) {
      if (typeof window.CRM_TOAST === 'function') {
        window.CRM_TOAST('Informe o motivo da reativação (mínimo 10 caracteres).', 'warn');
      }
      return;
    }
    var reativadoEm = new Date().toISOString();
    // Autor do log: usuário REAL (auditoria — não muda com "Visualizar como")
    var autor = (window.CRM_USER && window.CRM_USER.realEmail) || '';
    var patch = { status: 'ativo' };
    console.log('[CRM] Cliente reativado:', inicial.id, '· motivo:', motivo, '· por:', autor, '· em:', reativadoEm);
    if (inicial._spId && window.CRM_API) {
      try { await CRM_API.updateItem('Clientes', inicial._spId, patch); }
      catch(e) { console.warn('[CRM] updateItem cliente reativar:', e.message); }
    }
    if (CRM_DATA.clientes) {
      var idx = CRM_DATA.clientes.findIndex(c => c.id === inicial.id);
      if (idx >= 0) CRM_DATA.clientes[idx] = Object.assign({}, CRM_DATA.clientes[idx], patch);
    }
    // K-D2: persiste motivo de reativação via Atividade automática
    if (window.CRM_API) {
      var atvId = 'ATV-' + Date.now().toString(36).toUpperCase();
      var atividade = {
        id: atvId,
        tipo: 'followup',
        titulo: 'Cliente reativado',
        cliente_id: inicial.id,
        op_id: '',
        vendedor_id: inicial.vendedor_id || autor,
        data: reativadoEm,
        duracao: '—',
        resultado: 'positivo',
        notas: 'Motivo: ' + motivo + ' · Por: ' + autor,
        autor: autor,
      };
      CRM_API.addItem('Atividades', atividade)
        .then(function(created) {
          if (created && created.id) atividade._spId = String(created.id);
          if (window.CRM_DATA && Array.isArray(CRM_DATA.atividades)) CRM_DATA.atividades.unshift(atividade);
        })
        .catch(function(e){ console.warn('[CRM] atv reativacao:', e.message); });
    }
    setShowReativarModal(false);
    if (typeof window.CRM_TOAST === 'function') window.CRM_TOAST('Cliente reativado.', 'ok');
    onSave && onSave(Object.assign({}, inicial, patch));
    onClose && onClose();
  }

  function abrirReativarModal() {
    setReativarMotivo('');
    setReativarCheck(false);
    setShowReativarModal(true);
  }

  return (
    <Modal open={open} onClose={onClose}
      title={editing ? "Editar cliente" : "Novo cliente"}
      subtitle={editing ? inicial.razao : "Cadastrar na base de clientes"}
      wide>
      <div style={{ display: "flex", flexDirection: "column", gap: 14, marginTop: 16 }}>
        {erro && (
          <div style={{ padding: "10px 14px", background: "var(--danger-050, #fef2f2)", border: "1px solid var(--danger)", borderRadius: "var(--r-md)", font: "400 12px/1.4 var(--ff-body)", color: "var(--danger)" }}>
            {erro}
          </div>
        )}

        {/* Toggle Pessoa Jurídica / Pessoa Física */}
        <div style={{ display: 'flex', gap: 0, background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', padding: 3, alignSelf: 'flex-start' }}>
          {[
            { v: 'PJ', l: 'Pessoa Jurídica', icon: 'building-2' },
            { v: 'PF', l: 'Pessoa Física',   icon: 'user' }
          ].map(opt => {
            const ativo = tipoCliente === opt.v;
            return (
              <button
                key={opt.v}
                type="button"
                onClick={() => setTipoCliente(opt.v)}
                style={{
                  display: 'inline-flex', alignItems: 'center', gap: 6,
                  padding: '6px 14px',
                  border: 'none',
                  background: ativo ? 'var(--surface)' : 'transparent',
                  color: ativo ? 'var(--tx)' : 'var(--tx-3)',
                  font: '500 12px/1 var(--ff-body)',
                  borderRadius: 'var(--r-sm)',
                  cursor: 'pointer',
                  boxShadow: ativo ? '0 1px 3px rgba(0,0,0,.08)' : 'none',
                }}
              >
                <i data-lucide={opt.icon} style={{ width: 13, height: 13 }}></i>
                {opt.l}
              </button>
            );
          })}
        </div>

        {/* Busca de empresa (só PJ) */}
        {tipoCliente === 'PJ' && (
          <div className="f" style={{ position: 'relative' }}>
            <label>Buscar empresa <span style={{ font:"400 10px/1 var(--ff-body)", color:"var(--tx-3)", marginLeft: 4 }}>(Receita Federal)</span></label>
            <div style={{ display: 'flex', gap: 8, alignItems: 'stretch' }}>
              <div style={{ position: 'relative', flex: 1 }}>
                <i data-lucide="search" style={{ position: 'absolute', left: 10, top: '50%', transform: 'translateY(-50%)', width: 14, height: 14, color: 'var(--tx-3)', pointerEvents: 'none' }}></i>
                <input
                  className="inp"
                  value={buscaTermo}
                  onChange={e => { setBuscaTermo(e.target.value); if (autoFilled) setAutoFilled(false); }}
                  onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); buscarEmpresa(); } }}
                  placeholder="Buscar por CNPJ ou razão social..."
                  style={{ paddingLeft: 30 }}
                />
              </div>
              <button
                type="button"
                className="btn btn-secondary"
                onClick={buscarEmpresa}
                disabled={buscaLoading || !buscaTermo.trim()}
                style={{ gap: 6, whiteSpace: 'nowrap', opacity: (buscaLoading || !buscaTermo.trim()) ? 0.5 : 1 }}
              >
                {buscaLoading
                  ? <><i data-lucide="loader" style={{ width: 13, height: 13 }}></i> Buscando...</>
                  : <><i data-lucide="search" style={{ width: 13, height: 13 }}></i> Buscar</>}
              </button>
            </div>

            {/* Dropdown de resultados */}
            {buscaAberta && !buscaLoading && (
              <div style={{ position: 'absolute', top: 'calc(100% + 2px)', left: 0, right: 0, background: 'var(--surface)', border: '1px solid var(--border-2)', borderRadius: 'var(--r-md)', boxShadow: '0 4px 14px rgba(0,0,0,.12)', zIndex: 50, maxHeight: 320, overflowY: 'auto' }}>
                {buscaResultados.length === 0 ? (
                  <div style={{ padding: '14px 16px', font: '400 12px/1.4 var(--ff-body)', color: 'var(--tx-3)', textAlign: 'center' }}>
                    Nenhuma empresa encontrada para "<strong style={{ color: 'var(--tx-2)' }}>{buscaTermo}</strong>".
                  </div>
                ) : (
                  buscaResultados.map((emp, i) => (
                    <div
                      key={emp.cnpj}
                      onClick={() => selecionarEmpresa(emp)}
                      style={{ padding: '10px 14px', cursor: 'pointer', borderBottom: i < buscaResultados.length - 1 ? '1px solid var(--border)' : 'none' }}
                      onMouseEnter={e => e.currentTarget.style.background = 'var(--surface-2)'}
                      onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
                    >
                      <div style={{ font: '500 13px/1.2 var(--ff-body)', color: 'var(--tx)' }}>{emp.razao_social}</div>
                      <div style={{ font: '400 11px/1.4 var(--ff-body)', color: 'var(--tx-2)', marginTop: 3 }}>
                        {emp.nome_fantasia ? <span>{emp.nome_fantasia} · </span> : null}
                        <span>{emp.municipio_nome}/{emp.uf}</span>
                      </div>
                      <div style={{ font: '400 10px/1 var(--ff-mono)', color: 'var(--tx-3)', marginTop: 3 }}>{emp.cnpj_formatado}</div>
                    </div>
                  ))
                )}
              </div>
            )}

            {/* Badge auto-fill */}
            {autoFilled && (
              <div style={{ marginTop: 6, display: 'inline-flex', alignItems: 'center', gap: 6, padding: '4px 10px', background: 'var(--ok-050, #ecfdf5)', border: '1px solid var(--ok)', borderRadius: 'var(--r-sm)', font: '500 11px/1 var(--ff-body)', color: 'var(--ok)' }}>
                <i data-lucide="check-circle-2" style={{ width: 12, height: 12 }}></i>
                Dados preenchidos via Receita Federal
              </div>
            )}
          </div>
        )}

        {/* Razão Social (PJ) / Nome completo (PF) */}
        <div className="f">
          <label>{tipoCliente === 'PJ' ? 'Razão social' : 'Nome completo'} <span style={{color:"var(--danger)"}}>*</span></label>
          <input className="inp" value={razao} onChange={e => setRazao(e.target.value)}
            placeholder={tipoCliente === 'PJ' ? 'Nome completo da empresa' : 'Nome completo da pessoa'} autoFocus />
        </div>

        {/* CNPJ/CPF + Segmento */}
        <div className="form-grid-2">
          <div className="f">
            {tipoCliente === 'PJ' ? (
              <>
                <label>CNPJ</label>
                <input className="inp" value={cnpj}
                  onChange={e => { setCnpj(maskCNPJ(e.target.value)); setCnpjDup(null); }}
                  onBlur={e => checkCnpjDup(e.target.value)}
                  placeholder="00.000.000/0001-00" inputMode="numeric" />
              </>
            ) : (
              <>
                <label>CPF</label>
                <input className="inp" value={cpf}
                  onChange={e => setCpf(maskCPF(e.target.value))}
                  placeholder="000.000.000-00" inputMode="numeric" />
              </>
            )}
          </div>
          <div className="f">
            <label>Segmento</label>
            <select ref={segmentoRef} className="inp select" value={segmento} onChange={e => setSegmento(e.target.value)}>
              <option value="">— Selecione —</option>
              {SEGMENTOS_CLI.map(s => <option key={s} value={s}>{s}</option>)}
            </select>
          </div>
          <div className="f">
            <label>Classe</label>
            <select className="inp select" value={classe} onChange={e => setClasse(e.target.value)}>
              <option value="">— Selecione —</option>
              {['Grande conta','A','B','C','D'].map(c => <option key={c} value={c}>{c}</option>)}
            </select>
          </div>
        </div>

        {cnpjDup && (
          <div style={{
            background:'#fdecea', border:'1px solid #e74c3c', borderRadius:8,
            padding:'10px 14px', fontSize:13, color:'#5c1010', display:'flex',
            alignItems:'center', gap:10, marginTop:-4
          }}>
            <i data-lucide="alert-circle" style={{width:15,height:15,flexShrink:0}}></i>
            <span>
              CNPJ já cadastrado: <strong>{cnpjDup.razao}</strong>
              {cnpjDup.cidade ? ` — ${cnpjDup.cidade}/${cnpjDup.uf}` : ''}.{' '}
              Feche e abra o cliente existente para editar.
            </span>
          </div>
        )}

        {/* Atividade / Profissão (só PF) */}
        {tipoCliente === 'PF' && (
          <div className="f">
            <label>Atividade / Profissão</label>
            <input className="inp" value={atividade}
              onChange={e => setAtividade(e.target.value)}
              placeholder="Ex: Caminhoneiro autônomo, produtor rural, engenheiro..." />
          </div>
        )}

        {/* IE */}
        <div className="f">
          <label>Inscrição Estadual <span style={{font:"400 10px/1 var(--ff-body)",color:"var(--tx-3)",marginLeft:4}}>(opcional)</span></label>
          <input className="inp" value={ie}
            onChange={e => setIe(maskIE(e.target.value))}
            placeholder="ex: 111.111.111.111" style={{ maxWidth: 260 }} />
        </div>

        {/* Endereço */}
        <div className="f">
          <label>Endereço completo</label>
          <input className="inp" value={endereco} onChange={e => setEndereco(e.target.value)}
            placeholder="Rua das Acácias, 450 — Bairro Industrial" />
        </div>

        {/* CEP + Cidade + UF */}
        <div style={{ display: "grid", gridTemplateColumns: "130px 1fr 58px", gap: 10 }}>
          <div className="f">
            <label>CEP</label>
            <input className="inp" value={cep}
              onChange={e => setCep(maskCEP(e.target.value))}
              placeholder="00000-000" inputMode="numeric" />
          </div>
          <div className="f">
            <label>Cidade</label>
            <input className="inp" value={cidade} onChange={e => setCidade(e.target.value)}
              placeholder="São Paulo" />
          </div>
          <div className="f">
            <label>UF</label>
            <input className="inp" value={uf} onChange={e => setUf(e.target.value)}
              placeholder="SP" maxLength={2} style={{ textTransform: "uppercase" }} />
          </div>
        </div>

        {/* Contato + Telefone */}
        <div className="form-grid-2">
          <div className="f">
            <label>Nome do contato</label>
            <input className="inp" value={contato} onChange={e => setContato(e.target.value)}
              placeholder="Eng. João Silva" />
          </div>
          <div className="f">
            <label>Telefone</label>
            <input className="inp" value={fone}
              onChange={e => setFone(maskPhone(e.target.value))}
              placeholder="(11) 99999-0000" inputMode="numeric" />
          </div>
        </div>

        {/* E-mail */}
        <div className="f">
          <label>E-mail</label>
          <input className="inp" type="email" value={email} onChange={e => setEmail(e.target.value)}
            placeholder="compras@empresa.com.br" />
        </div>

        {/* Volume + Status */}
        <div className="form-grid-2">
          <div className="f">
            <label>Volume anual estimado (R$)</label>
            <input className="inp" type="number" value={volume} onChange={e => setVolume(e.target.value)}
              placeholder="0" min={0} step={100000} />
          </div>
          <div className="f">
            <label>Status</label>
            <select className="inp select" value={status} onChange={e => setStatus(e.target.value)}>
              {STATUS_CLI.map(s => <option key={s} value={s}>{s}</option>)}
            </select>
          </div>
        </div>

        {/* Vendedor responsável (carteira) */}
        {(function() {
          // Filtra só grupos que fazem sentido como responsáveis de carteira.
          // Comercial (vendedores), gerencia (key accounts), BDR (leads pré-pipeline).
          // Pos_vendas e administrativo ficam de fora.
          // Fonte: CRM_DATA.usuarios — mesmo dado usado pelas telas Propostas/Pedidos,
          // carregado via loadList() na inicialização do CRM (sem o cache de 1h separado
          // do WHITELIST_DETAILS). id mapeado para email — consistente com vendedor_id no SP.
          var vendedores = (CRM_DATA.usuarios || [])
            .filter(function(u) {
              var g = (u.grupo || '').toLowerCase();
              return g === 'comercial' || g === 'gerencia' || g === 'bdr';
            })
            .map(function(u) {
              return { id: u.email || u.id, nome: u.nome, grupo: u.grupo, cargo: u.cargo, cor: u.avatar_color };
            });
          var userEmail = (window.CRM_USER && window.CRM_USER.email) || '';
          // Permissão: gerência pode tudo; vendedor pode trocar se for o atual ou se vazio
          var canEdit = isGerente || !vendedorId || (vendedorId.toLowerCase() === userEmail);
          var vendedorAtual = vendedores.find(function(v) { return v.id === vendedorId; });
          // Edge case: vendedor antigo (ex.: pos_vendas/administrativo já atribuído antes do filtro
          // ou vendedor que mudou de grupo). Mostra ele mesmo assim — só não aparece pra escolher.
          var vendedorAtualFora = !vendedorAtual && vendedorId
            ? { id: vendedorId, nome: vendedorId, cor: 'var(--tx-3)', grupo: '(fora do escopo atual)' }
            : null;
          var displayVendedor = vendedorAtual || vendedorAtualFora;
          return (
            <div className="f">
              <label>
                Vendedor responsável
                <span className="hint" style={{ marginLeft: 6 }}>(carteira)</span>
                {!canEdit && (
                  <span className="hint" style={{ marginLeft: 8, color: 'var(--tx-3)' }}>
                    · apenas gerência ou o próprio vendedor pode alterar
                  </span>
                )}
                {vendedorAtualFora && canEdit && (
                  <span className="hint" style={{ marginLeft: 8, color: 'var(--warn)' }}>
                    · vendedor atual fora dos grupos comerciais
                  </span>
                )}
                {regiaoSugerida && regiaoSugerida.vendedor_email && (
                  <span className="hint" style={{
                    marginLeft: 8,
                    color: vendedorId && vendedorId.toLowerCase() === regiaoSugerida.vendedor_email.toLowerCase()
                      ? 'var(--ok)' : 'var(--warn)',
                    display: 'inline-flex', alignItems: 'center', gap: 4
                  }}>
                    {vendedorId && vendedorId.toLowerCase() === regiaoSugerida.vendedor_email.toLowerCase()
                      ? <><i data-lucide="map-pin" style={{ width: 11, height: 11 }}></i>região {regiaoSugerida.regiao_nome}</>
                      : <><i data-lucide="alert-triangle" style={{ width: 11, height: 11 }}></i>fora da região oficial ({regiaoSugerida.regiao_nome})</>
                    }
                  </span>
                )}
              </label>
              <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
                {displayVendedor && (
                  <div style={{
                    width: 28, height: 28, borderRadius: '50%',
                    background: displayVendedor.cor || 'var(--grn)',
                    color: '#fff', display: 'grid', placeItems: 'center',
                    font: '600 11px/1 var(--ff-body)', flexShrink: 0,
                  }} title={displayVendedor.nome}>
                    {(displayVendedor.nome || '?').split(' ').map(function(p){return p[0]||'';}).slice(0,2).join('').toUpperCase()}
                  </div>
                )}
                <select className="inp select" value={vendedorId}
                  onChange={e => setVendedorId(e.target.value)}
                  disabled={!canEdit}
                  style={{ flex: 1, opacity: canEdit ? 1 : 0.7 }}>
                  <option value="">— Sem vendedor atribuído —</option>
                  {vendedorAtualFora && (
                    <option value={vendedorId} disabled>{vendedorId} (fora do escopo)</option>
                  )}
                  {vendedores.map(function(v) {
                    return <option key={v.id} value={v.id}>{v.nome}{v.grupo ? ' · ' + v.grupo : ''}</option>;
                  })}
                </select>
              </div>
            </div>
          );
        })()}

        <div style={{ display: "flex", gap: 8, justifyContent: "space-between", paddingTop: 4, alignItems: "center" }}>
          {/* Inativar (só se editando + cliente ainda ativo) */}
          {editing && status !== 'inativo' && (
            <button className="btn btn-ghost" onClick={abrirInativarModal} disabled={saving}
              style={{ color: 'var(--warn)', borderColor: 'var(--warn)', gap: 6, fontSize: 12 }}
              title="Inativar cliente">
              <i data-lucide="user-x" style={{ width: 14, height: 14 }}></i> Inativar
            </button>
          )}
          {editing && status === 'inativo' && (
            <button className="btn btn-ghost" onClick={abrirReativarModal} disabled={saving}
              style={{ color: 'var(--ok)', borderColor: 'var(--ok)', gap: 6, fontSize: 12 }}
              title="Reativar cliente">
              <i data-lucide="user-check" style={{ width: 14, height: 14 }}></i> Reativar cliente
            </button>
          )}
          {!editing && <div></div>}
          <div style={{ display: 'flex', gap: 8 }}>
            <button className="btn btn-secondary" onClick={onClose} disabled={saving}>Cancelar</button>
            <button className="btn btn-primary" onClick={handleSave} disabled={saving}
              style={{ opacity: canSave ? 1 : 0.55, cursor: canSave ? 'pointer' : 'not-allowed' }}>
              {saving
                ? <><i data-lucide="loader" style={{width:13,height:13}}></i> Salvando...</>
                : <><i data-lucide={editing ? "check" : "plus"} style={{width:13,height:13}}></i> {editing ? "Salvar alterações" : "Cadastrar cliente"}</>
              }
            </button>
          </div>
        </div>
      </div>

      {/* Modal Inativar / Excluir Cliente */}
      {showInativarModal && (function() {
        var motivoOk = inativarMotivo.trim().length >= 10;
        var podeInativar = motivoOk && inativarCheck;
        var podeExcluir  = inativarCheck && !temOportunidadesAtivas;
        var isExcluir = inativarModoDelete;
        return (
          <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,.6)', zIndex:10100, display:'flex', alignItems:'center', justifyContent:'center', padding:16 }}
            onClick={e => { if (e.target === e.currentTarget) setShowInativarModal(false); }}>
            <div style={{ background:'var(--surface)', borderRadius:'var(--r-lg)', padding:'28px 32px', width:'100%', maxWidth:500, boxShadow:'var(--shadow-3)' }}>
              {/* Header */}
              <div style={{ display:'flex', alignItems:'center', gap:10, marginBottom:16 }}>
                <div style={{ width:36, height:36, borderRadius:'50%', background: isExcluir ? '#fef2f2' : '#fff7ed', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0 }}>
                  <i data-lucide={isExcluir ? 'trash-2' : 'user-x'} style={{ width:18, height:18, color: isExcluir ? 'var(--danger)' : '#c2410c' }}></i>
                </div>
                <div>
                  <div style={{ font:'600 15px/1.2 var(--ff-body)', color:'var(--tx)' }}>
                    {isExcluir ? 'Excluir cliente definitivamente' : 'Inativar cliente'}
                  </div>
                  <div style={{ font:'400 11px/1.4 var(--ff-mono)', color:'var(--tx-3)', marginTop:3 }}>
                    {inicial && inicial.razao}
                  </div>
                </div>
              </div>

              {/* Bloqueio se há oportunidades ativas (só relevante pra excluir) */}
              {isExcluir && temOportunidadesAtivas && (
                <div style={{ background:'#fef2f2', border:'1px solid var(--danger)', borderRadius:'var(--r-sm)', padding:'12px 14px', marginBottom:14, display:'flex', gap:10, alignItems:'flex-start' }}>
                  <i data-lucide="ban" style={{ width:16, height:16, color:'var(--danger)', flexShrink:0, marginTop:1 }}></i>
                  <div style={{ font:'400 12px/1.55 var(--ff-body)', color:'#7f1d1d' }}>
                    <strong>Exclusão bloqueada:</strong> este cliente tem <strong>{oportunidadesAtivas.length}</strong> oportunidade(s) ativa(s) vinculada(s).
                    Cancele-as primeiro pra poder excluir o cliente.
                  </div>
                </div>
              )}

              {/* Aviso info quando inativar */}
              {!isExcluir && (
                <div style={{ background:'var(--surface-2)', border:'1px solid var(--border)', borderRadius:'var(--r-md)', padding:'12px 14px', marginBottom:14 }}>
                  <div style={{ font:'400 12px/1.55 var(--ff-body)', color:'var(--tx-2)' }}>
                    Inativar mantém o cliente no SharePoint como histórico, mas ele sai do filtro padrão "Ativos".
                    Oportunidades e propostas vinculadas <strong>não são afetadas</strong>.
                  </div>
                </div>
              )}

              {/* Motivo — só pra inativar */}
              {!isExcluir && (
                <div style={{ marginBottom:14 }}>
                  <label style={{ font:'600 10px/1 var(--ff-body)', textTransform:'uppercase', letterSpacing:'.07em', color:'var(--tx-3)', display:'block', marginBottom:6 }}>
                    Motivo <span style={{ color:'var(--danger)' }}>*</span>
                    <span style={{ font:'400 9px/1 var(--ff-body)', color:'var(--tx-3)', textTransform:'none', marginLeft:6 }}>(mín. 10 caracteres)</span>
                  </label>
                  <textarea rows={3} value={inativarMotivo} onChange={e => setInativarMotivo(e.target.value)}
                    placeholder="Ex: cliente parou de operar, mudou de razão social, sem volume de negócio…"
                    style={{ width:'100%', padding:'9px 11px', border:'1px solid var(--border-2)', borderRadius:'var(--r-sm)', background:'var(--surface)', fontFamily:'var(--ff-body)', fontSize:13, color:'var(--tx)', outline:'none', boxSizing:'border-box', resize:'vertical', lineHeight:1.5 }} />
                  <div style={{ font:'400 10px/1.2 var(--ff-body)', color: motivoOk ? 'var(--tx-3)' : 'var(--danger)', marginTop:4 }}>
                    {inativarMotivo.trim().length}/10 caracteres
                  </div>
                </div>
              )}

              {/* Checkbox confirmação */}
              <label style={{ display:'flex', alignItems:'flex-start', gap:10, cursor:'pointer', marginBottom:14, padding:'12px 14px', background: isExcluir ? 'var(--danger-050)' : 'var(--surface-2)', border:'1px solid ' + (isExcluir ? 'var(--danger-100)' : 'var(--border)'), borderRadius:'var(--r-sm)' }}>
                <input type="checkbox" checked={inativarCheck} onChange={e => setInativarCheck(e.target.checked)}
                  style={{ marginTop:2, width:15, height:15, accentColor: isExcluir ? 'var(--danger)' : 'var(--warn)', flexShrink:0, cursor:'pointer' }} />
                <span style={{ font:'500 12px/1.5 var(--ff-body)', color: isExcluir ? 'var(--danger)' : 'var(--tx-2)' }}>
                  {isExcluir
                    ? <>Confirmo que desejo excluir permanentemente o cliente <strong>{inicial && inicial.razao}</strong>.</>
                    : <>Confirmo que o cliente <strong>{inicial && inicial.razao}</strong> será inativado.</>}
                </span>
              </label>

              {/* Link gerente */}
              {isGerente && !isExcluir && (
                <div style={{ paddingTop:10, borderTop:'1px dashed var(--border)', textAlign:'right' }}>
                  <button onClick={() => { setInativarModoDelete(true); setInativarCheck(false); }}
                    style={{ background:'none', border:'none', cursor:'pointer', font:'400 11px/1 var(--ff-body)', color:'var(--danger)', textDecoration:'underline', padding:0 }}>
                    Ou excluir definitivamente (apenas gerente)
                  </button>
                </div>
              )}
              {isGerente && isExcluir && (
                <div style={{ paddingTop:10, borderTop:'1px dashed var(--border)', textAlign:'right' }}>
                  <button onClick={() => { setInativarModoDelete(false); setInativarCheck(false); }}
                    style={{ background:'none', border:'none', cursor:'pointer', font:'400 11px/1 var(--ff-body)', color:'var(--tx-2)', textDecoration:'underline', padding:0 }}>
                    ← Voltar para inativar
                  </button>
                </div>
              )}

              <div style={{ display:'flex', gap:10, justifyContent:'flex-end', marginTop:18 }}>
                <button className="btn btn-ghost" onClick={() => setShowInativarModal(false)}>Voltar</button>
                <button className="btn"
                  disabled={isExcluir ? !podeExcluir : !podeInativar}
                  onClick={() => isExcluir ? handleExcluirCliente() : handleInativarCliente(inativarMotivo.trim())}
                  style={{
                    background: isExcluir
                      ? (podeExcluir ? 'var(--danger)' : 'var(--border)')
                      : (podeInativar ? 'var(--warn)' : 'var(--border)'),
                    color: ((isExcluir && podeExcluir) || (!isExcluir && podeInativar)) ? '#fff' : 'var(--tx-3)',
                    border:'none',
                    cursor: ((isExcluir && podeExcluir) || (!isExcluir && podeInativar)) ? 'pointer' : 'not-allowed',
                    gap:6,
                  }}>
                  {isExcluir
                    ? <><i data-lucide="trash-2" style={{ width:14, height:14 }}></i> Excluir definitivamente</>
                    : <><i data-lucide="user-x" style={{ width:14, height:14 }}></i> Inativar cliente</>}
                </button>
              </div>
            </div>
          </div>
        );
      })()}

      {/* Modal Reativar Cliente */}
      {showReativarModal && (
        <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,.6)', zIndex:10100, display:'flex', alignItems:'center', justifyContent:'center', padding:16 }}
          onClick={e => { if (e.target === e.currentTarget) setShowReativarModal(false); }}>
          <div style={{ background:'var(--surface)', borderRadius:'var(--r-lg)', padding:'28px 32px', width:'100%', maxWidth:480, boxShadow:'var(--shadow-3)' }}>
            <div style={{ display:'flex', alignItems:'center', gap:10, marginBottom:16 }}>
              <div style={{ width:36, height:36, borderRadius:'50%', background:'var(--ok-050, #ecfdf5)', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0 }}>
                <i data-lucide="user-check" style={{ width:18, height:18, color:'var(--ok)' }}></i>
              </div>
              <div>
                <div style={{ font:'600 15px/1.2 var(--ff-body)', color:'var(--tx)' }}>Reativar cliente</div>
                <div style={{ font:'400 11px/1.4 var(--ff-mono)', color:'var(--tx-3)', marginTop:3 }}>{inicial && inicial.razao}</div>
              </div>
            </div>

            <div style={{ background:'var(--surface-2)', border:'1px solid var(--border)', borderRadius:'var(--r-md)', padding:'12px 14px', marginBottom:14 }}>
              <div style={{ font:'400 12px/1.55 var(--ff-body)', color:'var(--tx-2)' }}>
                O cliente voltará ao status <strong>Ativo</strong> e aparecerá novamente no filtro padrão.
                Histórico de oportunidades e propostas é preservado.
              </div>
            </div>

            <div style={{ marginBottom:14 }}>
              <label style={{ font:'600 10px/1 var(--ff-body)', textTransform:'uppercase', letterSpacing:'.07em', color:'var(--tx-3)', display:'block', marginBottom:6 }}>
                Motivo <span style={{ font:'400 9px/1 var(--ff-body)', color:'var(--tx-3)', textTransform:'none', marginLeft:6 }}>(opcional)</span>
              </label>
              <textarea rows={2} value={reativarMotivo} onChange={e => setReativarMotivo(e.target.value)}
                placeholder="Ex: cliente retomou operação, voltou a comprar, ajuste após inativação por engano…"
                style={{ width:'100%', padding:'9px 11px', border:'1px solid var(--border-2)', borderRadius:'var(--r-sm)', background:'var(--surface)', fontFamily:'var(--ff-body)', fontSize:13, color:'var(--tx)', outline:'none', boxSizing:'border-box', resize:'vertical', lineHeight:1.5 }} />
            </div>

            <label style={{ display:'flex', alignItems:'flex-start', gap:10, cursor:'pointer', marginBottom:18, padding:'12px 14px', background:'var(--surface-2)', border:'1px solid var(--border)', borderRadius:'var(--r-sm)' }}>
              <input type="checkbox" checked={reativarCheck} onChange={e => setReativarCheck(e.target.checked)}
                style={{ marginTop:2, width:15, height:15, accentColor:'var(--ok)', flexShrink:0, cursor:'pointer' }} />
              <span style={{ font:'500 12px/1.5 var(--ff-body)', color:'var(--tx-2)' }}>
                Confirmo que o cliente <strong>{inicial && inicial.razao}</strong> será reativado.
              </span>
            </label>

            <div style={{ display:'flex', gap:10, justifyContent:'flex-end' }}>
              <button className="btn btn-ghost" onClick={() => setShowReativarModal(false)}>Voltar</button>
              <button className="btn"
                disabled={!reativarCheck}
                onClick={() => handleReativarCliente(reativarMotivo.trim())}
                style={{
                  background: reativarCheck ? 'var(--ok)' : 'var(--border)',
                  color: reativarCheck ? '#fff' : 'var(--tx-3)',
                  border:'none',
                  cursor: reativarCheck ? 'pointer' : 'not-allowed',
                  gap:6,
                }}>
                <i data-lucide="user-check" style={{ width:14, height:14 }}></i> Reativar cliente
              </button>
            </div>
          </div>
        </div>
      )}
    </Modal>
  );
}


// ── DupClienteModal — CNPJ já existe ao tentar cadastrar via Prospecção ──────
function DupClienteModal({ open, dupInfo, onClose, onAbrirCliente, onAplicar }) {
  if (!open || !dupInfo) return null;
  const { cliente, diff } = dupInfo;
  const temDiff = diff && diff.length > 0;
  const [selecionados, setSelecionados] = React.useState(() =>
    (diff || []).reduce(function(acc, d) { acc[d.key] = true; return acc; }, {})
  );
  function toggleField(key) {
    setSelecionados(function(prev) { var n = Object.assign({}, prev); n[key] = !n[key]; return n; });
  }
  async function handleAplicar() {
    var patch = {};
    (diff || []).forEach(function(d) { if (selecionados[d.key]) patch[d.key] = d.novo; });
    if (Object.keys(patch).length === 0) { onClose(); return; }
    await onAplicar(cliente, patch);
    onClose();
  }
  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal-box" style={{maxWidth:500}} onClick={e => e.stopPropagation()}>
        <div className="modal-header">
          <div>
            <div className="modal-title">CNPJ já cadastrado</div>
            <div className="modal-sub">{cliente.razao}{cliente.cidade ? ` — ${cliente.cidade}/${cliente.uf}` : ''}</div>
          </div>
          <button className="modal-close" onClick={onClose}>✕</button>
        </div>
        <div style={{padding:'16px 24px 20px'}}>
          {!temDiff ? (
            <div style={{background:'#e6f2ec',border:'1px solid #a8d4bc',borderRadius:8,padding:'12px 14px',fontSize:13,color:'#003820',marginBottom:16}}>
              Este cliente já está na base com todas as informações preenchidas.
            </div>
          ) : (
            <>
              <div style={{background:'#fff9ef',border:'1px solid #e6a817',borderRadius:8,padding:'12px 14px',fontSize:13,color:'#4a3000',marginBottom:12}}>
                O cliente já existe, mas a base nacional tem dados que faltam no cadastro. Selecione o que deseja aplicar:
              </div>
              <div style={{display:'flex',flexDirection:'column',gap:6,marginBottom:16}}>
                {diff.map(function(d) {
                  var sel = selecionados[d.key];
                  return (
                    <label key={d.key} style={{display:'flex',alignItems:'flex-start',gap:10,padding:'9px 12px',borderRadius:8,background:sel?'#e6f2ec':'#f7f9f8',border:'1px solid '+(sel?'#a8d4bc':'#e3e6e4'),cursor:'pointer',fontSize:13}}>
                      <input type="checkbox" checked={!!sel} onChange={() => toggleField(d.key)} style={{marginTop:2,accentColor:'#007c44'}} />
                      <div>
                        <div style={{fontWeight:600,color:'#1a2b1e'}}>{d.label}</div>
                        <div style={{color:'#6a7f71',fontSize:12,marginTop:2}}>
                          {d.atual ? <span>Atual: <em>{d.atual}</em> → </span> : <span style={{color:'#c47a00'}}>Vazio → </span>}
                          <strong style={{color:'#007c44'}}>{d.novo}</strong>
                        </div>
                      </div>
                    </label>
                  );
                })}
              </div>
            </>
          )}
          <div style={{display:'flex',gap:8,justifyContent:'flex-end'}}>
            <button className="btn btn-secondary" onClick={onClose}>Fechar</button>
            <button className="btn btn-secondary" onClick={() => { onClose(); onAbrirCliente(cliente); }}>
              Abrir cadastro
            </button>
            {temDiff && (
              <button className="btn btn-primary"
                disabled={!Object.values(selecionados).some(Boolean)}
                onClick={handleAplicar}>
                Aplicar selecionados
              </button>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

function Clientes({ onNav, empresaPreFill, abrirOportunidadeAposSalvar, onPreFillConsumido, onOpenNovaOpComCliente }) {
  const [search, setSearch]       = useScreenState("");
  const [statusFilter, setStatusFilter] = useScreenState("ativos"); // ativos | inativos | todos
  const [ufFilter, setUfFilter]   = useScreenState("");
  const [segmentoFilter, setSegmentoFilter] = useScreenState("");
  const [page, setPage]           = useScreenState(1); // paginação — 50 por página, "Carregar mais"
  const [forcarMostrarTodos, setForcarMostrarTodos] = useScreenState(false); // escape hatch
  const [selected, setSelected]   = useScreenState(null);
  const [modalOpen, setModalOpen] = useScreenState(false);
  const [editando, setEditando]   = useScreenState(null);
  const [clientes, setClientes]   = useScreenState(CRM_DATA.clientes);
  // Escopo da carteira — "meus" filtra por vendedor_id do CRM_USER; "todos" mostra a base inteira.
  // Default: comercial começa em "meus", BDR/Admin/Gerente em "todos".
  // K-D9: persiste preferência em sessionStorage para não resetar a cada navegação
  const _isComercial = !!(window.CRM_USER && window.CRM_USER.isComercial);
  const [escopo, setEscopo] = useScreenState(function() {
    try {
      var saved = sessionStorage.getItem('crm_clientes_escopo');
      if (saved === 'meus' || saved === 'todos') return saved;
    } catch(e) {}
    return _isComercial ? 'meus' : 'todos';
  });
  React.useEffect(function() {
    try { sessionStorage.setItem('crm_clientes_escopo', escopo); } catch(e) {}
  }, [escopo]);
  const [abaAtiva, setAbaAtiva]   = useScreenState('geral'); // 'geral' | 'dados_publicos' | 'frota'
  // Quando vindo da Prospecção: abre modal já com empresa pré-preenchida e
  // memoriza se deve disparar fluxo "abrir NovaOp após salvar".
  const [preFillAtivo, setPreFillAtivo] = useScreenState(null);
  const [abrirOpAposSalvar, setAbrirOpAposSalvar] = useScreenState(false);
  const [dupModal, setDupModal] = useScreenState(null);
  const PAGE_SIZE = 50;

  // Quando uma empresa pré-fill chega via props, verifica duplicata antes de abrir modal
  React.useEffect(() => {
    if (!empresaPreFill) return;
    const cnpjDigits = ((empresaPreFill.cnpj_raw || '').replace(/\D/g, '') ||
                        (empresaPreFill.cnpj_formatado || '').replace(/\D/g, ''));
    const found = cnpjDigits.length === 14
      ? (CRM_DATA.clientes || []).find(function(c) {
          return c.cnpj && c.cnpj.replace(/\D/g, '') === cnpjDigits;
        })
      : null;
    if (found) {
      const DIFF_MAP = [
        { key: 'fone',     label: 'Telefone',  pfVal: empresaPreFill.telefone },
        { key: 'email',    label: 'E-mail',    pfVal: empresaPreFill.email },
        { key: 'cep',      label: 'CEP',       pfVal: empresaPreFill.cep },
        { key: 'cidade',   label: 'Cidade',    pfVal: empresaPreFill.municipio_nome },
        { key: 'uf',       label: 'UF',        pfVal: empresaPreFill.uf },
        { key: 'endereco', label: 'Endereço',  pfVal: [
            empresaPreFill.logradouro || '',
            empresaPreFill.numero ? ', ' + empresaPreFill.numero : '',
            empresaPreFill.bairro   ? ' — ' + empresaPreFill.bairro : ''
          ].join('').trim() },
      ];
      const diff = DIFF_MAP.filter(function(d) {
        return d.pfVal && String(d.pfVal).trim() &&
               String(d.pfVal).trim() !== String(found[d.key] || '').trim();
      }).map(function(d) {
        return { key: d.key, label: d.label, atual: found[d.key] || '', novo: String(d.pfVal).trim() };
      });
      setDupModal({ cliente: found, preFill: empresaPreFill, diff });
      if (typeof onPreFillConsumido === 'function') onPreFillConsumido();
    } else {
      setPreFillAtivo(empresaPreFill);
      setAbrirOpAposSalvar(!!abrirOportunidadeAposSalvar);
      setEditando(null);
      setModalOpen(true);
      if (typeof onPreFillConsumido === 'function') onPreFillConsumido();
    }
  }, [empresaPreFill, abrirOportunidadeAposSalvar]);

  // Reset aba ao trocar de cliente (cliente PF não tem aba "Dados Públicos")
  // Ref para tab inicial pendente — lida dentro do useEffect de [selected]
  // para sobrepor o reset para 'geral' quando a Frota (ou outra tela) pede tab específica.
  const _abaInicialRef = React.useRef(null);

  React.useEffect(() => {
    var tab = _abaInicialRef.current;
    _abaInicialRef.current = null;
    setAbaAtiva(tab || 'geral');
  }, [selected]);

  // Permite que outras telas (ex: teia societária, Frota) abram um cliente específico
  // via CustomEvent. Aceita detail.tab opcional para abrir em aba específica.
  React.useEffect(() => {
    function h(e) {
      var id  = e && e.detail && e.detail.id;
      var tab = e && e.detail && e.detail.tab;
      if (id) {
        if (tab) _abaInicialRef.current = tab;
        setSelected(id);
      }
    }
    window.addEventListener('crm_open_cliente', h);
    return function() { window.removeEventListener('crm_open_cliente', h); };
  }, []);

  // Teia societária: abre ClienteModal em modo novo com dados RF pré-preenchidos
  React.useEffect(() => {
    function h(e) {
      if (e && e.detail) {
        setPreFillAtivo(e.detail);
        setEditando(null);
        setAbrirOpAposSalvar(false);
        setModalOpen(true);
      }
    }
    window.addEventListener('crm_new_cliente_prefill', h);
    return function() { window.removeEventListener('crm_new_cliente_prefill', h); };
  }, []);

  // Re-roda createIcons após cada render para evitar crash de reconciliação com Lucide
  React.useEffect(() => {
    // Try/catch defensivo: lucide manipula DOM direto, às vezes conflita com reconciliação React
    // após mudanças de state (filtros, lista). Sem isso, NotFoundError trava a tela inteira.
    try { if (window.lucide) window.lucide.createIcons({ attrs: { "stroke-width": 1.75 } }); }
    catch(e) { /* ícones podem ficar como <i>, mas a tela não trava */ }
  });

  // ─── Filtragem ───────────────────────────────────────────────
  // Pré-filtro por escopo: "meus" restringe aos clientes do vendedor logado
  // (vendedor_id pode ser U-id OU email — legado). "todos" mostra a base inteira.
  const _meuId    = (window.CRM_USER && window.CRM_USER.userId) || null;
  const _meuEmail = (window.CRM_USER && window.CRM_USER.email)  || '';
  const filtered = clientes
    .filter(c => {
      if (escopo !== 'meus') return true;
      var vid = c.vendedor_id || c.proprietario || '';
      return vid === _meuId || (vid && vid.toLowerCase && vid.toLowerCase() === _meuEmail);
    })
    .filter(c => {
      if (statusFilter === 'ativos')   return c.status !== 'inativo';
      if (statusFilter === 'inativos') return c.status === 'inativo';
      return true; // todos
    })
    .filter(c => !ufFilter || (c.uf || '').toUpperCase().slice(0,2) === ufFilter)
    .filter(c => !segmentoFilter || (c.segmento || '') === segmentoFilter)
    .filter(c => {
      if (!search.trim()) return true;
      // Normaliza: minúsculas + remove diacríticos (ex: "Máquinas" → "maquinas")
      function norm(s) {
        return String(s || '').normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase();
      }
      const q = norm(search).trim();
      // CNPJ/CPF: busca substring exata (não tokeniza)
      if ((c.cnpj || '').includes(q) || (c.cpf || '').includes(q)) return true;
      // Texto: tokeniza por espaços, ignora tokens com 1 char ("A", "E", "O"...)
      // Todos os tokens válidos precisam aparecer em algum campo (AND)
      var tokens = q.split(/\s+/).filter(function(t){ return t.length >= 2; });
      if (!tokens.length) tokens = [q]; // fallback: usa termo completo
      var hay = [norm(c.razao), norm(c.cidade), norm(c.segmento), norm(c.contato)].join(' ');
      return tokens.every(function(t){ return hay.includes(t); });
    });
  const nInativos = clientes.filter(c => c.status === 'inativo').length;

  // ─── Filtros ativos? Determina se mostra lista ou estado inicial ───
  // Search ou UF ou segmento ou status≠ativos → considera filtrado.
  // Status "ativos" sozinho é o default, não conta como filtro.
  const temFiltroAtivo = !!(
    search.trim() ||
    ufFilter ||
    segmentoFilter ||
    statusFilter !== 'ativos' ||
    forcarMostrarTodos
  );

  // ─── Reseta paginação quando filtros mudam ────────────────────
  React.useEffect(() => { setPage(1); }, [search, ufFilter, segmentoFilter, statusFilter, forcarMostrarTodos, escopo]);

  // ─── KPIs derivados (sempre da base toda, ignorando filtros) ──
  const totalClientes = clientes.length;
  const totalAtivos   = clientes.filter(c => c.status !== 'inativo').length;
  const totalComVendedor = clientes.filter(c => c.vendedor_id || c.proprietario).length;
  const totalSemVendedor = totalClientes - totalComVendedor;

  // ─── Opções de UF e Segmento derivadas da base ────────────────
  const ufOptions = React.useMemo(() => {
    const set = {};
    clientes.forEach(c => {
      const u = (c.uf || '').toUpperCase().slice(0,2);
      if (u) set[u] = (set[u] || 0) + 1;
    });
    return Object.keys(set).sort().map(k => ({ uf: k, n: set[k] }));
  }, [clientes]);

  const segmentoOptions = React.useMemo(() => {
    const set = {};
    clientes.forEach(c => { if (c.segmento) set[c.segmento] = (set[c.segmento] || 0) + 1; });
    return Object.keys(set).sort().map(k => ({ segmento: k, n: set[k] }));
  }, [clientes]);

  // ─── Paginação ──────────────────────────────────────────────
  const visibleClientes = filtered.slice(0, page * PAGE_SIZE);
  const temMaisPaginas = visibleClientes.length < filtered.length;

  const cli = selected ? clientes.find(c => c.id === selected) : null;
  const cliOps = cli ? CRM_DATA.oportunidades.filter(o => o.cliente_id === cli.id) : [];

  function limparFiltros() {
    setSearch("");
    setUfFilter("");
    setSegmentoFilter("");
    setStatusFilter("ativos");
    setForcarMostrarTodos(false);
  }

  function abrirNovo() {
    setEditando(null); setPreFillAtivo(null); setAbrirOpAposSalvar(false);
    setModalOpen(true);
  }
  function abrirEditar(c) {
    setEditando(c); setPreFillAtivo(null); setAbrirOpAposSalvar(false);
    setModalOpen(true);
  }
  function onSave(obj) {
    if (obj && obj.id) setSelected(obj.id);
    setClientes([...CRM_DATA.clientes]); // cópia fresca → re-render sem forceUpdate
    // Fluxo encadeado: vindo da Prospecção com flag "abrir NovaOp após salvar"
    if (obj && obj.id && abrirOpAposSalvar && typeof onOpenNovaOpComCliente === 'function') {
      onOpenNovaOpComCliente(obj.id);
    }
    setPreFillAtivo(null);
    setAbrirOpAposSalvar(false);
  }

  return (
    <div data-screen-label="03 Clientes">
      {/* ─── KPIs no topo (sempre visíveis) ───────────────────────── */}
      <div className="kpi-grid" style={{ gridTemplateColumns: 'repeat(4, 1fr)', marginBottom: 16 }}>
        <div className="kpi-card">
          <div className="kpi-card__eyebrow">Total de clientes</div>
          <div className="kpi-card__v kpi-card__v--mono">{totalClientes.toLocaleString('pt-BR')}</div>
        </div>
        <div className="kpi-card">
          <div className="kpi-card__eyebrow">Ativos</div>
          <div className="kpi-card__v kpi-card__v--mono">{totalAtivos.toLocaleString('pt-BR')}</div>
          <div className="kpi-card__delta kpi-card__sub">
            {nInativos ? nInativos.toLocaleString('pt-BR') + ' inativos' : 'nenhum inativo'}
          </div>
        </div>
        <div className="kpi-card">
          <div className="kpi-card__eyebrow">Com vendedor</div>
          <div className="kpi-card__v kpi-card__v--mono">{totalComVendedor.toLocaleString('pt-BR')}</div>
          <div className="kpi-card__delta kpi-card__sub">
            {totalClientes ? Math.round((totalComVendedor / totalClientes) * 100) + '% da base' : '—'}
          </div>
        </div>
        <div className="kpi-card">
          <div className="kpi-card__eyebrow">Sem vendedor</div>
          <div className="kpi-card__v kpi-card__v--mono" style={{ color: totalSemVendedor > 0 ? 'var(--warn)' : 'var(--tx)' }}>
            {totalSemVendedor.toLocaleString('pt-BR')}
          </div>
          <div className="kpi-card__delta kpi-card__sub">
            {totalSemVendedor > 0 ? 'precisam atribuição' : 'todos atribuídos'}
          </div>
        </div>
      </div>

      <div className="two-col" style={{ gap: 16, alignItems: "start" }}>
        {/* Lista */}
        <Panel
          title="Clientes"
          meta={
            temFiltroAtivo
              ? `${visibleClientes.length} de ${filtered.length.toLocaleString('pt-BR')} ${filtered.length === 1 ? 'resultado' : 'resultados'}`
              : `${totalClientes.toLocaleString('pt-BR')} cadastrados`
          }
          actions={
            <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: 'wrap' }}>
              {/* Toggle escopo de carteira — sempre visível, default "meus" para comercial */}
              <div className="tabs" style={{ marginBottom: 0 }}>
                {[
                  { v: 'meus',  l: 'Meus clientes' },
                  { v: 'todos', l: 'Toda a base' },
                ].map(f => (
                  <button key={f.v} className={escopo === f.v ? 'is-on' : ''} onClick={() => setEscopo(f.v)}>
                    {f.l}
                  </button>
                ))}
              </div>
              <div className="tabs" style={{ marginBottom: 0 }}>
                {[
                  { v: 'ativos', l: 'Ativos' },
                  { v: 'inativos', l: 'Inativos' + (nInativos ? ' (' + nInativos + ')' : '') },
                  { v: 'todos',  l: 'Todos' },
                ].map(f => (
                  <button key={f.v} className={statusFilter === f.v ? 'is-on' : ''} onClick={() => setStatusFilter(f.v)}>
                    {f.l}
                  </button>
                ))}
              </div>
              <select className="inp select" value={ufFilter} onChange={e => setUfFilter(e.target.value)}
                style={{ minWidth: 90, padding: '7px 28px 7px 10px', fontSize: 12 }}>
                <option value="">UF</option>
                {ufOptions.map(o => <option key={o.uf} value={o.uf}>{o.uf} ({o.n})</option>)}
              </select>
              <select className="inp select" value={segmentoFilter} onChange={e => setSegmentoFilter(e.target.value)}
                style={{ minWidth: 150, padding: '7px 28px 7px 10px', fontSize: 12 }}>
                <option value="">Segmento</option>
                {segmentoOptions.map(o => <option key={o.segmento} value={o.segmento}>{o.segmento} ({o.n})</option>)}
              </select>
              <div className="tb__search" style={{ minWidth: 200 }}>
                <i data-lucide="search"></i>
                <input placeholder="Razão, CNPJ, cidade..." value={search} onChange={e => setSearch(e.target.value)} />
              </div>
              <button className="btn btn-primary" onClick={abrirNovo}>
                <i data-lucide="plus"></i> Novo
              </button>
            </div>
          }
          noPad
        >
          {clientes.length === 0 ? (
            // Base vazia (primeira utilização)
            <div style={{ padding: "48px 0", textAlign: "center", color: "var(--tx-3)" }}>
              <i data-lucide="building-2" style={{ width: 24, height: 24, display: "block", margin: "0 auto 10px" }}></i>
              <div style={{ font: "500 13px/1 var(--ff-body)", color: "var(--tx-2)", marginBottom: 8 }}>
                Nenhum cliente cadastrado
              </div>
              <button className="btn btn-primary" onClick={abrirNovo} style={{ margin: "0 auto" }}>
                <i data-lucide="plus" style={{width:13,height:13}}></i> Cadastrar primeiro cliente
              </button>
            </div>
          ) : !temFiltroAtivo ? (
            // Estado inicial: base grande, exige busca/filtro pra listar
            <div style={{ padding: "56px 24px", textAlign: "center", color: "var(--tx-3)" }}>
              <i data-lucide="search" style={{ width: 28, height: 28, display: "block", margin: "0 auto 14px", color: 'var(--grn)' }}></i>
              <div style={{ font: "600 14px/1.3 var(--ff-body)", color: "var(--tx)", marginBottom: 6 }}>
                Base com {totalClientes.toLocaleString('pt-BR')} clientes
              </div>
              <div style={{ font: "400 13px/1.5 var(--ff-body)", color: "var(--tx-3)", marginBottom: 18, maxWidth: 380, margin: '0 auto 18px' }}>
                Use a busca acima ou aplique um filtro (UF, segmento ou status) para listar.
              </div>
              <button className="btn btn-ghost" onClick={() => setForcarMostrarTodos(true)}
                style={{ fontSize: 12, color: 'var(--tx-3)' }}>
                Mostrar todos mesmo assim (pode ficar lento)
              </button>
            </div>
          ) : filtered.length === 0 ? (
            // Filtro/busca sem resultados
            <div style={{ padding: "48px 0", textAlign: "center", color: "var(--tx-3)" }}>
              <i data-lucide="search-x" style={{ width: 24, height: 24, display: "block", margin: "0 auto 10px" }}></i>
              <div style={{ font: "500 13px/1 var(--ff-body)", color: "var(--tx-2)", marginBottom: 8 }}>
                Nenhum cliente encontrado
              </div>
              <button className="btn btn-ghost" onClick={limparFiltros} style={{ fontSize: 12 }}>
                Limpar filtros
              </button>
            </div>
          ) : (
            <>
              <div className="dt-wrap">
                <table className="dt">
                  <thead>
                    <tr>
                      <th>Razão social</th>
                      <th>Cidade/UF</th>
                      <th>Segmento</th>
                      <th style={{ textAlign: "right" }}>Vol. anual</th>
                      <th>Status</th>
                    </tr>
                  </thead>
                  <tbody>
                    {visibleClientes.map(c => (
                      <tr key={c.id}
                        onClick={() => setSelected(c.id)}
                        style={{ cursor: "pointer", background: selected === c.id ? "var(--grn-050)" : "", opacity: c.status === 'inativo' ? 0.55 : 1 }}
                      >
                        <td>
                          <div style={{ font: "500 13px/1.2 var(--ff-body)", color: "var(--tx)" }}>{c.razao}</div>
                          <div style={{ font: "400 11px/1 var(--ff-mono)", color: "var(--tx-3)", marginTop: 2 }}>{c.contato}</div>
                        </td>
                        <td className="tx2" style={{ fontSize: 12 }}>
                          {c.cidade ? c.cidade + (c.uf ? '/' + c.uf : '') : (c.uf || '—')}
                        </td>
                        <td className="tx2" style={{ fontSize: 12 }}>{c.segmento}</td>
                        <td className="num">{fR(c.volume_anual)}</td>
                        <td><Badge status={c.status} /></td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
              {/* Rodapé de paginação */}
              {(temMaisPaginas || filtered.length > PAGE_SIZE) && (
                <div style={{
                  padding: '14px 18px',
                  borderTop: '1px solid var(--border)',
                  display: 'flex',
                  alignItems: 'center',
                  justifyContent: 'space-between',
                  gap: 12,
                  flexWrap: 'wrap',
                }}>
                  <div style={{ font: '400 12px/1.4 var(--ff-body)', color: 'var(--tx-3)' }}>
                    Mostrando <strong style={{ color: 'var(--tx-2)' }}>{visibleClientes.length.toLocaleString('pt-BR')}</strong> de <strong style={{ color: 'var(--tx-2)' }}>{filtered.length.toLocaleString('pt-BR')}</strong> {filtered.length === 1 ? 'cliente' : 'clientes'}
                  </div>
                  {temMaisPaginas && (
                    <button className="btn btn-secondary" onClick={() => setPage(p => p + 1)} style={{ fontSize: 12 }}>
                      <i data-lucide="chevrons-down" style={{ width: 13, height: 13 }}></i>
                      Carregar mais {Math.min(PAGE_SIZE, filtered.length - visibleClientes.length).toLocaleString('pt-BR')}
                    </button>
                  )}
                </div>
              )}
            </>
          )}
        </Panel>

        {/* Detalhe */}
        {cli ? (() => {
          // Aba "Dados Públicos" só aparece se PJ (ou legado, sem tipo_cliente) E com CNPJ
          const podeDadosPublicos = (cli.tipo_cliente !== 'PF') && !!(cli.cnpj && cli.cnpj.trim());
          // "Frota" é universal (PF e PJ podem ter máquinas). Sempre exibida.
          // Se a aba ativa for "dados_publicos" mas não há permissão, cai pra "geral".
          let abaCorrente = abaAtiva;
          if (abaCorrente === 'dados_publicos' && !podeDadosPublicos) abaCorrente = 'geral';
          // Contagens de frota para o badge da aba (evita abrir a aba pra saber a composição)
          const _frotaCli = (CRM_DATA.frotaCliente || []).filter(function(m) { return m.cliente_id === cli.id; });
          const _nRepr    = _frotaCli.filter(function(m) { return m.tipo !== 'concorrente'; }).length;
          const _nConc    = _frotaCli.filter(function(m) { return m.tipo === 'concorrente'; }).length;
          return (
          <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
            {/* Tabs — Visão Geral + Frota sempre; Dados Públicos só pra PJ com CNPJ */}
            <div className="tabs" style={{ marginBottom: 0 }}>
              <button className={abaCorrente === 'geral' ? 'is-on' : ''}
                      onClick={() => setAbaAtiva('geral')}>
                Visão Geral
              </button>
              {podeDadosPublicos && (
                <button className={abaCorrente === 'dados_publicos' ? 'is-on' : ''}
                        onClick={() => setAbaAtiva('dados_publicos')}>
                  <i data-lucide="building" style={{ width: 12, height: 12, marginRight: 4, verticalAlign: 'middle' }}></i>
                  Dados Públicos
                </button>
              )}
              <button className={abaCorrente === 'frota' ? 'is-on' : ''}
                      onClick={() => setAbaAtiva('frota')}>
                <i data-lucide="truck" style={{ width: 12, height: 12, marginRight: 4, verticalAlign: 'middle' }}></i>
                Frota
                {_frotaCli.length > 0 && (
                  <span style={{ display: 'inline-flex', alignItems: 'center', gap: 3, marginLeft: 5, fontSize: 10, fontWeight: 700, lineHeight: 1 }}>
                    {_nRepr > 0 && <span style={{ color: 'var(--grn)' }}>{_nRepr}R</span>}
                    {_nRepr > 0 && _nConc > 0 && <span style={{ color: 'var(--tx-3)', fontWeight: 400 }}>·</span>}
                    {_nConc > 0 && <span style={{ color: '#c07800' }}>{_nConc}C</span>}
                  </span>
                )}
              </button>
              <button className={abaCorrente === 'contatos' ? 'is-on' : ''}
                      onClick={() => setAbaAtiva('contatos')}>
                <i data-lucide="users" style={{ width: 12, height: 12, marginRight: 4, verticalAlign: 'middle' }}></i>
                Contatos
              </button>
            </div>

            {abaCorrente === 'dados_publicos' ? (
              <DadosPublicosTab cnpj={cli.cnpj} onNav={onNav} />
            ) : abaCorrente === 'frota' ? (
              <FrotaTab cli={cli} />
            ) : abaCorrente === 'contatos' ? (
              <ContatosTab cli={cli} />
            ) : (
              <React.Fragment>
                <Panel title={cli.razao} meta={
                  <div style={{ display:'flex', gap:6, alignItems:'center', flexWrap:'wrap' }}>
                    <Badge status={cli.status} />
                    {cli.classe && (function(){
                      var classeColor = {
                        'Grande conta': { bg:'#fff3cd', color:'#7a5800', border:'#f4a300' },
                        'A':            { bg:'#d4edda', color:'#155724', border:'#28a745' },
                        'B':            { bg:'#cce5ff', color:'#004085', border:'#3370b7' },
                        'C':            { bg:'#e2e3e5', color:'#383d41', border:'#999'    },
                        'D':            { bg:'#f8d7da', color:'#721c24', border:'#e07070' },
                      }[cli.classe] || { bg:'var(--surface-2)', color:'var(--tx-2)', border:'var(--border)' };
                      return (
                        <span style={{ padding:'2px 8px', borderRadius:99, fontSize:11, fontWeight:600,
                          background: classeColor.bg, color: classeColor.color,
                          border:'1px solid '+classeColor.border, whiteSpace:'nowrap' }}>
                          {cli.classe === 'Grande conta' ? '★ '+cli.classe : 'Classe '+cli.classe}
                        </span>
                      );
                    })()}
                  </div>
                }
                  actions={
                    <button className="btn btn-secondary" onClick={() => abrirEditar(cli)}
                      style={{ fontSize: 12, padding: "5px 10px" }}>
                      <i data-lucide="pencil" style={{width:12,height:12}}></i> Editar
                    </button>
                  }>
                  <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
                    {[
                      // PF mostra CPF; PJ mostra CNPJ (default quando tipo_cliente vazio = legado PJ)
                      ...(cli.tipo_cliente === 'PF'
                          ? (cli.cpf ? [["CPF", cli.cpf]] : [])
                          : [["CNPJ", cli.cnpj]]),
                      ["Segmento",  cli.segmento],
                      ...(cli.classe  ? [["Classe",    cli.classe]]   : []),
                      ...(cli.tipo_cliente === 'PF' && cli.atividade ? [["Atividade / Profissão", cli.atividade]] : []),
                      ...(cli.ie      ? [["Insc. Estadual", cli.ie]]  : []),
                      ["Cidade",    (cli.cidade || "") + (cli.uf ? " · " + cli.uf : "")],
                      ...(cli.cep     ? [["CEP",            cli.cep]] : []),
                      ["Contato",   cli.contato],
                      ["Telefone",  cli.fone],
                      ["E-mail",    cli.email],
                      ...(cli.endereco ? [["Endereço", cli.endereco]] : []),
                    ].map(([lbl, val]) => (
                      <div key={lbl} style={{ background: "var(--surface-2)", border: "1px solid var(--border)", borderRadius: "var(--r-md)", padding: "8px 12px" }}>
                        <div style={{ font: "600 10px/1 var(--ff-body)", textTransform: "uppercase", letterSpacing: ".06em", color: "var(--tx-3)", marginBottom: 4 }}>{lbl}</div>
                        <div style={{ font: "400 12px/1.2 var(--ff-mono)", color: "var(--tx)" }}>{val || "—"}</div>
                      </div>
                    ))}
                  </div>
                  <div style={{ marginTop: 12, padding: "12px 14px", background: "var(--grn-050)", border: "1px solid var(--grn-100)", borderRadius: "var(--r-md)", display: "flex", justifyContent: "space-between", alignItems: "center" }}>
                    <span style={{ font: "600 11px/1 var(--ff-body)", textTransform: "uppercase", letterSpacing: ".06em", color: "var(--tx-3)" }}>Volume anual</span>
                    <span style={{ font: "400 24px/1 var(--ff-display)", color: "var(--grn)" }}>{fR(cli.volume_anual)}</span>
                  </div>
                </Panel>

                <Panel title="Oportunidades" meta={`${cliOps.length} total`}
                  actions={
                    typeof onOpenNovaOpComCliente === 'function' ? (
                      <button className="btn btn-primary" style={{ fontSize: 12, padding: "5px 10px" }}
                        onClick={() => onOpenNovaOpComCliente(cli.id)}>
                        <i data-lucide="plus" style={{width:12,height:12}}></i> Nova Oportunidade
                      </button>
                    ) : null
                  }>
                  {cliOps.length === 0 ? (
                    <div style={{ color: "var(--tx-3)", font: "400 13px/1 var(--ff-body)" }}>Nenhuma oportunidade vinculada.</div>
                  ) : (
                    <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                      {cliOps.map(op => (
                        <div key={op.id} style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 12px", background: "var(--surface-2)", border: "1px solid var(--border)", borderRadius: "var(--r-md)", cursor: "pointer" }}
                          onClick={() => onNav("oportunidade", op.id)}>
                          <div style={{ flex: 1 }}>
                            <div style={{ font: "500 13px/1 var(--ff-body)", color: "var(--tx)" }}>{op.titulo}</div>
                            <div style={{ font: "400 11px/1 var(--ff-mono)", color: "var(--tx-3)", marginTop: 3 }}>{op.id} · {fDate(op.previsao_fechamento)}</div>
                          </div>
                          <div style={{ textAlign: "right" }}>
                            <div style={{ font: "400 14px/1 var(--ff-mono)", color: "var(--tx)" }}>{fR(op.valor)}</div>
                            <div style={{ marginTop: 4 }}><Badge status={op.etapa} /></div>
                          </div>
                        </div>
                      ))}
                    </div>
                  )}
                </Panel>
              </React.Fragment>
            )}
          </div>
          );
        })() : (
          <Panel>
            <div style={{ padding: "48px 0", textAlign: "center", color: "var(--tx-3)" }}>
              <i data-lucide="building-2" style={{ width: 28, height: 28, display: "block", margin: "0 auto 12px" }}></i>
              <div style={{ font: "500 14px/1 var(--ff-body)", color: "var(--tx-2)" }}>Selecione um cliente</div>
              <div style={{ marginTop: 6, fontSize: 12 }}>Clique em uma linha para ver os detalhes</div>
            </div>
          </Panel>
        )}
      </div>

      <DupClienteModal
        open={!!dupModal}
        dupInfo={dupModal}
        onClose={() => setDupModal(null)}
        onAbrirCliente={function(c) {
          setDupModal(null); setEditando(c); setPreFillAtivo(null); setModalOpen(true);
        }}
        onAplicar={async function(c, patch) {
          try {
            await CRM_API.updateItem('Clientes', c._spId, patch);
            const idx = CRM_DATA.clientes.findIndex(function(x) { return x.id === c.id; });
            if (idx >= 0) CRM_DATA.clientes[idx] = Object.assign({}, CRM_DATA.clientes[idx], patch);
          } catch(e) { window.CRM_TOAST('Erro ao atualizar: ' + e.message, 'error'); }
        }}
      />
      <ClienteModal
        open={modalOpen}
        onClose={() => setModalOpen(false)}
        onSave={onSave}
        inicial={editando}
        empresaPreFill={preFillAtivo}
      />
    </div>
  );
}
window.Clientes = Clientes;

// ── Helpers: gerar links públicos a partir de dados brutos do CRM_DATA ──────
function _buildPropostaLink(p) {
  var op   = CRM_DATA.getOp ? CRM_DATA.getOp(p.op_id) : null;
  var cli  = (op && CRM_DATA.getCliente) ? CRM_DATA.getCliente(op.cliente_id) : null;
  var vend = CRM_DATA.getUsuario ? CRM_DATA.getUsuario(p.responsavel_id) : null;
  // Preferência: CRM_DATA.itens_proposta (sessão atual, mais atualizado) → p.itens (SP itens_json) → []
  var itensSource = (CRM_DATA.itens_proposta && CRM_DATA.itens_proposta[p.id] && CRM_DATA.itens_proposta[p.id].length)
    ? CRM_DATA.itens_proposta[p.id]
    : (p.itens || []);
  var itens = itensSource.map(function(it) {
    return { sku: it.sku||'', familia: it.familia||'', desc: it.desc||'',
             descricao_tecnica: it.descricao_tecnica||'',
             qtd: it.qtd||it.quantidade||1, total: it.total||0, desconto: it.desconto||0,
             prazo: it.prazo||it.prazo_entrega||'', frete_tipo: it.frete_tipo||'',
             garantia: it.garantia||'', icms_saida: it.icms_saida||0, composicao: it.composicao||[] };
  });
  var dados = JSON.stringify({
    id: p.id, versao: p.versao||1, data: p.data, validade: p.validade, status: p.status||'',
    op: op ? { titulo: op.titulo||'', familia: op.familia||'', modelo: op.modelo||'',
               descricao: (op.descricao||'').slice(0,200),
               fabricante: (itens[0]&&itens[0].familia)||op.familia||'' } : {},
    cli: cli ? { razao: cli.razao||'', cnpj: cli.cnpj||'', cidade: cli.cidade||'',
                 uf: cli.uf||'', contato: cli.contato||'', contato_nome: (op&&op.contato_nome)||'' } : {},
    vend: vend ? { nome: vend.nome||'', cargo: vend.cargo||'', email: vend.email||'' } : {},
    itens: itens,
  });
  // &int=1 sinaliza abertura interna (CRM) → proposta-publica.html exibe save-bar
  // Links enviados ao cliente (WhatsApp/e-mail) não carregam &int=1
  var BASE = window.location.origin + '/crm/proposta-publica.html?d=';
  var encoded = (typeof LZString !== 'undefined') ? LZString.compressToEncodedURIComponent(dados) : (function(){ try{ return btoa(unescape(encodeURIComponent(dados))); }catch(e){ return ''; } })();
  return encoded ? BASE + encoded + '&int=1' : '';
}

function _buildPedidoLink(ped) {
  var prop = (CRM_DATA.getProposta && ped.proposta_id) ? CRM_DATA.getProposta(ped.proposta_id) : null;
  var op   = CRM_DATA.getOp ? CRM_DATA.getOp(ped.op_id || (prop && prop.op_id) || '') : null;
  var cli  = CRM_DATA.getCliente ? CRM_DATA.getCliente(ped.cliente_id) : null;
  var vend = CRM_DATA.getUsuario ? CRM_DATA.getUsuario(ped.vendedor_id) : null;
  var valorNum = ped._valorNum || parseFloat(ped.valor) || 0;
  var itens = (ped.itens || []).map(function(it) {
    return { sku: it.sku||'', desc: it.desc||it.modelo||'',
             qtd: it.qtd||it.quantidade||1, total: it.total||0, desconto: it.desconto||0,
             prazo: it.prazo||'', impostos: it.impostos||0, garantia: it.garantia||'',
             composicao: it.composicao||[] };
  });
  var dados = JSON.stringify({
    tipo: 'pedido', id: ped.id, propId: ped.proposta_id||'', opId: ped.op_id||'',
    data: ped.data || new Date().toISOString(),
    pgto_condicao: ped.pgto_condicao||'', notas: ped.notas||'',
    unidade: ped.unidade_bauko||'', valor: valorNum,
    cli: cli ? { razao: cli.razao||'', cnpj: cli.cnpj||'', contato: cli.contato||'',
                 contato_nome: (op&&op.contato_nome)||'', cidade: cli.cidade||'',
                 uf: cli.uf||'', telefone: cli.telefone||'', email: cli.email||'',
                 endereco: cli.endereco||'', ie: cli.ie||'', cep: cli.cep||'',
                 segmento: cli.segmento||'', atividade: cli.atividade||'' } : {},
    vend: vend ? { nome: vend.nome||'', cargo: vend.cargo||'', email: vend.email||'' } : {},
    op: op ? { titulo: op.titulo||'', familia: op.familia||'', modelo: op.modelo||'',
               descricao: (op.descricao||'').slice(0,300) } : {},
    entrada: ped.pgto_entrada||ped.entrada||'',
    local_entrega: (itens[0]&&itens[0].local_entrega)||ped.local_entrega||'',
    validade: ped.validade||(prop&&prop.validade)||'',
    itens: itens,
  });
  var BASE = window.location.origin + '/crm/pedido-publica.html?view=1&d=';
  if (typeof LZString !== 'undefined') return BASE + LZString.compressToEncodedURIComponent(dados);
  try { return BASE + btoa(unescape(encodeURIComponent(dados))); } catch(e) { return ''; }
}

// ══════════════════════════════════════════════════════
// PROPOSTAS
// ══════════════════════════════════════════════════════
function Propostas({ onNav, onOpenProp }) {
  // K-D14: persiste filtro entre navegações via sessionStorage
  const [filter, setFilter] = useScreenState(function() {
    try { return sessionStorage.getItem('crm_propostas_filter') || 'abertas'; } catch(e) { return 'abertas'; }
  });
  const [busca, setBusca] = useScreenState("");
  const [, setTick] = useScreenState(0);
  // K-D5: paginação 50/pg
  const [page, setPage] = useScreenState(1);
  const PAGE_SIZE = 50;
  // K-D8: sort clicável
  const [sortBy,  setSortBy]  = useScreenState('emissao');
  const [sortDir, setSortDir] = useScreenState('desc');
  function toggleSort(col) {
    if (sortBy === col) setSortDir(function(d){ return d === 'asc' ? 'desc' : 'asc'; });
    else { setSortBy(col); setSortDir('desc'); }
    setPage(1);
  }
  function sortIcon(col) { return sortBy === col ? (sortDir === 'asc' ? ' ▲' : ' ▼') : ''; }
  React.useEffect(function() {
    try { sessionStorage.setItem('crm_propostas_filter', filter); } catch(e) {}
    setPage(1); // reset paginação ao mudar filtro
  }, [filter]);
  React.useEffect(function() { setPage(1); }, [busca]);
  const statusList = ["abertas", "rascunho", "aprovacao", "enviada", "perdidas", "canceladas", "todas"];

  // Re-render quando status ou deleção de proposta mudar via CustomEvent
  React.useEffect(function() {
    function sync() { setTick(function(t){ return t + 1; }); }
    window.addEventListener('crm_prop_status_changed', sync);
    window.addEventListener('crm_prop_deleted', sync);
    return function() {
      window.removeEventListener('crm_prop_status_changed', sync);
      window.removeEventListener('crm_prop_deleted', sync);
    };
  }, []);

  // ── Visibilidade por papel (CRM_USER) ────────────────────────
  // Proposta herda o vendedor da oportunidade (proposta.op_id → op.vendedor_id).
  // Comercial só vê propostas das próprias ops; BDR/Admin/Gerente veem todas.
  const _podeVerTodas = !!(window.CRM_USER && window.CRM_USER.podeVerOpAlheia);
  const _meuUserId    = (window.CRM_USER && window.CRM_USER.userId) || null;
  const propostasAutorizadas = _podeVerTodas
    ? CRM_DATA.propostas
    : CRM_DATA.propostas.filter(function(p) {
        // Verifica diretamente pelo responsável da proposta (mais confiável)
        if (p.responsavel_id && p.responsavel_id === _meuUserId) return true;
        // Fallback: herda vendedor da oportunidade (op pode ter sido reatribuída)
        var op = CRM_DATA.getOp(p.op_id);
        return !!(op && op.vendedor_id === _meuUserId);
      });

  const filtered = (function() {
    var base;
    if (filter === "abertas")         base = propostasAutorizadas.filter(p => !['cancelada','perdida','pedido','pedido_assinado','faturamento'].includes(p.status));
    else if (filter === "aprovacao")  base = propostasAutorizadas.filter(p => p.status === 'aguardando_aprovacao' || p.status === 'aprovada');
    else if (filter === "enviada")    base = propostasAutorizadas.filter(p => p.status === 'enviada' || p.status === 'em_negociacao');
    else if (filter === "pedido")     base = propostasAutorizadas.filter(p => p.status === 'pedido' || p.status === 'pedido_assinado' || p.status === 'faturamento');
    else if (filter === "perdidas")   base = propostasAutorizadas.filter(p => p.status === 'perdida');
    else if (filter === "canceladas") base = propostasAutorizadas.filter(p => p.status === 'cancelada');
    else if (filter === "todas")      base = propostasAutorizadas;
    else                              base = propostasAutorizadas.filter(p => p.status === filter); // rascunho
    if (!busca.trim()) return base;
    var q = busca.trim().toLowerCase();
    return base.filter(function(p) {
      var op    = CRM_DATA.getOp(p.op_id);
      var cli   = op ? CRM_DATA.getCliente(op.cliente_id) : null;
      var modelo = (op && op.modelo) || (p.itens && p.itens[0] && p.itens[0].sku) || '';
      return (p.id || '').toLowerCase().includes(q) ||
             (op  && op.titulo  && op.titulo.toLowerCase().includes(q)) ||
             (cli && cli.razao  && cli.razao.toLowerCase().includes(q)) ||
             modelo.toLowerCase().includes(q);
    });
  })();
  // K-D8: aplica sort antes de paginar
  const filteredSorted = filtered.slice().sort(function(a, b) {
    var dir = sortDir === 'asc' ? 1 : -1;
    if (sortBy === 'valor')   return dir * ((a.valor || 0) - (b.valor || 0));
    if (sortBy === 'status')  return dir * (a.status || '').localeCompare(b.status || '');
    if (sortBy === 'validade') return dir * (a.validade || '').localeCompare(b.validade || '');
    // default: emissao
    return dir * (a.criado_em || '').localeCompare(b.criado_em || '');
  });
  const nCanceladas = propostasAutorizadas.filter(p => p.status === 'cancelada').length;
  const nAprovacao  = propostasAutorizadas.filter(p => p.status === 'aguardando_aprovacao' || p.status === 'aprovada').length;

  const totalEnviadas = propostasAutorizadas.filter(p => p.status === "enviada").reduce((s, p) => s + p.valor, 0);
  const totalAceitas  = propostasAutorizadas.filter(p => p.status === "aceita").reduce((s, p) => s + p.valor, 0);

  function canEdit(p) { return p.status === 'rascunho' || p.status === 'enviada'; }

  return (
    <div data-screen-label="04 Propostas">
      {/* KPIs */}
      <div className="kpi-grid" style={{ gridTemplateColumns: "repeat(3,1fr)", marginBottom: 18 }}>
        <div className="kpi-card">
          <div className="kpi-card__eyebrow">Propostas enviadas</div>
          <div className="kpi-card__v kpi-card__v--mono">{fR(totalEnviadas)}</div>
          <div className="kpi-card__delta kpi-card__delta--warn">· Aguardando retorno</div>
        </div>
        <div className="kpi-card">
          <div className="kpi-card__eyebrow">Propostas aceitas</div>
          <div className="kpi-card__v kpi-card__v--mono">{fR(totalAceitas)}</div>
          <div className="kpi-card__delta kpi-card__delta--pos">▲ Negócios fechados</div>
        </div>
        <div className="kpi-card">
          <div className="kpi-card__eyebrow">Total de propostas</div>
          <div className="kpi-card__v">{propostasAutorizadas.length}</div>
          <div className="kpi-card__delta kpi-card__delta--pos">· {propostasAutorizadas.filter(p=>p.status==="aceita").length} aceitas</div>
        </div>
      </div>

      <Panel title="Propostas" actions={
        <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
          <input
            type="search" placeholder="Buscar proposta, cliente ou modelo…"
            value={busca} onChange={function(e){ setBusca(e.target.value); }}
            style={{ fontSize: 12, padding: "5px 10px", borderRadius: 6, border: "1px solid var(--border)", background: "var(--surface-1)", color: "var(--tx-1)", width: 220 }}
          />
          <div className="tabs" style={{ marginBottom: 0 }}>
            {statusList.map(s => {
              var label;
              if      (s === "abertas")    label = "Em aberto";
              else if (s === "rascunho")   label = "Rascunho";
              else if (s === "aprovacao")  label = "Aprovação" + (nAprovacao ? " (" + nAprovacao + ")" : "");
              else if (s === "enviada")    label = "Enviada";
              else if (s === "perdidas")   label = "Perdidas";
              else if (s === "canceladas") label = "Canceladas" + (nCanceladas ? " (" + nCanceladas + ")" : "");
              else                         label = "Todas";
              return (
                <button key={s} className={filter === s ? "is-on" : ""} onClick={() => setFilter(s)}>
                  {label}
                </button>
              );
            })}
          </div>
        </div>
      } noPad>
        <div className="dt-wrap">
          <table className="dt">
            <thead>
              <tr>
                <th>Proposta</th>
                <th>Modelo</th>
                <th>Cliente</th>
                <th style={{ textAlign: "right", cursor: "pointer", userSelect: "none" }} onClick={() => toggleSort('valor')}>Valor{sortIcon('valor')}</th>
                <th style={{ textAlign: "center" }}>Desconto</th>
                <th style={{ cursor: "pointer", userSelect: "none" }} onClick={() => toggleSort('status')}>Status{sortIcon('status')}</th>
                <th style={{ cursor: "pointer", userSelect: "none" }} onClick={() => toggleSort('emissao')}>Emissão{sortIcon('emissao')}</th>
                <th style={{ cursor: "pointer", userSelect: "none" }} onClick={() => toggleSort('validade')}>Validade{sortIcon('validade')}</th>
                <th>Responsável</th>
                <th></th>
              </tr>
            </thead>
            <tbody>
              {filtered.length === 0 && (
                <tr><td colSpan={10} style={{ textAlign: 'center', padding: '32px 0', color: 'var(--tx-3)' }}>Nenhuma proposta encontrada</td></tr>
              )}
              {filteredSorted.slice(0, page * PAGE_SIZE).map(p => {
                const op     = CRM_DATA.getOp(p.op_id);
                const cli    = op ? CRM_DATA.getCliente(op.cliente_id) : null;
                const vend   = CRM_DATA.getUsuario(p.responsavel_id);
                const isExpired = new Date(p.validade) < new Date();
                const cancelada = p.status === 'cancelada';
                const modelo = (op && op.modelo) || (p.itens && p.itens[0] && p.itens[0].sku) || '—';
                return (
                  <tr key={p.id}
                    title={cancelada && p.motivo_cancelamento ? 'Motivo: ' + p.motivo_cancelamento : 'Abrir proposta'}
                    style={{ cursor: "pointer", opacity: cancelada ? 0.55 : 1, textDecoration: cancelada ? 'line-through' : 'none' }}
                    onClick={function(e) {
                      if (e.target.closest('[data-action]')) return;
                      var url = _buildPropostaLink(p);
                      if (url) window.open(url, '_blank');
                    }}>
                    <td>
                      <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
                        <span className="mono tx3">{p.id}</span>
                        <span style={{ font: "500 10px/1 var(--ff-mono)", color: "var(--tx-3)", background: "var(--surface-2)", border: "1px solid var(--border)", borderRadius: 4, padding: "2px 6px" }}>{p.versao}</span>
                      </div>
                    </td>
                    <td style={{ fontSize: 12, color: "var(--tx-2)", maxWidth: 140, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{modelo}</td>
                    <td className="tx2" style={{ fontSize: 12 }}>{cli?.razao?.split(" ").slice(0, 2).join(" ")}</td>
                    <td className="num">{fR(p.valor)}</td>
                    <td style={{ textAlign: "center" }}>
                      <span className="mono" style={{ color: p.desconto > 0 ? "var(--warn)" : "var(--tx-3)" }}>{p.desconto > 0 ? `−${p.desconto}%` : "—"}</span>
                    </td>
                    <td>
                      <div style={{ display:'flex', flexWrap:'wrap', gap:4, alignItems:'center' }}>
                        <Badge status={p.status} />
                        {op && (op.etapa === 'fechado' || op.etapa === 'fechamento_parcial')
                          && p.status !== 'perdida' && p.status !== 'cancelada' && (
                          <span
                            title={'Oportunidade ' + (op.etapa === 'fechado' ? 'fechada' : 'ganho parcial')}
                            style={{ fontSize:9, fontFamily:'var(--ff-body)', fontWeight:600, textTransform:'uppercase', letterSpacing:'.05em',
                              padding:'2px 5px', borderRadius:'var(--r-pill)', whiteSpace:'nowrap',
                              background: op.etapa === 'fechado' ? 'var(--grn-050)' : 'var(--warn-050)',
                              color: op.etapa === 'fechado' ? 'var(--grn)' : 'var(--warn)',
                              border:'1px solid ' + (op.etapa === 'fechado' ? 'var(--grn)' : 'var(--warn)') + '55' }}>
                            {op.etapa === 'fechado' ? 'Op fechada' : 'Op parcial'}
                          </span>
                        )}
                      </div>
                    </td>
                    <td className="mono tx3" style={{ fontSize: 12 }}>{fDate(p.data)}</td>
                    <td className="mono" style={{ fontSize: 12, color: isExpired && p.status === "enviada" ? "var(--danger)" : "var(--tx-3)" }}>
                      {fDate(p.validade)}{isExpired && p.status === "enviada" ? " ⚠" : ""}
                    </td>
                    <td>
                      <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
                        <div style={{ width: 22, height: 22, borderRadius: "50%", background: vend?.avatar_color, display: "flex", alignItems: "center", justifyContent: "center", font: "600 9px/1 var(--ff-body)", color: "#fff" }}>{vend?.iniciais}</div>
                        <span style={{ fontSize: 12, color: "var(--tx-2)" }}>{vend?.nome?.split(" ")[0]}</span>
                      </div>
                    </td>
                    <td data-action="actions" style={{ whiteSpace: 'nowrap' }}>
                      <div style={{ display: 'flex', gap: 4, justifyContent: 'flex-end' }} onClick={function(e){ e.stopPropagation(); }}>
                        {canEdit(p) && onOpenProp && (
                          <button data-action="edit"
                            title="Editar proposta"
                            onClick={function(e){ e.stopPropagation(); onOpenProp(p.id); }}
                            style={{ background: 'none', border: '1px solid var(--border)', borderRadius: 6, padding: '3px 6px', cursor: 'pointer', color: 'var(--tx-2)', display: 'flex', alignItems: 'center' }}>
                            <i data-lucide="pencil" style={{ width: 13, height: 13 }}></i>
                          </button>
                        )}
                        {op && onNav && (
                          <button data-action="op"
                            title={'Ver oportunidade: ' + (op.titulo || '')}
                            onClick={function(e){ e.stopPropagation(); onNav('oportunidade', op.id); }}
                            style={{ background: 'none', border: '1px solid var(--border)', borderRadius: 6, padding: '3px 6px', cursor: 'pointer', color: 'var(--tx-2)', display: 'flex', alignItems: 'center' }}>
                            <i data-lucide="briefcase" style={{ width: 13, height: 13 }}></i>
                          </button>
                        )}
                      </div>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
        {/* K-D5: paginação carregar mais */}
        {filtered.length > page * PAGE_SIZE && (
          <div style={{ textAlign:'center', padding:'12px 0', borderTop:'1px solid var(--border)' }}>
            <button className="btn btn-secondary" onClick={function(){ setPage(function(p){ return p + 1; }); }}>
              Carregar mais ({filtered.length - page * PAGE_SIZE} restantes)
            </button>
          </div>
        )}
      </Panel>
    </div>
  );
}
window.Propostas = Propostas;

// ══════════════════════════════════════════════════════
// ATIVIDADES
// ══════════════════════════════════════════════════════

// ── Helpers ────────────────────────────────────────────
function _atvNowISO() {
  var d = new Date(), p = function(n){ return String(n).padStart(2,'0'); };
  return d.getFullYear()+'-'+p(d.getMonth()+1)+'-'+p(d.getDate())+'T'+p(d.getHours())+':'+p(d.getMinutes());
}
function _atvPeriodoOk(dataStr, periodo) {
  if (periodo === 'todas') return true;
  var d = new Date(dataStr);
  if (isNaN(d.getTime())) return false;
  var hoje = new Date(); hoje.setHours(0,0,0,0);
  if (periodo === 'hoje') {
    var amanha = new Date(hoje); amanha.setDate(hoje.getDate()+1);
    return d >= hoje && d < amanha;
  }
  if (periodo === 'semana') {
    var dow = hoje.getDay();
    var seg = new Date(hoje); seg.setDate(hoje.getDate()-(dow===0?6:dow-1));
    return d >= seg;
  }
  if (periodo === 'mes') {
    return d >= new Date(hoje.getFullYear(), hoje.getMonth(), 1);
  }
  return true;
}
function _atvBucket(dataStr) {
  var d = new Date(dataStr);
  if (isNaN(d.getTime())) return 'Mais antigas';
  var hoje = new Date(); hoje.setHours(0,0,0,0);
  var dDay = new Date(d); dDay.setHours(0,0,0,0);
  if (dDay.getTime() === hoje.getTime()) return 'Hoje';
  var ontem = new Date(hoje); ontem.setDate(hoje.getDate()-1);
  if (dDay.getTime() === ontem.getTime()) return 'Ontem';
  var dow = hoje.getDay();
  var seg = new Date(hoje); seg.setDate(hoje.getDate()-(dow===0?6:dow-1));
  if (dDay >= seg) return 'Esta semana';
  if (dDay >= new Date(hoje.getFullYear(), hoje.getMonth(), 1)) return 'Este mês';
  return 'Mais antigas';
}
function _atvNorm(s) {
  return String(s||'').normalize('NFD').replace(/[̀-ͯ]/g,'').toLowerCase();
}
var ATV_TIPOS  = ['ligacao','whatsapp','visita','email','followup','reuniao'];
var ATV_LABEL  = { ligacao:'Ligação', whatsapp:'WhatsApp', visita:'Visita', email:'E-mail', followup:'Follow-up', reuniao:'Reunião' };
var ATV_ICON   = { ligacao:'phone', whatsapp:'message-circle', visita:'map-pin', email:'mail', followup:'clock', reuniao:'users' };
var ATV_COLOR  = { ligacao:'var(--grn)', whatsapp:'#25d366', visita:'var(--info)', email:'var(--warn)', followup:'var(--tx-3)', reuniao:'var(--ok)' };
var ATV_BUCKETS = ['Hoje','Ontem','Esta semana','Este mês','Mais antigas'];

function Atividades({ onNav }) {
  // K-D14: persiste filtros via sessionStorage
  const [tipoFilter,    setTipoFilter]    = useScreenState(function(){
    try { return sessionStorage.getItem('crm_atv_tipo') || 'todas'; } catch(e) { return 'todas'; }
  });
  const [periodoFilter, setPeriodoFilter] = useScreenState(function(){
    try { return sessionStorage.getItem('crm_atv_periodo') || 'todas'; } catch(e) { return 'todas'; }
  });
  const [vendFilter,    setVendFilter]    = useScreenState(function(){
    try { return sessionStorage.getItem('crm_atv_vend') || 'todos'; } catch(e) { return 'todos'; }
  });
  React.useEffect(function(){ try { sessionStorage.setItem('crm_atv_tipo', tipoFilter); } catch(e){} }, [tipoFilter]);
  React.useEffect(function(){ try { sessionStorage.setItem('crm_atv_periodo', periodoFilter); } catch(e){} }, [periodoFilter]);
  React.useEffect(function(){ try { sessionStorage.setItem('crm_atv_vend', vendFilter); } catch(e){} }, [vendFilter]);
  const [search,        setSearch]        = useScreenState("");
  const [showModal,     setShowModal]     = useScreenState(false);
  const [saving,        setSaving]        = useScreenState(false);
  const [tick,          setTick]          = useScreenState(0);

  // K-D6: escuta mudanças globais de atividades (outras sessões/abas via AtividadeModalGlobal)
  React.useEffect(function() {
    function sync() { setTick(function(t){ return t + 1; }); }
    window.addEventListener('crm_atividade_added', sync);
    window.addEventListener('crm_atividade_removed', sync);
    return function() {
      window.removeEventListener('crm_atividade_added', sync);
      window.removeEventListener('crm_atividade_removed', sync);
    };
  }, []);
  // Modal — atividade
  const [mTipo,      setMTipo]      = useScreenState("ligacao");
  const [mVendId,    setMVendId]    = useScreenState("");
  const [mClienteId, setMClienteId] = useScreenState("");
  const [mOpId,      setMOpId]      = useScreenState("");
  const [mTitulo,    setMTitulo]    = useScreenState("");
  const [mData,      setMData]      = useScreenState("");
  const [mDuracao,   setMDuracao]   = useScreenState("");
  const [mResultado, setMResultado] = useScreenState("positivo");
  const [mNotas,     setMNotas]     = useScreenState("");
  // Modal — cliente autocomplete
  const [mClienteSearch, setMClienteSearch] = useScreenState("");
  const [mClienteOpen,   setMClienteOpen]   = useScreenState(false);
  // Sugestão IA — modal pós-save
  const [showSugestaoModal,  setShowSugestaoModal]  = useScreenState(false);
  const [sugestaoLoading,    setSugestaoLoading]     = useScreenState(false);
  const [sugestaoResumo,     setSugestaoResumo]      = useScreenState('');
  const [sugestaoOpcoes,     setSugestaoOpcoes]      = useScreenState([]);
  const [sugestaoSel,        setSugestaoSel]         = useScreenState(0);
  const [sugestaoEditTitulo, setSugestaoEditTitulo]  = useScreenState('');
  const [sugestaoEditData,   setSugestaoEditData]    = useScreenState('');
  const [sugestaoCtx,        setSugestaoCtx]         = useScreenState(null);
  // Edição de atividade
  const [editingAtiv,        setEditingAtiv]         = useScreenState(null);

  // Identidade efetiva (centralizada em CRM_USER, reativa a "Visualizar como")
  var emailLogado = (window.CRM_USER && window.CRM_USER.email) || '';
  var userLogado  = CRM_DATA.usuarios.find(function(u){ return (u.email||'').toLowerCase()===emailLogado; });
  var isGerente   = !!(window.CRM_USER && window.CRM_USER.isGerente);

  // ── Visibilidade por papel ───────────────────────────
  // Comercial só vê próprias atividades; BDR/Admin/Gerente veem todas.
  var podeVerTodasAtv = !!(window.CRM_USER && window.CRM_USER.podeVerOpAlheia);
  var meuUserId       = (window.CRM_USER && window.CRM_USER.userId) || null;
  var atividadesAutorizadas = podeVerTodasAtv
    ? CRM_DATA.atividades
    : CRM_DATA.atividades.filter(function(a){ return a.vendedor_id === meuUserId; });

  // Ops do cliente selecionado no modal
  var opsDoCliente = mClienteId
    ? CRM_DATA.oportunidades.filter(function(o){ return o.cliente_id===mClienteId && !o.cancelada_em; })
    : [];

  // ── Filtragem ────────────────────────────────────────
  var ativs = atividadesAutorizadas.slice().sort(function(a,b){ return new Date(b.data)-new Date(a.data); });
  if (tipoFilter !== 'todas')    ativs = ativs.filter(function(a){ return a.tipo===tipoFilter; });
  if (periodoFilter !== 'todas') ativs = ativs.filter(function(a){ return _atvPeriodoOk(a.data, periodoFilter); });
  if (vendFilter !== 'todos' && podeVerTodasAtv)
    ativs = ativs.filter(function(a){ return a.vendedor_id===vendFilter; });
  if (search.trim()) {
    var _tks = _atvNorm(search).split(/\s+/).filter(function(t){ return t.length>=2; });
    if (!_tks.length) _tks = [_atvNorm(search).trim()];
    ativs = ativs.filter(function(at){
      var cli = CRM_DATA.getCliente(at.cliente_id);
      var hay = [_atvNorm(at.titulo), _atvNorm(at.notas), _atvNorm(cli?cli.razao:'')].join(' ');
      return _tks.every(function(t){ return hay.includes(t); });
    });
  }

  // KPIs: respeita período + vendedor, ignora tipo
  var ativsKpi = atividadesAutorizadas.filter(function(a){
    return _atvPeriodoOk(a.data, periodoFilter) && (!podeVerTodasAtv || vendFilter==='todos' || a.vendedor_id===vendFilter);
  });

  // Agrupamento por bucket de data
  var grupos = {};
  ATV_BUCKETS.forEach(function(b){ grupos[b]=[]; });
  ativs.forEach(function(at){
    var b = _atvBucket(at.data);
    if (!grupos[b]) grupos[b]=[];
    grupos[b].push(at);
  });

  // ── Abrir / fechar modal ─────────────────────────────
  function abrirModal() {
    setMTipo('ligacao'); setMClienteId(''); setMOpId(''); setMTitulo('');
    setMData(_atvNowISO()); setMDuracao(''); setMResultado('positivo'); setMNotas('');
    setMClienteSearch(''); setMClienteOpen(false);
    setEditingAtiv(null);
    setMVendId(userLogado ? userLogado.id : '');
    setShowModal(true);
  }
  function fecharModal(){ setShowModal(false); setSaving(false); }

  // Regra de cadência por classe — dias para próxima visita presencial
  var CADENCIA_CLASSE = {
    'Grande conta': 7,
    'A': 14,
    'B': 30,
    'C': 60,
    'D': null, // sob demanda — sem sugestão automática
  };
  function _calcProximaVisita(classe, baseIso) {
    var dias = CADENCIA_CLASSE[classe];
    if (!dias) return null;
    var base = baseIso ? new Date(baseIso) : new Date();
    base.setDate(base.getDate() + dias);
    return base.toISOString().slice(0, 10);
  }
  function _gravarDatasVisita(cli, ultimaIso, proximaIso) {
    if (!cli || !cli._spId) return;
    var updates = {};
    if (ultimaIso)  { cli.ultima_visita  = ultimaIso;  updates.ultima_visita  = ultimaIso;  }
    if (proximaIso) { cli.proxima_visita = proximaIso; updates.proxima_visita = proximaIso; }
    if (Object.keys(updates).length)
      CRM_API.updateItem('Clientes', cli._spId, updates)
        .catch(function(e){ console.warn('[CRM] datas_visita update:', e.message); });
  }

  async function salvarAtividade() {
    if (!mTitulo.trim()) { window.CRM_TOAST('Informe o título da atividade.', 'warn'); return; }
    if (!mClienteId)     { window.CRM_TOAST('Selecione o cliente.', 'warn'); return; }
    if (!mNotas.trim())  { window.CRM_TOAST('Informe as notas da atividade.', 'warn'); return; }
    // K-D11: avisa se atividade ficará órfã (cliente tem ops ativas mas nenhuma vinculada)
    if (!editingAtiv && !mOpId && opsDoCliente.length > 0) {
      var _ok = await window.CRM_CONFIRM(
        'Esta atividade não está vinculada a nenhuma oportunidade. O cliente tem ' + opsDoCliente.length + ' op(s) ativa(s). Continuar mesmo assim?',
        { danger: false, confirmLabel: 'Continuar sem vincular' }
      );
      if (!_ok) return;
    }
    setSaving(true);
    try {
      var vendId = mVendId || (userLogado ? userLogado.id : '');
      if (editingAtiv) {
        // Edição: atualiza em memória + PATCH SP
        var campos = {
          tipo: mTipo, titulo: mTitulo.trim(), cliente_id: mClienteId, op_id: mOpId||'',
          vendedor_id: vendId, data: mData ? new Date(mData).toISOString() : new Date().toISOString(),
          duracao: mDuracao||'—', resultado: mResultado, notas: mNotas.trim()
        };
        Object.assign(editingAtiv, campos);
        if (typeof CRM_API !== 'undefined' && editingAtiv._spId) {
          CRM_API.updateItem('Atividades', editingAtiv._spId, campos)
            .catch(function(e){ console.warn('[CRM] atualizar atividade:', e.message); });
        }
        fecharModal();
        setTick(function(t){ return t+1; });
        if (typeof window.showCrmToast === 'function') window.showCrmToast('Atividade atualizada', 'ok');
      } else {
        // Novo registro
        var novaAtiv = {
          id: 'AT-'+Date.now(), tipo: mTipo, titulo: mTitulo.trim(),
          cliente_id: mClienteId, op_id: mOpId||'', vendedor_id: vendId,
          data: mData ? new Date(mData).toISOString() : new Date().toISOString(),
          duracao: mDuracao||'—', resultado: mResultado, notas: mNotas.trim()
        };
        if (typeof CRM_API !== 'undefined') {
          var created = await CRM_API.addItem('Atividades', novaAtiv);
          if (created && created.id) novaAtiv._spId = String(created.id);
        }
        CRM_DATA.atividades.unshift(novaAtiv);
        // Se for visita presencial: atualiza ultima_visita e calcula proxima por regra de classe
        if (novaAtiv.tipo === 'visita') {
          var cliVis = CRM_DATA.getCliente(novaAtiv.cliente_id);
          var ultimaIso = new Date(novaAtiv.data).toISOString().slice(0, 10);
          var proximaCalc = _calcProximaVisita(cliVis && cliVis.classe, novaAtiv.data);
          _gravarDatasVisita(cliVis, ultimaIso, proximaCalc);
        }
        fecharModal();
        setTick(function(t){ return t+1; });
        _sugerirProximaAcao(novaAtiv);
      }
    } catch(e) { window.CRM_TOAST('Erro ao salvar: '+e.message, 'error'); setSaving(false); }
  }

  async function _sugerirProximaAcao(atv) {
    var apiKey = (window.BAUKO_AUTH && window.BAUKO_AUTH.ANTHROPIC_API_KEY) || '';
    if (!apiKey) return;
    setShowSugestaoModal(true);
    setSugestaoLoading(true);
    setSugestaoResumo('');
    setSugestaoOpcoes([]);
    setSugestaoCtx({ vendedor_id: atv.vendedor_id, cliente_id: atv.cliente_id, op_id: atv.op_id||'' });
    try {
      var prompt = 'Você é um assistente de CRM de máquinas pesadas (Komatsu/Bauko). Analise a atividade abaixo e responda em JSON.\n\nTipo: ' + (ATV_LABEL[atv.tipo]||atv.tipo) + '\nResultado: ' + atv.resultado + '\nNotas: "' + atv.notas + '"\n\nRetorne APENAS JSON válido, sem markdown:\n{\n  "resumo": "frase curta e natural (máx 120 chars) resumindo o que aconteceu e convidando a agendar o próximo passo",\n  "sugestoes": [\n    {"tipo":"...","titulo":"...","dias":3},\n    {"tipo":"...","titulo":"...","dias":7},\n    {"tipo":"...","titulo":"...","dias":14}\n  ]\n}\n\nRegras:\n- tipo deve ser exatamente um de: ligacao, whatsapp, visita, email, followup, reuniao\n- dias: inteiro 1-30\n- titulo: frase curta e específica ao contexto (máx 70 chars)\n- as 3 sugestões devem ter tipos e prazos diferentes entre si\n- resumo: linguagem natural, tom consultivo, primeira pessoa do plural';
      var resp = await fetch('https://api.anthropic.com/v1/messages', {
        method: 'POST',
        headers: {
          'x-api-key': apiKey,
          'anthropic-version': '2023-06-01',
          'anthropic-dangerous-direct-browser-access': 'true',
          'content-type': 'application/json'
        },
        body: JSON.stringify({ model: 'claude-haiku-4-5', max_tokens: 400,
          messages: [{ role: 'user', content: prompt }] })
      });
      if (!resp.ok) throw new Error('HTTP ' + resp.status);
      var json = await resp.json();
      var text = (json.content && json.content[0] && json.content[0].text) || '';
      var match = text.match(/\{[\s\S]*\}/);
      if (!match) throw new Error('JSON não encontrado');
      var parsed = JSON.parse(match[0]);
      var TIPOS_VALIDOS = ['ligacao','whatsapp','visita','email','followup','reuniao'];
      var pad = function(n){ return String(n).padStart(2,'0'); };
      var opcoes = (parsed.sugestoes||[]).slice(0,3).map(function(s){
        if (TIPOS_VALIDOS.indexOf(s.tipo)===-1) s.tipo = 'ligacao';
        var dt = new Date();
        dt.setDate(dt.getDate() + Math.max(1, Math.min(30, parseInt(s.dias)||3)));
        var iso = dt.getFullYear()+'-'+pad(dt.getMonth()+1)+'-'+pad(dt.getDate())+'T09:00';
        return { tipo: s.tipo, titulo: String(s.titulo||'').slice(0,70), data_iso: iso };
      });
      setSugestaoResumo(String(parsed.resumo||'').slice(0,160));
      setSugestaoOpcoes(opcoes);
      setSugestaoSel(0);
      if (opcoes.length > 0) {
        setSugestaoEditTitulo(opcoes[0].titulo);
        setSugestaoEditData(opcoes[0].data_iso);
      }
    } catch(e) {
      console.warn('[CRM] sugestão IA:', e.message);
      setShowSugestaoModal(false);
    } finally {
      setSugestaoLoading(false);
    }
  }

  function _selecionarOpcaoSugestao(idx) {
    setSugestaoSel(idx);
    var op = sugestaoOpcoes[idx];
    if (op) { setSugestaoEditTitulo(op.titulo); setSugestaoEditData(op.data_iso); }
  }

  function fecharSugestaoModal() {
    setShowSugestaoModal(false);
    setSugestaoLoading(false);
  }

  function aceitarSugestaoModal() {
    if (!sugestaoCtx) return;
    var opcSel = sugestaoOpcoes[sugestaoSel];
    if (!opcSel) return;
    var novaAg = {
      id: 'AG-'+Date.now(),
      tipo: opcSel.tipo,
      titulo: sugestaoEditTitulo.trim() || opcSel.titulo,
      cliente_id: sugestaoCtx.cliente_id, op_id: sugestaoCtx.op_id,
      vendedor_id: sugestaoCtx.vendedor_id,
      data: new Date(sugestaoEditData || opcSel.data_iso).toISOString(),
      duracao: '', local: ''
    };
    if (typeof CRM_API !== 'undefined') {
      CRM_API.addItem('Agenda', novaAg).catch(function(e){ console.warn('[CRM] agenda add:', e.message); });
      // Criar evento no Outlook — não bloqueia o fluxo
      CRM_API.criarEventoOutlook(novaAg).catch(function(e){ console.warn('[CRM] outlook:', e.message); });
    }
    CRM_DATA.agenda.push(novaAg);
    // Se o usuário agendou uma visita → sobrescreve proxima_visita com a data escolhida
    if (opcSel.tipo === 'visita' && sugestaoCtx && sugestaoCtx.cliente_id) {
      var cliAg = CRM_DATA.getCliente(sugestaoCtx.cliente_id);
      var proxIsoAg = new Date(sugestaoEditData || opcSel.data_iso).toISOString().slice(0, 10);
      _gravarDatasVisita(cliAg, null, proxIsoAg);
    }
    setShowSugestaoModal(false);
    if (typeof window.showCrmToast === 'function') window.showCrmToast('Próxima atividade agendada · Outlook ✓', 'ok');
  }

  function abrirModalEditar(at) {
    setEditingAtiv(at);
    setMTipo(at.tipo||'ligacao');
    setMClienteId(at.cliente_id||'');
    var cli = CRM_DATA.getCliente(at.cliente_id);
    setMClienteSearch(cli ? cli.razao : '');
    setMClienteOpen(false);
    setMOpId(at.op_id||'');
    setMTitulo(at.titulo||'');
    var dtLocal = at.data ? (function(){
      var d = new Date(at.data);
      var pad = function(n){ return String(n).padStart(2,'0'); };
      return d.getFullYear()+'-'+pad(d.getMonth()+1)+'-'+pad(d.getDate())+'T'+pad(d.getHours())+':'+pad(d.getMinutes());
    })() : _atvNowISO();
    setMData(dtLocal);
    setMDuracao(at.duracao && at.duracao!=='—' ? at.duracao : '');
    setMResultado(at.resultado||'positivo');
    setMNotas(at.notas||'');
    setMVendId(at.vendedor_id||'');
    setShowModal(true);
  }

  async function excluirAtividade(at) {
    if (!await window.CRM_CONFIRM('Excluir esta atividade? A ação não pode ser desfeita.', { danger: true, confirmLabel: 'Excluir' })) return;
    var idx = CRM_DATA.atividades.indexOf(at);
    if (idx !== -1) CRM_DATA.atividades.splice(idx, 1);
    if (typeof CRM_API !== 'undefined' && at._spId) {
      CRM_API.deleteItem('Atividades', at._spId).catch(function(e){ console.warn('[CRM] excluir atividade:', e.message); });
    }
    setTick(function(t){ return t+1; });
    if (typeof window.showCrmToast === 'function') window.showCrmToast('Atividade excluída', 'ok');
  }

  var clientes  = CRM_DATA.getClientesAtivos ? CRM_DATA.getClientesAtivos() : CRM_DATA.clientes.filter(function(c){ return c.status!=='inativo'; });
  var vendedores = CRM_DATA.usuarios;

  return (
    <div data-screen-label="05 Atividades">

      {/* ── KPI cards (6 tipos) ──────────────────────── */}
      <div className="kpi-grid" style={{ gridTemplateColumns:'repeat(6,1fr)', marginBottom:18, gap:10 }}>
        {ATV_TIPOS.map(function(tipo){
          var count = ativsKpi.filter(function(a){ return a.tipo===tipo; }).length;
          var ativo = tipoFilter===tipo;
          return (
            <div key={tipo} className="kpi-card"
              style={{ cursor:'pointer', borderTop: ativo ? '3px solid '+ATV_COLOR[tipo] : '3px solid transparent' }}
              onClick={function(){ setTipoFilter(tipoFilter===tipo?'todas':tipo); }}>
              <div style={{ display:'flex', gap:6, alignItems:'center', marginBottom:8 }}>
                <i data-lucide={ATV_ICON[tipo]} style={{ width:14, height:14, color:ATV_COLOR[tipo], flexShrink:0 }}></i>
                <div className="kpi-card__eyebrow" style={{ marginBottom:0 }}>{ATV_LABEL[tipo]}</div>
              </div>
              <div className="kpi-card__v" style={{ fontSize:30 }}>{count}</div>
            </div>
          );
        })}
      </div>

      {/* ── Toolbar ──────────────────────────────────── */}
      <div style={{ display:'flex', gap:8, marginBottom:16, flexWrap:'wrap', alignItems:'center' }}>
        <div className="tb__search" style={{ flex:'1 1 200px', minWidth:160 }}>
          <i data-lucide="search"></i>
          <input placeholder="Buscar título, cliente, notas..." value={search}
            onChange={function(e){ setSearch(e.target.value); }} />
        </div>
        <div className="tabs" style={{ marginBottom:0 }}>
          {[['todas','Todas'],['hoje','Hoje'],['semana','Esta semana'],['mes','Este mês']].map(function(p){
            return <button key={p[0]} className={periodoFilter===p[0]?'is-on':''} onClick={function(){ setPeriodoFilter(p[0]); }}>{p[1]}</button>;
          })}
        </div>
        {vendedores.length > 1 && podeVerTodasAtv && (
          <select value={vendFilter} onChange={function(e){ setVendFilter(e.target.value); }} className="inp" style={{ width:'auto' }}>
            <option value="todos">Todos os vendedores</option>
            {vendedores.map(function(v){ return <option key={v.id} value={v.id}>{v.nome}</option>; })}
          </select>
        )}
        <button className="btn btn-primary" onClick={abrirModal} style={{ whiteSpace:'nowrap' }}>
          <i data-lucide="plus" style={{ width:14, height:14 }}></i>
          Nova atividade
        </button>
      </div>

      {/* ── Lista agrupada ───────────────────────────── */}
      <Panel title="Registro de atividades" meta={ativs.length+' registro'+(ativs.length!==1?'s':'')}>
        {ativs.length === 0 ? (
          <div style={{ padding:'40px 0', textAlign:'center', color:'var(--tx-3)' }}>
            <i data-lucide="inbox" style={{ width:28, height:28, display:'block', margin:'0 auto 10px' }}></i>
            Nenhuma atividade encontrada para os filtros aplicados.
          </div>
        ) : ATV_BUCKETS.map(function(bucket){
          var lista = grupos[bucket];
          if (!lista || !lista.length) return null;
          return (
            <div key={bucket}>
              <div style={{ padding:'12px 0 6px', font:'600 11px/1 var(--ff-mono)', color:'var(--tx-3)',
                textTransform:'uppercase', letterSpacing:'.06em',
                borderBottom:'1px solid var(--border)', marginBottom:2 }}>
                {bucket} <span style={{ fontWeight:400, opacity:.7 }}>· {lista.length}</span>
              </div>
              {lista.map(function(at, i){
                var cli  = CRM_DATA.getCliente(at.cliente_id);
                var vend = CRM_DATA.getUsuario(at.vendedor_id);
                var op   = CRM_DATA.getOp(at.op_id);
                return (
                  <div key={at.id}
                    onClick={function(e){ if (e.target.closest('button,[data-action]')) return; abrirModalEditar(at); }}
                    style={{ display:'flex', gap:14, alignItems:'flex-start', padding:'13px 0', cursor:'pointer',
                    borderBottom: i<lista.length-1 ? '1px solid var(--border)' : 'none' }}>
                    <span style={{ width:30, height:30, borderRadius:'50%', flexShrink:0,
                      background:'var(--surface-2)', border:'1px solid var(--border)',
                      display:'inline-flex', alignItems:'center', justifyContent:'center',
                      color: ATV_COLOR[at.tipo]||'var(--tx-3)' }}>
                      <i data-lucide={ATV_ICON[at.tipo]||'activity'} style={{ width:14, height:14 }}></i>
                    </span>
                    <div style={{ flex:1 }}>
                      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap:12, flexWrap:'wrap' }}>
                        <div>
                          <div style={{ font:'500 14px/1.2 var(--ff-body)', color:'var(--tx)' }}>{at.titulo}</div>
                          <div style={{ font:'400 11px/1 var(--ff-mono)', color:'var(--tx-3)', marginTop:4, display:'flex', gap:8, flexWrap:'wrap' }}>
                            {cli && <span>{(cli.razao||'').split(' ').slice(0,3).join(' ')}</span>}
                            {op && <><span>·</span><span style={{ cursor:'pointer', color:'var(--info)' }} onClick={function(){ onNav('oportunidade', op.id); }}>{at.op_id}</span></>}
                            {vend && <><span>·</span><span>{(vend.nome||'').split(' ')[0]}</span></>}
                            {at.duracao && at.duracao!=='—' && <><span>·</span><span>{at.duracao}</span></>}
                          </div>
                        </div>
                        <div style={{ display:'flex', gap:8, alignItems:'center', flexShrink:0 }}>
                          <Badge status={at.resultado} />
                          <span style={{ font:'400 11px/1 var(--ff-mono)', color:'var(--tx-3)' }}>{fDateTime(at.data)}</span>
                          <button className="btn btn-ghost" style={{ padding:'3px 6px', minWidth:'unset' }}
                            title="Editar" onClick={function(){ abrirModalEditar(at); }}>
                            <i data-lucide="pencil" style={{ width:12, height:12 }}></i>
                          </button>
                          <button className="btn btn-ghost" style={{ padding:'3px 6px', minWidth:'unset', color:'var(--danger)' }}
                            title="Excluir" onClick={function(){ excluirAtividade(at); }}>
                            <i data-lucide="trash-2" style={{ width:12, height:12 }}></i>
                          </button>
                        </div>
                      </div>
                      {at.notas && (
                        <div style={{ marginTop:8, font:'400 12px/1.5 var(--ff-body)', color:'var(--tx-2)',
                          background:'var(--surface-2)', borderRadius:'var(--r-sm)',
                          padding:'8px 12px', borderLeft:'3px solid var(--border-2)' }}>
                          {at.notas}
                        </div>
                      )}
                    </div>
                  </div>
                );
              })}
            </div>
          );
        })}
      </Panel>

      {/* ── Modal nova atividade ─────────────────────── */}
      <Modal open={showModal} onClose={fecharModal} title={editingAtiv ? "Editar atividade" : "Registrar atividade"} wide>
        <div className="form-grid-2" style={{ marginTop:16 }}>

          {/* Tipo — grid 2×3 com ícone + label */}
          <FormField label="Tipo" col={2}>
            <div style={{ display:'grid', gridTemplateColumns:'repeat(3,1fr)', border:'1px solid var(--border-2)', borderRadius:'var(--r-md)', overflow:'hidden' }}>
              {ATV_TIPOS.map(function(t, i){
                return (
                  <div key={t}
                    className={'tb-tog '+(mTipo===t?'on':'')}
                    style={{
                      display:'flex', alignItems:'center', justifyContent:'center', gap:5,
                      borderRight: (i%3!==2) ? '1px solid var(--border-2)' : 'none',
                      borderBottom: i<3 ? '1px solid var(--border-2)' : 'none',
                    }}
                    onClick={function(){ setMTipo(t); }}>
                    <i data-lucide={ATV_ICON[t]} style={{ width:13, height:13 }}></i>
                    {ATV_LABEL[t]}
                  </div>
                );
              })}
            </div>
          </FormField>

          {/* Cliente — autocomplete filter-as-you-type */}
          <div className="f" style={{ gridColumn:'span 2', position:'relative' }}>
            <label>Cliente</label>
            <div style={{ position:'relative' }}>
              <i data-lucide="search" style={{ position:'absolute', left:10, top:'50%', transform:'translateY(-50%)', width:14, height:14, color:'var(--tx-3)', pointerEvents:'none' }}></i>
              <input
                className="inp"
                style={{ paddingLeft:32 }}
                placeholder="Digite para filtrar clientes..."
                value={mClienteSearch}
                autoComplete="off"
                onChange={function(e){
                  var v = e.target.value;
                  setMClienteSearch(v);
                  setMClienteOpen(true);
                  if (!v) { setMClienteId(''); setMOpId(''); }
                }}
                onFocus={function(){ if (mClienteSearch) setMClienteOpen(true); }}
                onBlur={function(){ setTimeout(function(){ setMClienteOpen(false); }, 160); }}
              />
            </div>
            {mClienteOpen && mClienteSearch.length >= 1 && (function(){
              var term = _atvNorm(mClienteSearch);
              var hits = clientes.filter(function(c){ return _atvNorm(c.razao||'').includes(term); }).slice(0,8);
              if (!hits.length) return null;
              return (
                <div style={{
                  position:'absolute', top:'100%', left:0, right:0, zIndex:200,
                  background:'var(--surface)', border:'1px solid var(--border)',
                  borderRadius:'var(--r-md)', boxShadow:'0 8px 24px rgba(0,0,0,.12)',
                  maxHeight:240, overflowY:'auto',
                }}>
                  {hits.map(function(c){
                    return (
                      <div key={c.id}
                        onMouseDown={function(){
                          setMClienteId(c.id);
                          setMClienteSearch(c.razao);
                          setMClienteOpen(false);
                          setMOpId('');
                        }}
                        style={{
                          padding:'10px 14px', cursor:'pointer', fontSize:13,
                          borderBottom:'1px solid var(--border)', color:'var(--tx)',
                        }}
                        onMouseEnter={function(e){ e.currentTarget.style.background='var(--surface-2)'; }}
                        onMouseLeave={function(e){ e.currentTarget.style.background='transparent'; }}
                      >
                        <div style={{ fontWeight:500 }}>{c.razao}</div>
                        {c.cidade && <div style={{ fontSize:11, color:'var(--tx-3)', marginTop:2 }}>{c.cidade}{c.uf?' · '+c.uf:''}</div>}
                      </div>
                    );
                  })}
                </div>
              );
            })()}
          </div>

          <FormField label="Oportunidade" hint="opcional" col={2}>
            <select value={mOpId} onChange={function(e){ setMOpId(e.target.value); }} className="inp" disabled={!mClienteId}>
              <option value="">Nenhuma</option>
              {opsDoCliente.map(function(o){ return <option key={o.id} value={o.id}>{o.titulo||o.id}</option>; })}
            </select>
          </FormField>

          <FormField label="Título" col={2}>
            <input value={mTitulo} onChange={function(e){ setMTitulo(e.target.value); }} className="inp"
              placeholder="Ex: Ligação de follow-up sobre proposta enviada..." />
          </FormField>

          <FormField label="Data e hora">
            <input type="datetime-local" value={mData} onChange={function(e){ setMData(e.target.value); }} className="inp" />
          </FormField>

          <FormField label="Duração" hint="ex: 30 min">
            <input value={mDuracao} onChange={function(e){ setMDuracao(e.target.value); }} className="inp" placeholder="30 min" />
          </FormField>

          {isGerente ? (
            <FormField label="Vendedor">
              <select value={mVendId} onChange={function(e){ setMVendId(e.target.value); }} className="inp">
                <option value="">Selecione...</option>
                {vendedores.map(function(v){ return <option key={v.id} value={v.id}>{v.nome}</option>; })}
              </select>
            </FormField>
          ) : <div></div>}

          <FormField label="Notas" hint="obrigatório" col={2}>
            <textarea value={mNotas} onChange={function(e){ setMNotas(e.target.value); }} className="inp"
              rows={3} placeholder="O que foi discutido, observações relevantes..."
              style={{ resize:'vertical' }} />
          </FormField>
        </div>

        {/* ── Resultado ────────────────────────────── */}
        <div style={{ marginTop:16, paddingTop:14, borderTop:'1px solid var(--border)' }}>
          <div className="f">
            <label>Resultado</label>
            <div className="tog">
              {[['positivo','😊 Positivo'],['neutro','😐 Neutro'],['negativo','😞 Negativo']].map(function(item){
                return <div key={item[0]} className={'tb '+(mResultado===item[0]?'on':'')}
                  onClick={function(){ setMResultado(item[0]); }}>{item[1]}</div>;
              })}
            </div>
          </div>
        </div>

        {/* ── Rodapé ───────────────────────────────── */}
        <div style={{ display:'flex', justifyContent:'flex-end', gap:10, marginTop:24, paddingTop:16, borderTop:'1px solid var(--border)' }}>
          <button className="btn btn-secondary" onClick={fecharModal} disabled={saving}>Cancelar</button>
          <button className="btn btn-primary" onClick={salvarAtividade} disabled={saving}>
            {saving ? 'Salvando...' : (editingAtiv ? 'Salvar alterações' : 'Salvar atividade')}
          </button>
        </div>
      </Modal>

      {/* ── Modal IA: Próxima Atividade ─────────────── */}
      <Modal open={showSugestaoModal} onClose={fecharSugestaoModal} title="Próxima Atividade" wide>
        {sugestaoLoading ? (
          <div style={{ padding:'40px 0', textAlign:'center' }}>
            <div style={{ display:'inline-flex', alignItems:'center', gap:10, color:'var(--tx-2)', font:'500 14px/1.4 var(--ff-body)' }}>
              <i data-lucide="sparkles" style={{ width:18, height:18, color:'var(--grn)' }}></i>
              A IA está analisando a atividade registrada...
            </div>
          </div>
        ) : (
          <div style={{ marginTop:16 }}>
            {/* Resumo IA */}
            {sugestaoResumo && (
              <div style={{ background:'var(--surface-2)', borderRadius:'var(--r-md)', padding:'12px 16px',
                borderLeft:'3px solid var(--grn)', marginBottom:20,
                font:'400 13px/1.6 var(--ff-body)', color:'var(--tx-2)' }}>
                <div style={{ display:'flex', gap:8, alignItems:'flex-start' }}>
                  <i data-lucide="sparkles" style={{ width:15, height:15, color:'var(--grn)', flexShrink:0, marginTop:2 }}></i>
                  <span>{sugestaoResumo}</span>
                </div>
              </div>
            )}

            {/* 3 opções de sugestão */}
            {sugestaoOpcoes.length > 0 && (
              <div style={{ marginBottom:20 }}>
                <div style={{ font:'600 11px/1 var(--ff-body)', textTransform:'uppercase', letterSpacing:'.06em',
                  color:'var(--tx-3)', marginBottom:10 }}>Escolha uma sugestão</div>
                <div style={{ display:'flex', flexDirection:'column', gap:8 }}>
                  {sugestaoOpcoes.map(function(op, idx){
                    var sel = sugestaoSel === idx;
                    return (
                      <div key={idx}
                        onClick={function(){ _selecionarOpcaoSugestao(idx); }}
                        style={{
                          display:'flex', alignItems:'center', gap:12, padding:'12px 14px',
                          border: sel ? '2px solid var(--grn)' : '1px solid var(--border)',
                          borderRadius:'var(--r-md)', cursor:'pointer',
                          background: sel ? 'color-mix(in srgb,var(--grn) 6%,var(--surface))' : 'var(--surface)',
                          transition:'border .15s, background .15s',
                        }}>
                        <div style={{ width:32, height:32, borderRadius:'50%', flexShrink:0,
                          background: sel ? 'color-mix(in srgb,var(--grn) 15%,var(--surface))' : 'var(--surface-2)',
                          border: sel ? '2px solid var(--grn)' : '1px solid var(--border)',
                          display:'flex', alignItems:'center', justifyContent:'center',
                          color: ATV_COLOR[op.tipo]||'var(--grn)' }}>
                          <i data-lucide={ATV_ICON[op.tipo]||'calendar'} style={{ width:14, height:14 }}></i>
                        </div>
                        <div style={{ flex:1 }}>
                          <div style={{ font:'500 13px/1.2 var(--ff-body)', color:'var(--tx)' }}>{op.titulo}</div>
                          <div style={{ font:'400 11px/1 var(--ff-mono)', color:'var(--tx-3)', marginTop:3 }}>
                            {ATV_LABEL[op.tipo]||op.tipo} · {new Date(op.data_iso).toLocaleDateString('pt-BR',{ weekday:'short', day:'2-digit', month:'short' })}
                          </div>
                        </div>
                        {sel && <i data-lucide="check-circle-2" style={{ width:16, height:16, color:'var(--grn)', flexShrink:0 }}></i>}
                      </div>
                    );
                  })}
                </div>
              </div>
            )}

            {/* Campos editáveis */}
            {sugestaoOpcoes.length > 0 && (
              <div className="form-grid-2" style={{ marginBottom:8 }}>
                <FormField label="Descrição" col={2}>
                  <input value={sugestaoEditTitulo} onChange={function(e){ setSugestaoEditTitulo(e.target.value); }}
                    className="inp" placeholder="Edite o título da próxima atividade..." />
                </FormField>
                <FormField label="Data e hora" col={2}>
                  <input type="datetime-local" value={sugestaoEditData}
                    onChange={function(e){ setSugestaoEditData(e.target.value); }} className="inp" />
                </FormField>
              </div>
            )}

            {/* Rodapé */}
            <div style={{ display:'flex', justifyContent:'flex-end', gap:10, marginTop:24, paddingTop:16, borderTop:'1px solid var(--border)' }}>
              <button className="btn btn-secondary" onClick={fecharSugestaoModal}>Não agora</button>
              <button className="btn btn-primary" onClick={aceitarSugestaoModal} disabled={!sugestaoOpcoes.length}>
                <i data-lucide="calendar-plus" style={{ width:13, height:13 }}></i>
                Agendar →
              </button>
            </div>
          </div>
        )}
      </Modal>

    </div>
  );
}
window.Atividades = Atividades;

// ── AtividadeModalGlobal ── exposto via window.CRM_OPEN_ATIVIDADE(opts) ──────
// opts: { clienteId, clienteNome, tipo, titulo, data, vendedorId, opId, agendaEvtRef, onSalvo }
// agendaEvtRef: objeto agenda in-memory (tem ._spId) — marcado realizada ao salvar
function AtividadeModalGlobal() {
  var [open,           setOpen]           = React.useState(false);
  var [mTipo,          setMTipo]          = React.useState('visita');
  var [mClienteId,     setMClienteId]     = React.useState('');
  var [mClienteSearch, setMClienteSearch] = React.useState('');
  var [mClienteOpen,   setMClienteOpen]   = React.useState(false);
  var [mOpId,          setMOpId]          = React.useState('');
  var [mTitulo,        setMTitulo]        = React.useState('');
  var [mData,          setMData]          = React.useState('');
  var [mDuracao,       setMDuracao]       = React.useState('');
  var [mResultado,     setMResultado]     = React.useState('positivo');
  var [mNotas,         setMNotas]         = React.useState('');
  var [mVendId,        setMVendId]        = React.useState('');
  var [saving,         setSaving]         = React.useState(false);
  // Suggestion modal state
  var [showSugestao,   setShowSugestao]   = React.useState(false);
  var [sugestLoading,  setSugestLoading]  = React.useState(false);
  var [sugestResumo,   setSugestResumo]   = React.useState('');
  var [sugestOpcoes,   setSugestOpcoes]   = React.useState([]);
  var [sugestSel,      setSugestSel]      = React.useState(0);
  var [sugestEditTit,  setSugestEditTit]  = React.useState('');
  var [sugestEditDt,   setSugestEditDt]   = React.useState('');
  var [sugestCtx,      setSugestCtx]      = React.useState(null);
  // Refs — não disparam re-render
  var _agEvtRef  = React.useRef(null);
  var _onSalvRef = React.useRef(null);

  // Expor função global
  React.useEffect(function() {
    window.CRM_OPEN_ATIVIDADE = function(opts) {
      opts = opts || {};
      var emailL = (window.CRM_USER && window.CRM_USER.email) || '';
      var uLogado = CRM_DATA.usuarios.find(function(u){ return (u.email||'').toLowerCase()===emailL; });
      setMTipo(opts.tipo || 'visita');
      setMClienteId(opts.clienteId || '');
      var cliPre = opts.clienteId ? CRM_DATA.getCliente(opts.clienteId) : null;
      setMClienteSearch(opts.clienteNome || (cliPre ? cliPre.razao : ''));
      setMClienteOpen(false);
      setMOpId(opts.opId || '');
      setMTitulo(opts.titulo || '');
      var dtV = opts.data
        ? (function(){
            var d = new Date(opts.data);
            var pad = function(n){ return String(n).padStart(2,'0'); };
            return d.getFullYear()+'-'+pad(d.getMonth()+1)+'-'+pad(d.getDate())+'T'+pad(d.getHours())+':'+pad(d.getMinutes());
          })()
        : _atvNowISO();
      setMData(dtV);
      setMDuracao('');
      setMResultado('positivo');
      setMNotas('');
      setMVendId(opts.vendedorId || (uLogado ? uLogado.id : ''));
      _agEvtRef.current  = opts.agendaEvtRef || null;
      _onSalvRef.current = opts.onSalvo      || null;
      setSaving(false);
      setOpen(true);
    };
  }, []);

  // Re-init Lucide após abrir
  React.useEffect(function() {
    if ((open || showSugestao) && typeof lucide !== 'undefined') {
      setTimeout(function(){ lucide.createIcons(); }, 0);
    }
  }, [open, showSugestao]);

  // Derivados (computados a cada render) — identidade efetiva via CRM_USER
  var emailLogado = (window.CRM_USER && window.CRM_USER.email) || '';
  var userLogado  = CRM_DATA.usuarios.find(function(u){ return (u.email||'').toLowerCase()===emailLogado; });
  var isGerente   = !!(window.CRM_USER && window.CRM_USER.isGerente);
  var clientes    = CRM_DATA.getClientesAtivos ? CRM_DATA.getClientesAtivos() : CRM_DATA.clientes.filter(function(c){ return c.status!=='inativo'; });
  var vendedores  = CRM_DATA.usuarios;
  var opsDoCli    = mClienteId ? CRM_DATA.oportunidades.filter(function(o){ return o.cliente_id===mClienteId && !o.cancelada_em; }) : [];

  var CADENCIA_GL = { 'Grande conta':7, 'A':14, 'B':30, 'C':60, 'D':null };
  function _calcProxGL(classe, baseIso) {
    var dias = CADENCIA_GL[classe];
    if (!dias) return null;
    var base = baseIso ? new Date(baseIso) : new Date();
    base.setDate(base.getDate() + dias);
    return base.toISOString().slice(0, 10);
  }
  function _gravarDatasGL(cli, ultimaIso, proximaIso) {
    if (!cli || !cli._spId) return;
    var upd = {};
    if (ultimaIso)  { cli.ultima_visita  = ultimaIso;  upd.ultima_visita  = ultimaIso;  }
    if (proximaIso) { cli.proxima_visita = proximaIso; upd.proxima_visita = proximaIso; }
    if (Object.keys(upd).length)
      CRM_API.updateItem('Clientes', cli._spId, upd)
        .catch(function(e){ console.warn('[CRM] datas_visita:', e.message); });
  }

  function fecharModal() { setOpen(false); setSaving(false); }

  // K-D6: dispara evento global ao salvar atividade — outras telas sincronizam
  function _dispatchAtvAdded(atv) {
    try {
      window.dispatchEvent(new CustomEvent('crm_atividade_added', { detail: atv }));
    } catch(e) {}
  }

  async function salvar() {
    if (!mTitulo.trim()) { window.CRM_TOAST('Informe o título da atividade.', 'warn'); return; }
    if (!mClienteId)     { window.CRM_TOAST('Selecione o cliente.', 'warn'); return; }
    if (!mNotas.trim())  { window.CRM_TOAST('Informe as notas da atividade.', 'warn'); return; }
    // K-D11: avisa se atividade ficará órfã (cliente com ops mas nenhuma vinculada)
    if (!mOpId && opsDoCli.length > 0) {
      var ok = await window.CRM_CONFIRM(
        'Esta atividade não está vinculada a nenhuma oportunidade. O cliente tem ' + opsDoCli.length + ' op(s) ativa(s). Continuar mesmo assim?',
        { danger: false, confirmLabel: 'Continuar sem vincular' }
      );
      if (!ok) return;
    }
    setSaving(true);
    try {
      var vendId = mVendId || (userLogado ? userLogado.id : '');
      var novaAtiv = {
        id: 'AT-'+Date.now(), tipo: mTipo, titulo: mTitulo.trim(),
        cliente_id: mClienteId, op_id: mOpId||'', vendedor_id: vendId,
        data: mData ? new Date(mData).toISOString() : new Date().toISOString(),
        duracao: mDuracao||'—', resultado: mResultado, notas: mNotas.trim()
      };
      if (typeof CRM_API !== 'undefined') {
        var created = await CRM_API.addItem('Atividades', novaAtiv);
        if (created && created.id) novaAtiv._spId = String(created.id);
      }
      CRM_DATA.atividades.unshift(novaAtiv);
      // K-D6: notifica outras telas (Tela Atividades, Oportunidade, etc.)
      _dispatchAtvAdded(novaAtiv);
      // Datas de visita no cliente
      if (novaAtiv.tipo === 'visita') {
        var cliVis  = CRM_DATA.getCliente(novaAtiv.cliente_id);
        var ultIso  = new Date(novaAtiv.data).toISOString().slice(0, 10);
        var proxCalc = _calcProxGL(cliVis && cliVis.classe, novaAtiv.data);
        _gravarDatasGL(cliVis, ultIso, proxCalc);
      }
      // Marcar visita planejada como realizada
      var agEvt = _agEvtRef.current;
      if (agEvt && agEvt._spId) {
        agEvt.status_visita = 'realizada';
        CRM_API.updateItem('Agenda', agEvt._spId, { status_visita: 'realizada' })
          .catch(function(e){ console.warn('[CRM] agenda realizada:', e.message); });
      }
      // Callback
      if (typeof _onSalvRef.current === 'function') _onSalvRef.current(novaAtiv);
      fecharModal();
      if (typeof window.showCrmToast === 'function') window.showCrmToast('Atividade registrada', 'ok');
      _sugerirGlobal(novaAtiv);
    } catch(e) { window.CRM_TOAST('Erro ao salvar: '+e.message, 'error'); setSaving(false); }
  }

  async function _sugerirGlobal(atv) {
    var apiKey = (window.BAUKO_AUTH && window.BAUKO_AUTH.ANTHROPIC_API_KEY) || '';
    if (!apiKey) return;
    setShowSugestao(true);
    setSugestLoading(true);
    setSugestResumo('');
    setSugestOpcoes([]);
    setSugestCtx({ vendedor_id: atv.vendedor_id, cliente_id: atv.cliente_id, op_id: atv.op_id||'' });
    try {
      var prompt = 'Você é um assistente de CRM de máquinas pesadas (Komatsu/Bauko). Analise a atividade abaixo e responda em JSON.\n\nTipo: ' + (ATV_LABEL[atv.tipo]||atv.tipo) + '\nResultado: ' + atv.resultado + '\nNotas: "' + atv.notas + '"\n\nRetorne APENAS JSON válido, sem markdown:\n{\n  "resumo": "frase curta e natural (máx 120 chars) resumindo o que aconteceu e convidando a agendar o próximo passo",\n  "sugestoes": [\n    {"tipo":"...","titulo":"...","dias":3},\n    {"tipo":"...","titulo":"...","dias":7},\n    {"tipo":"...","titulo":"...","dias":14}\n  ]\n}\n\nRegras:\n- tipo deve ser exatamente um de: ligacao, whatsapp, visita, email, followup, reuniao\n- dias: inteiro 1-30\n- titulo: frase curta e específica ao contexto (máx 70 chars)\n- as 3 sugestões devem ter tipos e prazos diferentes entre si\n- resumo: linguagem natural, tom consultivo, primeira pessoa do plural';
      var resp = await fetch('https://api.anthropic.com/v1/messages', {
        method: 'POST',
        headers: {
          'x-api-key': apiKey,
          'anthropic-version': '2023-06-01',
          'anthropic-dangerous-direct-browser-access': 'true',
          'content-type': 'application/json'
        },
        body: JSON.stringify({ model: 'claude-haiku-4-5', max_tokens: 400,
          messages: [{ role: 'user', content: prompt }] })
      });
      if (!resp.ok) throw new Error('HTTP ' + resp.status);
      var json = await resp.json();
      var text = (json.content && json.content[0] && json.content[0].text) || '';
      var match = text.match(/\{[\s\S]*\}/);
      if (!match) throw new Error('JSON não encontrado');
      var parsed = JSON.parse(match[0]);
      var TIPOS_V = ['ligacao','whatsapp','visita','email','followup','reuniao'];
      var pad2 = function(n){ return String(n).padStart(2,'0'); };
      var opcoes = (parsed.sugestoes||[]).slice(0,3).map(function(s){
        if (TIPOS_V.indexOf(s.tipo)===-1) s.tipo = 'ligacao';
        var dt = new Date();
        dt.setDate(dt.getDate() + Math.max(1, Math.min(30, parseInt(s.dias)||3)));
        var iso = dt.getFullYear()+'-'+pad2(dt.getMonth()+1)+'-'+pad2(dt.getDate())+'T09:00';
        return { tipo: s.tipo, titulo: String(s.titulo||'').slice(0,70), data_iso: iso };
      });
      setSugestResumo(String(parsed.resumo||'').slice(0,160));
      setSugestOpcoes(opcoes);
      setSugestSel(0);
      if (opcoes.length > 0) { setSugestEditTit(opcoes[0].titulo); setSugestEditDt(opcoes[0].data_iso); }
    } catch(e) {
      console.warn('[CRM] sugestão IA:', e.message);
      setShowSugestao(false);
    } finally { setSugestLoading(false); }
  }

  function _selOpcaoSug(idx) {
    setSugestSel(idx);
    var op = sugestOpcoes[idx];
    if (op) { setSugestEditTit(op.titulo); setSugestEditDt(op.data_iso); }
  }

  function aceitarSugestao() {
    if (!sugestCtx) return;
    var opcSel = sugestOpcoes[sugestSel];
    if (!opcSel) return;
    var novaAg = {
      id: 'AG-'+Date.now(), tipo: opcSel.tipo,
      titulo: sugestEditTit.trim() || opcSel.titulo,
      cliente_id: sugestCtx.cliente_id, op_id: sugestCtx.op_id,
      vendedor_id: sugestCtx.vendedor_id,
      data: new Date(sugestEditDt || opcSel.data_iso).toISOString(),
      duracao: '', local: ''
    };
    if (typeof CRM_API !== 'undefined') {
      CRM_API.addItem('Agenda', novaAg).catch(function(e){ console.warn('[CRM] agenda add:', e.message); });
      CRM_API.criarEventoOutlook(novaAg).catch(function(e){ console.warn('[CRM] outlook:', e.message); });
    }
    CRM_DATA.agenda.push(novaAg);
    if (opcSel.tipo === 'visita' && sugestCtx && sugestCtx.cliente_id) {
      var cliAg2 = CRM_DATA.getCliente(sugestCtx.cliente_id);
      var proxIsoAg = new Date(sugestEditDt || opcSel.data_iso).toISOString().slice(0, 10);
      _gravarDatasGL(cliAg2, null, proxIsoAg);
    }
    setShowSugestao(false);
    if (typeof window.showCrmToast === 'function') window.showCrmToast('Próxima atividade agendada · Outlook ✓', 'ok');
  }

  return (
    <>
      {/* ── Modal principal: Registrar atividade ── */}
      <Modal open={open} onClose={fecharModal} title="Registrar atividade" wide>
        <div className="form-grid-2" style={{ marginTop:16 }}>

          {/* Tipo */}
          <FormField label="Tipo" col={2}>
            <div style={{ display:'grid', gridTemplateColumns:'repeat(3,1fr)', border:'1px solid var(--border-2)', borderRadius:'var(--r-md)', overflow:'hidden' }}>
              {ATV_TIPOS.map(function(t, i){
                return (
                  <div key={t}
                    className={'tb-tog '+(mTipo===t?'on':'')}
                    style={{
                      display:'flex', alignItems:'center', justifyContent:'center', gap:5,
                      borderRight: (i%3!==2) ? '1px solid var(--border-2)' : 'none',
                      borderBottom: i<3 ? '1px solid var(--border-2)' : 'none',
                    }}
                    onClick={function(){ setMTipo(t); }}>
                    <i data-lucide={ATV_ICON[t]} style={{ width:13, height:13 }}></i>
                    {ATV_LABEL[t]}
                  </div>
                );
              })}
            </div>
          </FormField>

          {/* Cliente autocomplete */}
          <div className="f" style={{ gridColumn:'span 2', position:'relative' }}>
            <label>Cliente</label>
            <div style={{ position:'relative' }}>
              <i data-lucide="search" style={{ position:'absolute', left:10, top:'50%', transform:'translateY(-50%)', width:14, height:14, color:'var(--tx-3)', pointerEvents:'none' }}></i>
              <input
                className="inp"
                style={{ paddingLeft:32 }}
                placeholder="Digite para filtrar clientes..."
                value={mClienteSearch}
                autoComplete="off"
                onChange={function(e){
                  var v = e.target.value;
                  setMClienteSearch(v);
                  setMClienteOpen(true);
                  if (!v) { setMClienteId(''); setMOpId(''); }
                }}
                onFocus={function(){ if (mClienteSearch) setMClienteOpen(true); }}
                onBlur={function(){ setTimeout(function(){ setMClienteOpen(false); }, 160); }}
              />
            </div>
            {mClienteOpen && mClienteSearch.length >= 1 && (function(){
              var term = _atvNorm(mClienteSearch);
              var hits = clientes.filter(function(c){ return _atvNorm(c.razao||'').includes(term); }).slice(0,8);
              if (!hits.length) return null;
              return (
                <div style={{
                  position:'absolute', top:'100%', left:0, right:0, zIndex:300,
                  background:'var(--surface)', border:'1px solid var(--border)',
                  borderRadius:'var(--r-md)', boxShadow:'0 8px 24px rgba(0,0,0,.12)',
                  maxHeight:240, overflowY:'auto',
                }}>
                  {hits.map(function(c){
                    return (
                      <div key={c.id}
                        onMouseDown={function(){
                          setMClienteId(c.id);
                          setMClienteSearch(c.razao);
                          setMClienteOpen(false);
                          setMOpId('');
                        }}
                        style={{
                          padding:'10px 14px', cursor:'pointer', fontSize:13,
                          borderBottom:'1px solid var(--border)', color:'var(--tx)',
                        }}
                        onMouseEnter={function(e){ e.currentTarget.style.background='var(--surface-2)'; }}
                        onMouseLeave={function(e){ e.currentTarget.style.background='transparent'; }}
                      >
                        <div style={{ fontWeight:500 }}>{c.razao}</div>
                        {c.cidade && <div style={{ fontSize:11, color:'var(--tx-3)', marginTop:2 }}>{c.cidade}{c.uf?' · '+c.uf:''}</div>}
                      </div>
                    );
                  })}
                </div>
              );
            })()}
          </div>

          {/* Oportunidade */}
          <FormField label="Oportunidade" hint="opcional" col={2}>
            <select value={mOpId} onChange={function(e){ setMOpId(e.target.value); }} className="inp" disabled={!mClienteId}>
              <option value="">Nenhuma</option>
              {opsDoCli.map(function(o){ return <option key={o.id} value={o.id}>{o.titulo||o.id}</option>; })}
            </select>
          </FormField>

          {/* Título */}
          <FormField label="Título" col={2}>
            <input value={mTitulo} onChange={function(e){ setMTitulo(e.target.value); }} className="inp"
              placeholder="Ex: Visita de acompanhamento pós-proposta..." />
          </FormField>

          {/* Data / Duração / Vendedor */}
          <FormField label="Data e hora">
            <input type="datetime-local" value={mData} onChange={function(e){ setMData(e.target.value); }} className="inp" />
          </FormField>

          <FormField label="Duração" hint="ex: 1h">
            <input value={mDuracao} onChange={function(e){ setMDuracao(e.target.value); }} className="inp" placeholder="1h" />
          </FormField>

          {isGerente ? (
            <FormField label="Vendedor" col={2}>
              <select value={mVendId} onChange={function(e){ setMVendId(e.target.value); }} className="inp">
                <option value="">Selecione...</option>
                {vendedores.map(function(v){ return <option key={v.id} value={v.id}>{v.nome}</option>; })}
              </select>
            </FormField>
          ) : null}

          {/* Notas */}
          <FormField label="Notas" hint="obrigatório" col={2}>
            <textarea value={mNotas} onChange={function(e){ setMNotas(e.target.value); }} className="inp"
              rows={3} placeholder="O que foi discutido, observações relevantes..."
              style={{ resize:'vertical' }} />
          </FormField>
        </div>

        {/* Resultado */}
        <div style={{ marginTop:16, paddingTop:14, borderTop:'1px solid var(--border)' }}>
          <div className="f">
            <label>Resultado</label>
            <div className="tog">
              {[['positivo','😊 Positivo'],['neutro','😐 Neutro'],['negativo','😞 Negativo']].map(function(item){
                return <div key={item[0]} className={'tb '+(mResultado===item[0]?'on':'')}
                  onClick={function(){ setMResultado(item[0]); }}>{item[1]}</div>;
              })}
            </div>
          </div>
        </div>

        {/* Rodapé */}
        <div style={{ display:'flex', justifyContent:'flex-end', gap:10, marginTop:24, paddingTop:16, borderTop:'1px solid var(--border)' }}>
          <button className="btn btn-secondary" onClick={fecharModal} disabled={saving}>Cancelar</button>
          <button className="btn btn-primary" onClick={salvar} disabled={saving}>
            {saving ? 'Salvando...' : 'Salvar atividade'}
          </button>
        </div>
      </Modal>

      {/* ── Modal IA: Próxima Atividade ── */}
      <Modal open={showSugestao} onClose={function(){ setShowSugestao(false); }} title="Próxima Atividade" wide>
        {sugestLoading ? (
          <div style={{ padding:'40px 0', textAlign:'center' }}>
            <div style={{ display:'inline-flex', alignItems:'center', gap:10, color:'var(--tx-2)', font:'500 14px/1.4 var(--ff-body)' }}>
              <i data-lucide="sparkles" style={{ width:18, height:18, color:'var(--grn)' }}></i>
              A IA está analisando a atividade registrada...
            </div>
          </div>
        ) : (
          <div style={{ marginTop:16 }}>
            {sugestResumo && (
              <div style={{ background:'var(--surface-2)', borderRadius:'var(--r-md)', padding:'12px 16px',
                borderLeft:'3px solid var(--grn)', marginBottom:20,
                font:'400 13px/1.6 var(--ff-body)', color:'var(--tx-2)' }}>
                <div style={{ display:'flex', gap:8, alignItems:'flex-start' }}>
                  <i data-lucide="sparkles" style={{ width:15, height:15, color:'var(--grn)', flexShrink:0, marginTop:2 }}></i>
                  <span>{sugestResumo}</span>
                </div>
              </div>
            )}
            {sugestOpcoes.length > 0 && (
              <div style={{ marginBottom:20 }}>
                <div style={{ font:'600 11px/1 var(--ff-body)', textTransform:'uppercase', letterSpacing:'.06em', color:'var(--tx-3)', marginBottom:10 }}>Escolha uma sugestão</div>
                <div style={{ display:'flex', flexDirection:'column', gap:8 }}>
                  {sugestOpcoes.map(function(op, idx){
                    var sel = sugestSel === idx;
                    return (
                      <div key={idx} onClick={function(){ _selOpcaoSug(idx); }}
                        style={{
                          display:'flex', alignItems:'center', gap:12, padding:'12px 14px',
                          border: sel ? '2px solid var(--grn)' : '1px solid var(--border)',
                          borderRadius:'var(--r-md)', cursor:'pointer',
                          background: sel ? 'color-mix(in srgb,var(--grn) 6%,var(--surface))' : 'var(--surface)',
                          transition:'border .15s, background .15s',
                        }}>
                        <div style={{ width:32, height:32, borderRadius:'50%', flexShrink:0,
                          background: sel ? 'color-mix(in srgb,var(--grn) 15%,var(--surface))' : 'var(--surface-2)',
                          border: sel ? '2px solid var(--grn)' : '1px solid var(--border)',
                          display:'flex', alignItems:'center', justifyContent:'center',
                          color: ATV_COLOR[op.tipo]||'var(--grn)' }}>
                          <i data-lucide={ATV_ICON[op.tipo]||'calendar'} style={{ width:14, height:14 }}></i>
                        </div>
                        <div style={{ flex:1 }}>
                          <div style={{ font:'500 13px/1.2 var(--ff-body)', color:'var(--tx)' }}>{op.titulo}</div>
                          <div style={{ font:'400 11px/1 var(--ff-mono)', color:'var(--tx-3)', marginTop:3 }}>
                            {ATV_LABEL[op.tipo]||op.tipo} · {new Date(op.data_iso).toLocaleDateString('pt-BR',{ weekday:'short', day:'2-digit', month:'short' })}
                          </div>
                        </div>
                        {sel && <i data-lucide="check-circle-2" style={{ width:16, height:16, color:'var(--grn)', flexShrink:0 }}></i>}
                      </div>
                    );
                  })}
                </div>
              </div>
            )}
            {sugestOpcoes.length > 0 && (
              <div className="form-grid-2" style={{ marginBottom:8 }}>
                <FormField label="Descrição" col={2}>
                  <input value={sugestEditTit} onChange={function(e){ setSugestEditTit(e.target.value); }}
                    className="inp" placeholder="Edite o título da próxima atividade..." />
                </FormField>
                <FormField label="Data e hora" col={2}>
                  <input type="datetime-local" value={sugestEditDt}
                    onChange={function(e){ setSugestEditDt(e.target.value); }} className="inp" />
                </FormField>
              </div>
            )}
            <div style={{ display:'flex', justifyContent:'flex-end', gap:10, marginTop:24, paddingTop:16, borderTop:'1px solid var(--border)' }}>
              <button className="btn btn-secondary" onClick={function(){ setShowSugestao(false); }}>Não agora</button>
              <button className="btn btn-primary" onClick={aceitarSugestao} disabled={!sugestOpcoes.length}>
                <i data-lucide="calendar-plus" style={{ width:13, height:13 }}></i>
                Agendar →
              </button>
            </div>
          </div>
        )}
      </Modal>
    </>
  );
}
window.AtividadeModalGlobal = AtividadeModalGlobal;

// ── PedidosScreen ──────────────────────────────────────────────────────────
function PedidosScreen({ onNav }) {
  // K-D14: persiste filtro
  const [filter, setFilter] = useScreenState(function() {
    try { return sessionStorage.getItem('crm_pedidos_filter') || 'ativos'; } catch(e) { return 'ativos'; }
  });
  const [busca, setBusca] = useScreenState("");
  const [, setTick] = useScreenState(0);
  // K-D5: paginação 50/pg
  const [page, setPage] = useScreenState(1);
  const PAGE_SIZE = 50;
  React.useEffect(function() {
    try { sessionStorage.setItem('crm_pedidos_filter', filter); } catch(e) {}
    setPage(1);
  }, [filter]);
  React.useEffect(function() { setPage(1); }, [busca]);

  React.useEffect(function() {
    function sync() { setTick(function(t){ return t + 1; }); }
    window.addEventListener('crm_prop_status_changed', sync);
    return function() { window.removeEventListener('crm_prop_status_changed', sync); };
  }, []);

  const todos = CRM_DATA.pedidos || [];
  function isCancelado(p) { return p.status === 'cancelado' || p.status === 'cancelado_correcao' || p.status === 'cancelado_apos_assinatura'; }

  const filtered = (function() {
    var base;
    if (filter === 'ativos')          base = todos.filter(function(p){ return !isCancelado(p); });
    else if (filter === 'cancelados') base = todos.filter(isCancelado);
    else if (filter === 'todos')      base = todos;
    else                              base = todos.filter(function(p) { return p.status === filter; });
    if (!busca.trim()) return base;
    var q = busca.trim().toLowerCase();
    return base.filter(function(ped) {
      var cli = CRM_DATA.getCliente ? CRM_DATA.getCliente(ped.cliente_id) : null;
      var op  = CRM_DATA.getOp ? CRM_DATA.getOp(ped.op_id) : null;
      var modelo = (op && op.modelo) || (ped.itens && ped.itens[0] && ped.itens[0].sku) || '';
      return (ped.id || '').toLowerCase().includes(q) ||
             (cli && cli.razao && cli.razao.toLowerCase().includes(q)) ||
             modelo.toLowerCase().includes(q) ||
             (ped.proposta_id || '').toLowerCase().includes(q);
    });
  })();

  // KPIs ignoram cancelados
  var ativos = todos.filter(function(p){ return !isCancelado(p); });
  var totalValor      = ativos.reduce(function(s,p){ return s + (p._valorNum || parseFloat(p.valor) || 0); }, 0);
  var totalAssinados  = ativos.filter(function(p){ return p.status === 'pedido_assinado'; }).length;
  var totalEmitidos   = ativos.filter(function(p){ return p.status === 'emitido'; }).length;
  var totalCancelados = todos.filter(isCancelado).length;

  return (
    <div data-screen-label="05 Pedidos">
      {/* KPIs */}
      <div className="kpi-grid" style={{ gridTemplateColumns: "repeat(3,1fr)", marginBottom: 18 }}>
        <div className="kpi-card">
          <div className="kpi-card__eyebrow">Total de pedidos</div>
          <div className="kpi-card__v">{ativos.length}</div>
          <div className="kpi-card__delta kpi-card__delta--pos">· {totalEmitidos} aguardando assinatura</div>
        </div>
        <div className="kpi-card">
          <div className="kpi-card__eyebrow">Valor total</div>
          <div className="kpi-card__v kpi-card__v--mono">{fR(totalValor)}</div>
          <div className="kpi-card__delta kpi-card__delta--pos">▲ Em pedidos ativos</div>
        </div>
        <div className="kpi-card">
          <div className="kpi-card__eyebrow">Pedidos assinados</div>
          <div className="kpi-card__v">{totalAssinados}</div>
          <div className="kpi-card__delta kpi-card__delta--pos">· Confirmados pelo cliente</div>
        </div>
      </div>

      <Panel title="Pedidos" noPad actions={
        <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
          <input
            type="search" placeholder="Buscar pedido, cliente ou modelo…"
            value={busca} onChange={function(e){ setBusca(e.target.value); }}
            style={{ fontSize: 12, padding: "5px 10px", borderRadius: 6, border: "1px solid var(--border)", background: "var(--surface-1)", color: "var(--tx-1)", width: 220 }}
          />
          <div className="tabs" style={{ marginBottom: 0 }}>
            {[
              { v: 'ativos',          l: 'Ativos' },
              { v: 'emitido',         l: 'Ag. assinatura' },
              { v: 'pedido_assinado', l: 'Assinado' },
              { v: 'cancelados',      l: 'Cancelados' + (totalCancelados ? ' (' + totalCancelados + ')' : '') },
              { v: 'todos',           l: 'Todos' },
            ].map(function(f) {
              return (
                <button key={f.v} className={filter === f.v ? 'is-on' : ''} onClick={function(){ setFilter(f.v); }}>
                  {f.l}
                </button>
              );
            })}
          </div>
        </div>
      }>
        <div className="dt-wrap">
          <table className="dt">
            <thead>
              <tr>
                <th>Pedido</th>
                <th>Proposta</th>
                <th>Modelo</th>
                <th>Cliente</th>
                <th>Unidade</th>
                <th>Condição Pgto</th>
                <th style={{ textAlign: 'right' }}>Valor</th>
                <th>Status</th>
                <th>Data</th>
                <th>Responsável</th>
                <th></th>
              </tr>
            </thead>
            <tbody>
              {filtered.length === 0 && (
                <tr><td colSpan={11} style={{ textAlign: 'center', padding: '32px 0', color: 'var(--tx-3)' }}>Nenhum pedido encontrado</td></tr>
              )}
              {filtered.slice(0, page * PAGE_SIZE).map(function(ped) {
                var cli      = CRM_DATA.getCliente ? CRM_DATA.getCliente(ped.cliente_id) : null;
                var vend     = CRM_DATA.getUsuario ? CRM_DATA.getUsuario(ped.vendedor_id) : null;
                var op       = CRM_DATA.getOp ? CRM_DATA.getOp(ped.op_id) : null;
                var cancelado = isCancelado(ped);
                var valorNum = ped._valorNum || parseFloat(ped.valor) || 0;
                var modelo   = (op && op.modelo) || (ped.itens && ped.itens[0] && ped.itens[0].sku) || '—';
                // Status efetivo: se o pedido ainda está 'emitido' em memória mas a proposta
                // vinculada já avançou (pedido_assinado / faturamento), usa o status da proposta
                var propVinc = ped.proposta_id ? CRM_DATA.getProposta(ped.proposta_id) : null;
                var effectivePedStatus = ped.status;
                var statusInconsistente = false;
                if (ped.status === 'emitido' && propVinc &&
                    ['pedido_assinado', 'faturamento'].includes(propVinc.status)) {
                  effectivePedStatus = propVinc.status;
                  // K-D7: marca inconsistência — pedido mostra avançado mas SP ainda em 'emitido'
                  statusInconsistente = true;
                }
                // K-D7: pedido cancelado mas proposta ativa também é inconsistente
                if (cancelado && propVinc && !['cancelada','pedido','pedido_assinado','faturamento'].includes(propVinc.status)) {
                  statusInconsistente = true;
                }
                return (
                  <tr key={ped.id}
                    title={cancelado && ped.motivo_cancelamento ? 'Motivo: ' + ped.motivo_cancelamento : 'Abrir pedido'}
                    style={{ cursor: 'pointer', opacity: cancelado ? 0.55 : 1, textDecoration: cancelado ? 'line-through' : 'none' }}
                    onClick={function(e) {
                      if (e.target.closest('[data-action]')) return;
                      var url = _buildPedidoLink(ped);
                      if (url) window.open(url, '_blank');
                    }}>
                    <td style={{ fontFamily: 'var(--ff-mono)', fontSize: 12 }}>{ped.id}</td>
                    <td style={{ fontFamily: 'var(--ff-mono)', fontSize: 12, color: 'var(--tx-3)' }}>{ped.proposta_id || '—'}</td>
                    <td style={{ fontSize: 12, color: 'var(--tx-2)', maxWidth: 140, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{modelo}</td>
                    <td style={{ fontSize: 12 }}>{cli ? cli.razao.split(' ').slice(0,2).join(' ') : (ped.cliente_id || '—')}</td>
                    <td style={{ fontSize: 12, color: 'var(--tx-2)' }}>{ped.unidade_bauko || '—'}</td>
                    <td style={{ fontSize: 12, color: 'var(--tx-2)', maxWidth: 160, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
                      title={ped.pgto_condicao || ''}>
                      {ped.pgto_condicao || '—'}
                    </td>
                    <td style={{ textAlign: 'right', fontFamily: 'var(--ff-mono)', fontSize: 12 }}>{fR(valorNum)}</td>
                    <td>
                      <div style={{ display:'flex', flexWrap:'wrap', alignItems:'center', gap:6 }}>
                        <Badge status={effectivePedStatus} />
                        {statusInconsistente && (
                          <span title="Inconsistência detectada: status do pedido difere do esperado pela proposta. Recarregue a página para sincronizar."
                                style={{ color:'var(--warn)', cursor:'help', fontSize:14, lineHeight:1 }}>⚠</span>
                        )}
                        {op && (op.etapa === 'fechado' || op.etapa === 'fechamento_parcial') && !cancelado && (
                          <span
                            title={'Oportunidade ' + (op.etapa === 'fechado' ? 'fechada' : 'ganho parcial')}
                            style={{ fontSize:9, fontFamily:'var(--ff-body)', fontWeight:600, textTransform:'uppercase', letterSpacing:'.05em',
                              padding:'2px 5px', borderRadius:'var(--r-pill)', whiteSpace:'nowrap',
                              background: op.etapa === 'fechado' ? 'var(--grn-050)' : 'var(--warn-050)',
                              color: op.etapa === 'fechado' ? 'var(--grn)' : 'var(--warn)',
                              border:'1px solid ' + (op.etapa === 'fechado' ? 'var(--grn)' : 'var(--warn)') + '55' }}>
                            {op.etapa === 'fechado' ? 'Op fechada' : 'Op parcial'}
                          </span>
                        )}
                      </div>
                    </td>
                    <td style={{ fontSize: 12, color: 'var(--tx-3)' }}>
                      {ped.data ? new Date(ped.data).toLocaleDateString('pt-BR') : '—'}
                    </td>
                    <td>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                        {vend && (
                          <div style={{ width: 22, height: 22, borderRadius: '50%', background: vend.avatar_color || '#706f6f', display: 'flex', alignItems: 'center', justifyContent: 'center', font: '600 9px/1 var(--ff-body)', color: '#fff', flexShrink: 0 }}>{vend.iniciais}</div>
                        )}
                        <span style={{ fontSize: 12, color: 'var(--tx-2)' }}>{vend ? vend.nome.split(' ')[0] : '—'}</span>
                      </div>
                    </td>
                    <td data-action="actions" style={{ whiteSpace: 'nowrap' }}>
                      <div style={{ display: 'flex', gap: 4, justifyContent: 'flex-end', alignItems: 'center' }} onClick={function(e){ e.stopPropagation(); }}>
                        {op && onNav && (
                          <button data-action="op"
                            title={'Ver oportunidade: ' + (op.titulo || '')}
                            onClick={function(e){ e.stopPropagation(); onNav('oportunidade', op.id); }}
                            style={{ background: 'none', border: '1px solid var(--border)', borderRadius: 6, padding: '3px 6px', cursor: 'pointer', color: 'var(--tx-2)', display: 'flex', alignItems: 'center' }}>
                            <i data-lucide="briefcase" style={{ width: 13, height: 13 }}></i>
                          </button>
                        )}
                        {!op && ped.op_id && (
                          <span data-action="orphan"
                            title={'Oportunidade ' + ped.op_id + ' não encontrada — dado de teste ou op removida.'}
                            style={{ color: 'var(--tx-3)', cursor: 'help', fontSize: 13, lineHeight: 1, padding: '3px 4px' }}>⚠</span>
                        )}
                      </div>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
        {/* K-D5: paginação carregar mais */}
        {filtered.length > page * PAGE_SIZE && (
          <div style={{ textAlign:'center', padding:'12px 0', borderTop:'1px solid var(--border)' }}>
            <button className="btn btn-secondary" onClick={function(){ setPage(function(p){ return p + 1; }); }}>
              Carregar mais ({filtered.length - page * PAGE_SIZE} restantes)
            </button>
          </div>
        )}
      </Panel>
    </div>
  );
}
window.PedidosScreen = PedidosScreen;
