// ═══════════════════════════════════════════════════════
// BAUKO CRM — Oportunidade Detail Screen
// ═══════════════════════════════════════════════════════
/* global React, CRM_DATA, CRM_API, Badge, Panel, Modal, FormField, AtivIcon, fR, fR2, fDate, fDateTime, fN */
const { useState: useOpState, useEffect: useOpEffect } = React;

// ── Helper: dias até previsão ─────────────────────────
function diasRestantes(dateStr) {
  if (!dateStr) return null;
  const d = new Date(dateStr);
  if (isNaN(d)) return null;
  const diff = Math.ceil((d - new Date()) / 86400000);
  if (diff < 0) return { label: `${Math.abs(diff)}d atraso`, color: 'var(--danger)' };
  if (diff === 0) return { label: 'hoje', color: 'var(--warn)' };
  if (diff <= 14) return { label: `em ${diff}d`, color: 'var(--warn)' };
  return { label: `em ${diff}d`, color: 'var(--tx-3)' };
}

// ── Editar Oportunidade Modal ─────────────────────────
function EditarOpModal({ open, onClose, op, onSave }) {
  // Origens — fonte única no CRM_CONFIG (configurável em /config sem deploy).
  // Lista B oficial (canal de aquisição) com 22 itens. Fallback inline roda
  // apenas se CRM_CONFIG não carregou (ex: lista SP indisponível).
  const ORIGENS = (window.CRM_CONFIG && CRM_CONFIG.get('pipeline_origens')) || [
    'Prospecção ativa','Indicação','Cliente da base','Pós-vendas',
    'Locação','Peças','Site','WhatsApp','Telefone','Evento/feira',
    'Campanha de marketing','Redes sociais','Marketplace','Fabricante',
    'Operador','Representante/parceiro','Reativação de cliente',
    'Upgrade/substituição de frota',
    'Licitação pública','Financiamento BKB','Projeto especial','Outro',
  ];
  const [form, setForm] = useOpState({});

  useOpEffect(() => {
    if (open && op) setForm({
      valor:                op.valor || 0,
      origem:               op.origem || '',
      previsao_fechamento:  (op.previsao_fechamento || '').slice(0, 7),
      prioridade:           op.prioridade || 'media',
      probabilidade:        op.probabilidade || 0,
      descricao:            op.descricao || '',
      vendedor_id:          op.vendedor_id || '',
    });
  }, [open]);

  function set(k, v) { setForm(prev => ({ ...prev, [k]: v })); }

  // J-D6: reatribuição de responsável visível apenas para gerentes/admins
  var _isGerenteModal = !!(window.CRM_USER && (window.CRM_USER.isGerente || window.CRM_USER.isAdmin));
  var _vendedores = _isGerenteModal
    ? ((window.CRM_DATA && CRM_DATA.usuarios) || []).filter(function(u){ return u.ativo !== false; })
    : [];

  function handleSave() {
    var valorFinal = Number(form.valor);
    var probFinal  = Math.min(100, Math.max(0, Number(form.probabilidade) || 0));
    // J-D5: validações de entrada — previne dados inválidos no SP
    if (valorFinal < 0) {
      if (typeof window.CRM_TOAST === 'function') window.CRM_TOAST('Valor estimado não pode ser negativo.', 'warn');
      return;
    }
    onSave({
      ...form,
      previsao_fechamento: form.previsao_fechamento ? form.previsao_fechamento + '-01' : '',
      valor: valorFinal,
      probabilidade: probFinal,
    });
    onClose();
  }

  return (
    <Modal open={open} onClose={onClose} title="Editar oportunidade" subtitle={op?.titulo}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14, marginTop: 16 }}>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          <div className="f">
            <label>Valor estimado (R$)</label>
            <input className="inp" type="number" value={form.valor || ''} onChange={e => set('valor', e.target.value)} />
          </div>
          <div className="f">
            <label>Origem</label>
            <select className="inp" value={form.origem || ''} onChange={e => set('origem', e.target.value)}>
              <option value="">— Selecione —</option>
              {ORIGENS.map(o => <option key={o} value={o}>{o}</option>)}
            </select>
          </div>
          <div className="f">
            <label>Previsão de fechamento</label>
            <input className="inp" type="month" value={form.previsao_fechamento || ''} onChange={e => set('previsao_fechamento', e.target.value)} />
          </div>
          <div className="f">
            <label>Prioridade</label>
            <div className="tog">
              {[['baixa','Baixa'],['media','Média'],['alta','Alta']].map(([v,l]) => (
                <div key={v} className={'tb ' + (form.prioridade === v ? 'on' : '')} onClick={() => set('prioridade', v)}>{l}</div>
              ))}
            </div>
          </div>
          <div className="f">
            <label>Probabilidade (%)</label>
            <input className="inp" type="number" min={0} max={100} value={form.probabilidade || ''} onChange={e => set('probabilidade', e.target.value)} />
          </div>
          {/* J-D6: reatribuição de responsável — somente gerentes/admins */}
          {_isGerenteModal && (
            <div className="f">
              <label style={{ display:'flex', alignItems:'center', gap:5 }}>
                Responsável
                <span style={{ font:'400 9px/1 var(--ff-body)', background:'var(--warn-050)', color:'var(--warn)', border:'1px solid var(--warn)55', borderRadius:'var(--r-pill)', padding:'1px 5px' }}>gerente</span>
              </label>
              <select className="inp" value={form.vendedor_id || ''} onChange={e => set('vendedor_id', e.target.value)}>
                <option value="">— Manter atual —</option>
                {_vendedores.map(function(u){ return <option key={u.id} value={u.id}>{u.nome}</option>; })}
              </select>
            </div>
          )}
        </div>
        <div className="f">
          <label>Observações</label>
          <textarea className="inp" rows={3} value={form.descricao || ''} onChange={e => set('descricao', e.target.value)} style={{ resize: 'vertical', lineHeight: 1.5, fontFamily: 'var(--ff-body)', fontSize: 13 }} />
        </div>
        {/* J-D6: aviso de impacto da reatribuição */}
        {_isGerenteModal && form.vendedor_id && form.vendedor_id !== (op && op.vendedor_id) && (
          <div style={{ font:'400 11px/1.5 var(--ff-body)', color:'var(--warn)', background:'var(--warn-050)', border:'1px solid var(--warn)44', borderRadius:'var(--r-md)', padding:'8px 12px' }}>
            ⚠ A reatribuição cascateará para todas as propostas e pedidos desta oportunidade. O responsável antigo perderá acesso.
          </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={handleSave}><i data-lucide="check"></i> Salvar</button>
        </div>
      </div>
    </Modal>
  );
}

// ── Quantidade Modal ──────────────────────────────────
function QuantidadeModal({ open, item, onConfirm, onClose }) {
  const [qtd, setQtd] = useOpState(1);
  if (!open || !item) return null;
  const valorTotal = (item.valor_unitario || 0) * qtd;
  function handleConfirm() { onConfirm(qtd); setQtd(1); }
  return (
    <Modal open={open} onClose={onClose} title="Confirmar máquina" subtitle={item.modelo || item.fab || 'Máquina'}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 16, marginTop: 16 }}>
        <div style={{ background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', padding: '14px 16px', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          {[['Modelo', item.modelo || '—'],['Tabela', item.tabela || '—'],['Vlr. unitário', fR2(item.valor_unitario || 0)],['UF comprador', item.uf_cliente || '—']].map(([lbl, val]) => (
            <div key={lbl}>
              <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 13px/1.2 var(--ff-mono)', color: 'var(--tx)' }}>{val}</div>
            </div>
          ))}
        </div>
        <FormField label="Quantidade">
          <input className="inp" type="number" min={1} value={qtd} onChange={e => setQtd(Math.max(1, Number(e.target.value)))} style={{ width: 120 }} />
        </FormField>
        <div style={{ background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', padding: '12px 14px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <span style={{ font: '400 12px/1 var(--ff-mono)', color: 'var(--tx-3)' }}>Valor total</span>
          <span style={{ font: '600 16px/1 var(--ff-mono)', color: 'var(--grn)' }}>{fR(valorTotal)}</span>
        </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={handleConfirm}><i data-lucide="check"></i> Adicionar</button>
        </div>
      </div>
    </Modal>
  );
}

// NovaAtividadeModal removido — usar window.CRM_OPEN_ATIVIDADE() (AtividadeModalGlobal em crm-screens-v2.jsx)

// ── Nova Proposta Modal ───────────────────────────────
function NovaPropostaModal({ open, onClose, op, itens, onSave }) {
  const [validade, setValidade] = useOpState(function(){ return (window.CRM_CONFIG && CRM_CONFIG.get('proposta_validade_default_dias')) || 30; });

  const [prazoEntrega, setPrazoEntrega] = useOpState(90);

  function handleSave() {
    const dataValidade = new Date();
    dataValidade.setDate(dataValidade.getDate() + Number(validade));
    const dataPrazo = new Date();
    dataPrazo.setDate(dataPrazo.getDate() + Number(prazoEntrega));
    onSave({
      op_id:         op?.id,
      validade:      dataValidade.toISOString().slice(0, 10),
      prazo_entrega: dataPrazo.toISOString().slice(0, 10),
    });
    setValidade(30); setPrazoEntrega(90); onClose();
  }

  const totalItens = (itens || []).reduce((s, it) => s + (it.valor_total || 0), 0);

  return (
    <Modal open={open} onClose={onClose} title="Nova proposta" subtitle={op ? op.titulo : ''}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14, marginTop: 16 }}>
        {/* Resumo readonly */}
        <div style={{ background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', padding: '14px 16px', display: 'flex', flexDirection: 'column', gap: 8 }}>
          {[
            ['Cliente',   op?.cliente_id || '—'],
            ['Máquinas',  (itens||[]).length > 0 ? (itens||[]).map(i => i.modelo || i.fab || 'Máquina').join(', ') : '—'],
            ['Valor ref.', totalItens > 0 ? fR(totalItens) : (op?.valor ? fR(op.valor) : '—')],
          ].map(([lbl, val]) => (
            <div key={lbl} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 12 }}>
              <span style={{ font: '600 10px/1 var(--ff-body)', textTransform: 'uppercase', letterSpacing: '.06em', color: 'var(--tx-3)' }}>{lbl}</span>
              <span style={{ font: '400 12px/1.2 var(--ff-mono)', color: 'var(--tx)', textAlign: 'right', maxWidth: '60%', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{val}</span>
            </div>
          ))}
        </div>
        {/* Validade */}
        <FormField label="Validade da proposta (dias)">
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <input className="inp" type="number" min={1} max={180} value={validade}
              onChange={e => setValidade(e.target.value)}
              style={{ width: 90 }} />
            <span style={{ font: '400 12px/1 var(--ff-mono)', color: 'var(--tx-3)' }}>dias a partir de hoje</span>
          </div>
        </FormField>
        <FormField label="Prazo de entrega (dias a partir de hoje)">
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <input className="inp" type="number" min={1} max={730} value={prazoEntrega}
              onChange={e => setPrazoEntrega(e.target.value)}
              style={{ width: 90 }} />
            <span style={{ font: '400 12px/1 var(--ff-mono)', color: 'var(--tx-3)' }}>dias a partir de hoje</span>
          </div>
        </FormField>
        <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
          <button className="btn btn-secondary" onClick={onClose}>Cancelar</button>
          <button className="btn btn-primary" onClick={handleSave}><i data-lucide="file-plus"></i> Criar proposta</button>
        </div>
      </div>
    </Modal>
  );
}

// ── FechamentoModal ───────────────────────────────────────────────────────────
// Motivos de perda: fonte única no CRM_CONFIG (key `op_motivos_perda`).
// Lista padrão Komatsu — alterar com cuidado, afeta relatórios de vendas perdidas.
function getMotivosPerda() {
  return (window.CRM_CONFIG && CRM_CONFIG.get('op_motivos_perda')) || [
    'Afinidade com a marca',
    'Condição de pagamento',
    'Crédito (aprovação negada)',
    'Preço (concorrente mais barato)',
    'Prazo de entrega',
    'Cancelamento do projeto/obra',
    'Redução de orçamento',
    'Compra de outra marca',
    'Optou por equipamento locado',
    'Optou por equipamento usado',
    'Sem necessidade no momento',
    'Relacionamento com o concorrente',
    'Restrição interna do comprador',
    'Proposta não enviada a tempo',
    'Atraso no processo de venda',
    'Outros',
  ];
}
var MOTIVOS_PERDA = getMotivosPerda();

// FechamentoModal v2 — trabalha no nível de PROPOSTA (não de item).
// Cada proposta da oportunidade recebe um resultado: ganho / perdido / em negociação.
// Propostas com pedido_assinado são travadas como "ganho" (cliente já assinou).
// O onConfirm recebe { statusPropostas, motivoPerdaProp, obsPerdaProp } e é responsável
// por persistir os status das propostas e atualizar os itens em cascata.
function FechamentoModal({ open, onClose, op, propostas, itens, onConfirm }) {
  var useFS = React.useState;

  // Apenas propostas submetidas ao cliente (não canceladas, não rascunhos).
  // Rascunhos nunca foram enviados → não podem ser "perdidos"; serão auto-cancelados
  // pelo onConfirm sem precisar de decisão do usuário.
  var propostasAtivas = (propostas || []).filter(function(p) {
    return p.status !== 'cancelada' && p.status !== 'rascunho';
  });

  // pedido_assinado → travado como ganho (cliente assinou; não pode ser marcada como perdida)
  function isPropLocked(p) { return p.status === 'pedido_assinado'; }

  function buildInitStatus() {
    var init = {};
    propostasAtivas.forEach(function(p) {
      // Rascunhos default para perdido; propostas com pedido/assinado para ganho; demais ganho
      init[p.id] = (p.status === 'rascunho') ? 'perdido' : 'ganho';
    });
    return init;
  }

  var [statusPropostas, setStatusPropostas] = useFS(buildInitStatus);
  var [motivoPerdaProp, setMotivoPerdaProp] = useFS({});
  var [obsPerdaProp, setObsPerdaProp] = useFS({});

  React.useEffect(function() {
    if (!open) return;
    setStatusPropostas(buildInitStatus());
    setMotivoPerdaProp({});
    setObsPerdaProp({});
    setTimeout(function() {
      try {
        if (window.lucide && typeof window.lucide.createIcons === 'function') {
          window.lucide.createIcons({ attrs: { 'stroke-width': 1.75 } });
        }
      } catch(e) { /* DOM já alterado por outra reconciliação; ignora */ }
    }, 0);
  }, [open]);

  if (!open) return null;

  var statusKeys = Object.keys(statusPropostas);
  var nGanho   = statusKeys.filter(function(k){ return statusPropostas[k] === 'ganho'; }).length;
  var nPerdido = statusKeys.filter(function(k){ return statusPropostas[k] === 'perdido'; }).length;
  var nNeg     = statusKeys.filter(function(k){ return statusPropostas[k] === 'negociacao'; }).length;

  // "Em negociação" só disponível quando há 2+ propostas ativas
  var allowNegociacao = propostasAtivas.length > 1;
  var etapaResultante = nNeg > 0 ? 'fechamento_parcial' : 'fechado';

  // Propostas marcadas como perdido sem motivo preenchido bloqueiam o Confirmar
  var perdidosSemMotivo = propostasAtivas.filter(function(p) {
    return statusPropostas[p.id] === 'perdido' && !isPropLocked(p) && !(motivoPerdaProp[p.id]);
  });
  var podeSalvar = perdidosSemMotivo.length === 0;

  function setPropStatus(propId, val) {
    setStatusPropostas(function(prev) { return Object.assign({}, prev, { [propId]: val }); });
  }

  function handleConfirm() {
    if (!podeSalvar) return;
    onConfirm(etapaResultante, { statusPropostas, motivoPerdaProp, obsPerdaProp });
  }

  var statusOpts = [
    { val: 'ganho',      label: 'Ganho',        color: 'var(--ok)',     bg: 'var(--ok-050)'     },
    { val: 'negociacao', label: 'Em negociação', color: 'var(--warn)',   bg: 'var(--warn-050)'   },
    { val: 'perdido',    label: 'Perdida',       color: 'var(--danger)', bg: 'var(--danger-050)' },
  ].filter(function(o) { return o.val !== 'negociacao' || allowNegociacao; });

  var STATUS_PROP_LABEL = {
    rascunho: 'Rascunho', enviada: 'Enviada', aprovada: 'Aprovada',
    aguardando_aprovacao: 'Ag. aprovação', em_negociacao: 'Em negociação',
    pedido: 'Pedido emitido', pedido_assinado: 'Pedido assinado',
  };

  var overlay = { position: 'fixed', inset: 0, background: 'rgba(0,0,0,.55)', zIndex: 9999, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 16 };
  var box = { background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-lg)', width: '100%', maxWidth: 520, maxHeight: '90vh', overflow: 'auto', boxShadow: 'var(--shadow-lg)' };

  return (
    <div style={overlay} onClick={function(e){ if (e.target === e.currentTarget) onClose(); }}>
      <div style={box}>
        {/* Header */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '18px 20px 14px', borderBottom: '1px solid var(--border)' }}>
          <div style={{ width: 32, height: 32, borderRadius: '50%', background: 'var(--grn-050)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
            <i data-lucide="flag" style={{ width: 16, height: 16, color: 'var(--grn)' }}></i>
          </div>
          <div>
            <div style={{ font: '600 14px/1.2 var(--ff-body)', color: 'var(--tx)' }}>Fechar Oportunidade</div>
            <div style={{ font: '400 11px/1 var(--ff-mono)', color: 'var(--tx-3)', marginTop: 2 }}>{op && op.titulo}</div>
          </div>
          <button onClick={onClose} style={{ marginLeft: 'auto', background: 'none', border: 'none', cursor: 'pointer', color: 'var(--tx-3)', padding: 4 }}>
            <i data-lucide="x" style={{ width: 16, height: 16 }}></i>
          </button>
        </div>

        {/* Body */}
        <div style={{ padding: '18px 20px', display: 'flex', flexDirection: 'column', gap: 16 }}>

          {/* Sem propostas */}
          {propostasAtivas.length === 0 && (
            <div style={{ font: '400 13px/1.5 var(--ff-body)', color: 'var(--tx-3)', padding: '4px 0' }}>
              Esta oportunidade não possui propostas ativas. O fechamento apenas atualizará a etapa.
            </div>
          )}

          {/* Resultado por proposta */}
          {propostasAtivas.length > 0 && (
            <div>
              <div style={{ font: '600 11px/1 var(--ff-body)', textTransform: 'uppercase', letterSpacing: '.06em', color: 'var(--tx-3)', marginBottom: 10 }}>
                Resultado por proposta
              </div>
              {propostasAtivas.map(function(p) {
                var cur = statusPropostas[p.id] || 'ganho';
                var locked = isPropLocked(p);
                return (
                  <div key={p.id} style={{ borderRadius: 'var(--r-md)', border: '1px solid var(--border)', marginBottom: 8, background: 'var(--surface-2)', overflow: 'hidden' }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 12px' }}>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ font: '500 12px/1 var(--ff-body)', color: 'var(--tx)' }}>{p.id}</div>
                        <div style={{ font: '400 10px/1 var(--ff-mono)', color: 'var(--tx-3)', marginTop: 3 }}>
                          {p.versao}{p.valor ? ' · ' + (typeof fR === 'function' ? fR(p.valor) : '') : ''} · {STATUS_PROP_LABEL[p.status] || p.status}
                        </div>
                      </div>
                      {locked ? (
                        <div style={{ padding: '5px 12px', borderRadius: 'var(--r-pill)', font: '500 10px/1 var(--ff-body)',
                          background: 'var(--ok-050)', color: 'var(--ok)', border: '1px solid var(--ok)' }}>
                          Assinado ✓
                        </div>
                      ) : (
                        <div style={{ display: 'flex', gap: 4 }}>
                          {statusOpts.map(function(opt) {
                            var isOn = cur === opt.val;
                            return (
                              <button key={opt.val} onClick={function(){ setPropStatus(p.id, opt.val); }}
                                style={{ padding: '5px 10px', borderRadius: 'var(--r-pill)', font: '500 10px/1 var(--ff-body)', cursor: 'pointer', transition: 'all 120ms',
                                  background: isOn ? opt.bg : 'transparent',
                                  color: isOn ? opt.color : 'var(--tx-3)',
                                  border: '1px solid ' + (isOn ? opt.color : 'var(--border)') }}>
                                {opt.label}
                              </button>
                            );
                          })}
                        </div>
                      )}
                    </div>
                    {/* Motivo inline — aparece quando proposta marcada como perdida */}
                    {!locked && cur === 'perdido' && (
                      <div style={{ padding: '0 12px 12px', display: 'flex', flexDirection: 'column', gap: 6, borderTop: '1px solid var(--border)' }}>
                        <div style={{ font: '600 10px/1 var(--ff-body)', textTransform: 'uppercase', letterSpacing: '.06em', color: 'var(--danger)', marginTop: 10 }}>Motivo da perda</div>
                        <select className="inp" value={motivoPerdaProp[p.id] || ''} onChange={function(e){
                          var v = e.target.value;
                          setMotivoPerdaProp(function(prev) { return Object.assign({}, prev, { [p.id]: v }); });
                        }}>
                          <option value="">— Selecione o motivo —</option>
                          {MOTIVOS_PERDA.map(function(m){ return <option key={m} value={m}>{m}</option>; })}
                        </select>
                        <textarea className="inp" rows={2} value={obsPerdaProp[p.id] || ''} onChange={function(e){
                          var v = e.target.value;
                          setObsPerdaProp(function(prev) { return Object.assign({}, prev, { [p.id]: v }); });
                        }}
                          placeholder="Observações adicionais (opcional)"
                          style={{ resize: 'vertical', lineHeight: 1.5, fontFamily: 'var(--ff-body)', fontSize: 13 }} />
                      </div>
                    )}
                  </div>
                );
              })}
            </div>
          )}

          {/* Resumo contadores */}
          {propostasAtivas.length > 0 && (
            <div style={{ display: 'flex', gap: 8 }}>
              {[
                ['Ganho', nGanho, 'var(--ok)', 'var(--ok-050)'],
                ...(allowNegociacao ? [['Em negociação', nNeg, 'var(--warn)', 'var(--warn-050)']] : []),
                ['Perdido', nPerdido, 'var(--danger)', 'var(--danger-050)']
              ].map(function(r) {
                return (
                  <div key={r[0]} style={{ flex: 1, textAlign: 'center', padding: '10px 8px', borderRadius: 'var(--r-md)', background: r[3], border: '1px solid ' + r[2] + '33' }}>
                    <div style={{ font: '600 18px/1 var(--ff-display)', color: r[2] }}>{r[1]}</div>
                    <div style={{ font: '400 10px/1 var(--ff-body)', color: r[2], marginTop: 3 }}>{r[0]}</div>
                  </div>
                );
              })}
            </div>
          )}

          {/* Alerta de motivo faltando */}
          {perdidosSemMotivo.length > 0 && (
            <div style={{ font: '500 11px/1.4 var(--ff-body)', color: 'var(--danger)', background: 'var(--danger-050)', padding: '8px 12px', borderRadius: 'var(--r-md)', border: '1px solid rgba(220,53,69,.2)' }}>
              Selecione o motivo da perda para {perdidosSemMotivo.length === 1 ? 'a proposta marcada como perdida' : 'as ' + perdidosSemMotivo.length + ' propostas marcadas como perdidas'} antes de confirmar.
            </div>
          )}

          {/* Resultado previsto */}
          <div style={{ background: etapaResultante === 'fechado' ? 'var(--grn-050)' : 'var(--warn-050)', border: '1px solid ' + (etapaResultante === 'fechado' ? 'var(--grn)' : 'var(--warn)') + '44', borderRadius: 'var(--r-md)', padding: '10px 14px', display: 'flex', alignItems: 'center', gap: 8 }}>
            <i data-lucide={etapaResultante === 'fechado' ? 'check-circle-2' : 'alert-circle'}
              style={{ width: 16, height: 16, color: etapaResultante === 'fechado' ? 'var(--grn)' : 'var(--warn)', flexShrink: 0 }}></i>
            <span style={{ font: '500 12px/1.4 var(--ff-body)', color: etapaResultante === 'fechado' ? 'var(--grn)' : 'var(--warn)' }}>
              {etapaResultante === 'fechado'
                ? (nPerdido > 0 && nGanho > 0 ? 'Negociação concluída'
                    : nPerdido > 0 ? 'Todas as propostas perdidas'
                    : 'Todas as propostas ganhas') + ' — oportunidade será marcada como Fechado.'
                : 'Há ' + nNeg + ' proposta(s) ainda em negociação — oportunidade ficará como Fechamento Parcial.'}
            </span>
          </div>
        </div>

        {/* Footer */}
        <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', padding: '12px 20px 18px', borderTop: '1px solid var(--border)' }}>
          <button className="btn btn-secondary" onClick={onClose}>Cancelar</button>
          <button className="btn btn-primary" disabled={!podeSalvar} onClick={handleConfirm}
            style={{ background: !podeSalvar ? 'var(--grn-100)' : 'var(--grn)', opacity: podeSalvar ? 1 : 0.6 }}>
            <i data-lucide="check"></i> Confirmar fechamento
          </button>
        </div>
      </div>
    </div>
  );
}

// ── Main Oportunidade Screen ──────────────────────────
// ── SelecionarItensModal ─────────────────────────────────────────────────────
function SelecionarItensModal({ open, onClose, itens, onConfirm, onAddMaquina }) {
  var useSelState = React.useState;
  const [selecionados, setSelecionados] = useSelState(() => new Set(itens.map(function(it){ return it._localId; })));

  React.useEffect(function() {
    if (open) setSelecionados(new Set(itens.map(function(it){ return it._localId; })));
  }, [open]);

  function toggle(id) {
    setSelecionados(function(prev) {
      var next = new Set(prev);
      if (next.has(id)) next.delete(id); else next.add(id);
      return next;
    });
  }

  var itensParaProposta = itens.filter(function(it){ return selecionados.has(it._localId); });

  return (
    <Modal open={open} onClose={onClose} title="Nova Proposta" subtitle="Selecione os itens a incluir">
      <div style={{ marginBottom: 16 }}>
        {itens.length === 0 ? (
          <div style={{ padding: '20px 0', textAlign: 'center', color: 'var(--tx-3)', font: '400 13px/1.5 var(--ff-body)' }}>
            Nenhuma máquina na oportunidade ainda.<br/>
            <span style={{ font: '400 12px/1 var(--ff-mono)' }}>Adicione máquinas antes de criar uma proposta.</span>
          </div>
        ) : (
          itens.map(function(item) {
            var checked = selecionados.has(item._localId);
            return (
              <div key={item._localId} onClick={function(){ toggle(item._localId); }}
                style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 14px',
                  borderRadius: 'var(--r-md)', cursor: 'pointer', marginBottom: 6,
                  background: checked ? 'var(--surface-2)' : 'transparent',
                  border: '1px solid ' + (checked ? 'var(--grn)' : 'var(--border)'),
                  transition: 'all 120ms' }}>
                <div style={{ width: 18, height: 18, borderRadius: 4, flexShrink: 0,
                  background: checked ? 'var(--grn)' : 'var(--surface)',
                  border: '2px solid ' + (checked ? 'var(--grn)' : 'var(--border-2)'),
                  display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  {checked && <svg width="10" height="10" viewBox="0 0 12 12" fill="none">
                    <polyline points="2,6 5,9 10,3" stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
                  </svg>}
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ font: '500 13px/1 var(--ff-body)', color: 'var(--tx)' }}>{item.modelo || item.fab || 'Máquina'}</div>
                  <div style={{ font: '400 11px/1.3 var(--ff-mono)', color: 'var(--tx-3)', marginTop: 3 }}>
                    {item.familia || ''}{item.tabela ? ' · Tab ' + item.tabela : ''} · Qtd {item.quantidade || item.qtd || 1}
                  </div>
                </div>
                <div style={{ font: '600 13px/1 var(--ff-mono)', color: checked ? 'var(--grn)' : 'var(--tx-3)', flexShrink: 0 }}>
                  {typeof fR === 'function' ? fR(item.valor_total || 0) : ''}
                </div>
              </div>
            );
          })
        )}
      </div>
      {itens.length > 0 && (
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center',
          padding: '10px 14px', background: 'var(--surface-2)', borderRadius: 'var(--r-md)',
          marginBottom: 16, font: '400 12px/1 var(--ff-mono)', color: 'var(--tx-3)' }}>
          <span>{selecionados.size} de {itens.length} máquina{itens.length > 1 ? 's' : ''} selecionada{selecionados.size !== 1 ? 's' : ''}</span>
          <span style={{ font: '600 13px/1 var(--ff-mono)', color: 'var(--grn)' }}>
            {typeof fR === 'function' ? fR(itensParaProposta.reduce(function(s, it){ return s + (it.valor_total || 0); }, 0)) : ''}
          </span>
        </div>
      )}
      <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end' }}>
        <button className="btn btn-secondary" onClick={onClose}>Cancelar</button>
        {itens.length === 0 ? (
          <button className="btn btn-primary" onClick={function(){ onClose(); if (onAddMaquina) onAddMaquina(); }}>
            <i data-lucide="plus-circle"></i> Adicionar Máquina
          </button>
        ) : (
          <button className="btn btn-primary" disabled={selecionados.size === 0}
            onClick={function(){ onConfirm(itensParaProposta); onClose(); }}>
            Criar Proposta
          </button>
        )}
      </div>
    </Modal>
  );
}

// ── Modal: Registrar aprovação de crédito ─────────────
function AprovCreditoModal({ open, onClose, op, propostas, onConfirm }) {
  var FINANC_OPTIONS = ['À vista','FINAME','CDC','Leasing','Consórcio','Recursos próprios'];

  // Busca o pedido vinculado às propostas da oportunidade para pré-preencher condição de pagto
  var pedidoVinculado = (CRM_DATA.pedidos || []).find(function(ped) {
    return propostas && propostas.some(function(p) { return p.id === ped.proposta_id; });
  });

  const [form, setForm] = useOpState({ financiamento: '', data_entrega: '', obs: '' });

  useOpEffect(function() {
    if (!open) return;
    var pgto = pedidoVinculado ? (pedidoVinculado.pgto_condicao || '') : '';
    setForm({ financiamento: pgto, data_entrega: '', obs: '' });
  }, [open]);

  function set(k, v) { setForm(function(prev){ return Object.assign({}, prev, { [k]: v }); }); }

  return (
    <Modal open={open} onClose={onClose} title="Registrar aprovação de crédito" subtitle={op && op.titulo}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14, marginTop: 16 }}>
        <div className="f">
          <label>Tipo de financiamento <span style={{ color: 'var(--danger)' }}>*</span></label>
          <select className="inp" value={form.financiamento || ''} onChange={function(e){ set('financiamento', e.target.value); }}>
            <option value="">— Selecione —</option>
            {FINANC_OPTIONS.map(function(o){ return <option key={o} value={o}>{o}</option>; })}
          </select>
          {pedidoVinculado && pedidoVinculado.pgto_condicao && (
            <span style={{ font: '400 11px/1.4 var(--ff-body)', color: 'var(--tx-3)', marginTop: 3 }}>
              ℹ️ Informado no pedido: <em>{pedidoVinculado.pgto_condicao}</em>
            </span>
          )}
        </div>
        <div className="f">
          <label>Data prevista de entrega</label>
          <input className="inp" type="month" value={form.data_entrega || ''} onChange={function(e){ set('data_entrega', e.target.value); }} />
        </div>
        <div className="f">
          <label>Observações (opcional)</label>
          <textarea className="inp" rows={3} style={{ resize: 'vertical' }}
            value={form.obs || ''} onChange={function(e){ set('obs', e.target.value); }}
            placeholder="Ex: financiamento aprovado banco X, taxa 1,2% a.m." />
        </div>
        <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 4 }}>
          <button className="btn btn-secondary" onClick={onClose}>Cancelar</button>
          <button className="btn btn-primary" disabled={!form.financiamento}
            onClick={function(){ onConfirm(form); }}>
            <i data-lucide="check-circle"></i> Confirmar aprovação
          </button>
        </div>
      </div>
    </Modal>
  );
}

function Oportunidade({ opId, extraOps, onBack, onOpenProp }) {
  const [op, setOp] = useOpState(() => {
    const allOps = [...CRM_DATA.oportunidades, ...(extraOps || [])];
    return allOps.find(o => o.id === opId) || CRM_DATA.oportunidades[0];
  });
  const [atividades, setAtividades] = useOpState(() => {
    // M: Mostra todas as atividades do cliente, não só as dessa op
    var clienteId = op && op.cliente_id;
    var source = CRM_DATA.atividades || [];
    var lista = clienteId
      ? source.filter(a => a.cliente_id === clienteId)
      : source.filter(a => a.op_id === opId); // fallback se op ainda não tem cliente_id
    return lista.slice().sort(function(a, b) { return new Date(b.data) - new Date(a.data); });
  });
  const [propostas, setPropostas] = useOpState(() =>
    (CRM_DATA.propostas || []).filter(p => p.op_id === opId)
  );
  const [itens, setItens] = useOpState([]);

  // Mantém CRM_DATA.itens_op atualizado para que PropostaModal detecte órfãos
  React.useEffect(() => {
    if (!op) return;
    if (!CRM_DATA.itens_op) CRM_DATA.itens_op = {};
    CRM_DATA.itens_op[op.id] = itens;
  }, [itens, op && op.id]);

  // F3: Sincroniza op.valor no SP/CRM_DATA sempre que os itens mudarem
  var _syncedOpValor = React.useRef(null);
  var _syncValorTimer = React.useRef(null); // J-D7: debounce p/ adições rápidas de múltiplas máquinas
  React.useEffect(function() {
    if (!op || !op._spId || itens.length === 0) return;
    var newValor = itens.reduce(function(s, it) { return s + (it.valor_total || 0); }, 0);
    if (newValor <= 0) return;
    if (newValor === _syncedOpValor.current) return;
    var stored = (CRM_DATA.oportunidades || []).find(function(o) { return o.id === op.id; });
    if (stored && stored.valor === newValor) { _syncedOpValor.current = newValor; return; }
    // J-D7: debounce 300ms — evita múltiplos PATCHes quando máquinas são adicionadas em sequência rápida
    if (_syncValorTimer.current) clearTimeout(_syncValorTimer.current);
    var capturedValor = newValor, capturedSpId = op._spId, capturedOpId = op.id;
    _syncValorTimer.current = setTimeout(function() {
      _syncValorTimer.current = null;
      if (capturedValor === _syncedOpValor.current) return;
      _syncedOpValor.current = capturedValor;
      if (window.CRM_API) {
        CRM_API.updateItem('Oportunidades', capturedSpId, { valor: capturedValor })
          .catch(function(e) { console.warn('[CRM] sync op.valor:', e.message); });
      }
      if (CRM_DATA.oportunidades) {
        var oppIdx = CRM_DATA.oportunidades.findIndex(function(o) { return o.id === capturedOpId; });
        if (oppIdx >= 0) {
          CRM_DATA.oportunidades[oppIdx] = Object.assign({}, CRM_DATA.oportunidades[oppIdx], { valor: capturedValor });
        }
      }
      // Atualiza o estado local da oportunidade aberta para o "Resumo" refletir o valor na hora.
      // Sem isto, o subtitulo continuava exibindo o op.valor antigo ate a oportunidade ser reaberta.
      setOp(function(prev) {
        return (prev && prev.id === capturedOpId && prev.valor !== capturedValor)
          ? Object.assign({}, prev, { valor: capturedValor })
          : prev;
      });
    }, 300);
  }, [itens]);

  // Escuta atualizações de itens vindas do PropostaModal
  React.useEffect(() => {
    function onPropItens(e) {
      if (e.detail && e.detail.opId === opId && Array.isArray(e.detail.itens)) {
        var propItens = e.detail.itens;
        setItens(function(prev) {
          // Normaliza item vindo da proposta para formato de oportunidade
          function norm(it) {
            return Object.assign({}, it, {
              valor_total: it.total || it.unit || 0,
              fab:   it.familia || it.fab || '',
              modelo: it.modelo || '',
              qtd:   it.qtd || 1,
              desconto: 0,        // op items sempre a preço de tabela
              desconto_max: undefined,
            });
          }
          // Só atualiza specs de itens que JÁ existem na oportunidade.
          // Nunca re-insere itens removidos da oportunidade (órfãos da proposta).
          var propIds = new Set(propItens.map(function(it){ return it._localId; }));
          return prev.map(function(it) {
            if (propIds.has(it._localId)) {
              var propVer = propItens.find(function(p){ return p._localId === it._localId; });
              return norm(propVer);
            }
            return it;
          });
        });
        // B2: Atualiza valor exibido no card da proposta com o total recalculado
        var newValor = propItens.reduce(function(s, it) {
          return s + Math.round((it.total || 0) * (1 - (it.desconto || 0) / 100));
        }, 0);
        setPropostas(function(prev) {
          return prev.map(function(p) {
            return p.id === e.detail.propId
              ? Object.assign({}, p, { valor: newValor })
              : p;
          });
        });
      }
    }
    function onPropDeleted(e) {
      if (e.detail && e.detail.opId === opId) {
        setPropostas(function(prev){ return prev.filter(function(p){ return p.id !== e.detail.propId; }); });
      }
    }
    function onPropStatusChanged(e) {
      if (!e.detail || e.detail.opId !== opId) return;
      setPropostas(function(prev) {
        return prev.map(function(p) {
          return p.id === e.detail.propId
            ? Object.assign({}, p, { status: e.detail.newStatus })
            : p;
        });
      });
      // Auto-avança etapa da oportunidade com base no novo status da proposta
      var opAtual = CRM_DATA.oportunidades.find(function(o){ return o.id === opId; }) || {};
      var etapaAtual = opAtual.etapa || 'lead';
      var ns = e.detail.newStatus;
      if ((ns === 'enviada' || ns === 'em_negociacao') && (etapaAtual === 'lead' || etapaAtual === 'qualificacao')) {
        mudarEtapa('proposta');
        if (etapaAtual !== 'proposta') setAutoAvancNotif({ de: etapaAtual, para: 'proposta' });
      } else if (ns === 'pedido' && etapaAtual !== 'pedido' && etapaAtual !== 'fechamento_parcial' && etapaAtual !== 'fechado') {
        mudarEtapa('pedido');
        if (etapaAtual !== 'pedido') setAutoAvancNotif({ de: etapaAtual, para: 'pedido' });
      } else if (ns === 'pedido_assinado') {
        // Cliente acabou de assinar — avança para crédito e dispara splash
        if (etapaAtual !== 'fechamento_parcial' && etapaAtual !== 'fechado' && etapaAtual !== 'credito') {
          mudarEtapa('credito');
        }
        var splashKey = 'crm_ped_assinado_' + opId;
        if (!sessionStorage.getItem(splashKey)) {
          sessionStorage.setItem(splashKey, '1');
          setShowPedAssinado(true);
        }
      }
    }
    function onAbrirFechamento(e) {
      if (e.detail && e.detail.opId === opId) {
        setModalFechamento(true);
      }
    }
    // M: Escuta novas atividades do cliente (independente de op_id)
    function onAtividadeAdded(e) {
      if (!e.detail) return;
      var atv = e.detail.atividade || e.detail;
      if (!atv || !atv.id) return;
      var opAtual = (CRM_DATA.oportunidades || []).find(function(o) { return o.id === opId; });
      var clienteId = opAtual && opAtual.cliente_id;
      if (!clienteId || atv.cliente_id !== clienteId) return;
      setAtividades(function(prev) {
        if (prev.some(function(a) { return a.id === atv.id; })) return prev; // dedup
        return [atv].concat(prev).sort(function(a, b) { return new Date(b.data) - new Date(a.data); });
      });
    }
    window.addEventListener('crm_prop_itens', onPropItens);
    window.addEventListener('crm_prop_deleted', onPropDeleted);
    window.addEventListener('crm_prop_status_changed', onPropStatusChanged);
    window.addEventListener('crm_abrir_fechamento', onAbrirFechamento);
    window.addEventListener('crm_atividade_added', onAtividadeAdded);
    return function() {
      window.removeEventListener('crm_prop_itens', onPropItens);
      window.removeEventListener('crm_prop_deleted', onPropDeleted);
      window.removeEventListener('crm_prop_status_changed', onPropStatusChanged);
      window.removeEventListener('crm_abrir_fechamento', onAbrirFechamento);
      window.removeEventListener('crm_atividade_added', onAtividadeAdded);
    };
  }, [opId]);
  const [pendingItem, setPendingItem] = useOpState(null);
  const [showQtd, setShowQtd] = useOpState(false);
  const [editingLocalId, setEditingLocalId] = useOpState(null);
  const [modalProp, setModalProp] = useOpState(false);
  const [modalSelecionarItens, setModalSelecionarItens] = useOpState(false);
  const [modalEditar, setModalEditar] = useOpState(false);
  const [modalExcluirOp, setModalExcluirOp] = useOpState(false);
  const [modalFechamento,    setModalFechamento]    = useOpState(false);
  const [modalAprovCredito,  setModalAprovCredito]  = useOpState(false);
  const [autoAvancNotif,     setAutoAvancNotif]     = useOpState(null);  // null | { de, para }
  const [showCongrats,       setShowCongrats]       = useOpState(null);  // null | 'fechado' | 'fechamento_parcial'
  const [showPedAssinado,    setShowPedAssinado]    = useOpState(false); // splash
  const [showFecharAposCredito, setShowFecharAposCredito] = useOpState(false); "pedido assinado pelo cliente"

  // ── Enriquecimento CNPJ ──────────────────────────────
  const [cnpjData,   setCnpjData]   = useOpState(null);   // dados públicos Receita
  const [cnpjStatus, setCnpjStatus] = useOpState('idle'); // idle|loading|ok|err|unavail
  const [cnpjErr,    setCnpjErr]    = useOpState('');
  const [aceitando,  setAceitando]  = useOpState(false);  // salvando sugestões
  const [cnpjDismissed, setCnpjDismissed] = useOpState([]); // campos descartados

  // ── Frota do cliente (tick = força re-render após add/edit) ──
  const [frotaTick,      setFrotaTick]      = useOpState(0);
  const [maqFrotaOpen,   setMaqFrotaOpen]   = useOpState(false);
  const [maqFrotaEditar, setMaqFrotaEditar] = useOpState(null);
  const [showSimIframe, setShowSimIframe] = useOpState(false);
  const [simIframeUrl,  setSimIframeUrl]  = useOpState("");

  // ── Listener postMessage do simulador ────────────────
  useOpEffect(() => {
    function handleSimMessage(e) {
      // Ignora se o simulador foi aberto pela proposta (não pela oportunidade)
      if (window._baukoPropSimActive) return;
      if (e.data && e.data.type === 'CRM_ITEM_DATA') {
        setPendingItem(e.data.data);
        setShowQtd(true);
        setShowSimIframe(false);
      }
    }
    window.addEventListener('message', handleSimMessage);
    return () => window.removeEventListener('message', handleSimMessage);
  }, []);

  // ── Carregar itens do SP quando a oportunidade abre ─────
  useOpEffect(() => {
    if (window.CRM_API && typeof window.CRM_API.loadItensByOp === 'function') {
      CRM_API.loadItensByOp(opId)
        .then(function(loaded) {
          if (loaded && loaded.length > 0) {
            setItens(loaded.map(function(it) {
              return Object.assign({}, it, {
                _localId: it._localId || ('SP-' + (it._spId || it.id || Math.random())),
              });
            }));
          }
        })
        .catch(function(e) {
          console.warn('[CRM] loadItensByOp:', e.message);
        });
    }
  }, [opId]);

  // ── Re-init lucide icons when itens list changes ──────────
  useOpEffect(() => {
    // setTimeout 0 garante que o DOM foi atualizado antes de chamar createIcons
    var t = setTimeout(function() {
      try {
        if (window.lucide && typeof window.lucide.createIcons === 'function') {
          window.lucide.createIcons({ attrs: { 'stroke-width': 1.75 } });
        }
      } catch(e) { /* DOM já alterado por outra reconciliação; ignora pra não travar */ }
    }, 0);
    return function() { clearTimeout(t); };
  }, [itens]);

  // Hard block: comercial não pode abrir oportunidade alheia
  // (mesmo via URL/link antigo). Calcula antes do early return de !op para
  // manter ordem estável dos hooks.
  var _podeVerOpAlheia = !!(window.CRM_USER && window.CRM_USER.podeVerOpAlheia);
  var _meuUserId       = (window.CRM_USER && window.CRM_USER.userId) || null;
  var _accessDenied    = !!(op && !_podeVerOpAlheia && op.vendedor_id !== _meuUserId);

  React.useEffect(function() {
    if (!_accessDenied) return;
    if (typeof window.CRM_TOAST === 'function') {
      window.CRM_TOAST('Você não tem acesso a esta oportunidade — ela é de outro vendedor.', 'warn');
    }
    // Pequeno delay para o toast aparecer antes do redirect
    var t = setTimeout(function(){ if (onBack) onBack(); }, 300);
    return function() { clearTimeout(t); };
  }, [_accessDenied]);

  // Detecta pedido já assinado ao abrir a oportunidade (ex: vendedor abre depois de receber e-mail de notificação)
  React.useEffect(function() {
    if (!opId || !propostas.length || !op) return;
    var jaAssinado = propostas.some(function(p) { return p.status === 'pedido_assinado'; });
    if (!jaAssinado) return;
    // Avança para etapa de crédito se ainda não estiver lá
    var etapaAtual = op.etapa || 'lead';
    if (etapaAtual !== 'credito' && etapaAtual !== 'fechamento_parcial' && etapaAtual !== 'fechado') {
      mudarEtapa('credito');
    }
    // Exibe splash uma única vez por sessão
    var splashKey = 'crm_ped_assinado_' + opId;
    if (!sessionStorage.getItem(splashKey)) {
      sessionStorage.setItem(splashKey, '1');
      setShowPedAssinado(true);
    }
  }, [opId, propostas]);

  // useMemo DEVE ficar antes de qualquer early return (regra dos hooks)
  var cnpjSugestoes = React.useMemo(function() {
    if (!cnpjData || !op) return [];
    var cliMemo = CRM_DATA.getCliente(op.cliente_id);
    if (!cliMemo) return [];
    return [
      { campo: 'fone',     label: 'Telefone', pub: cnpjData.telefone,       crm: cliMemo.fone     },
      { campo: 'email',    label: 'E-mail',   pub: cnpjData.email,          crm: cliMemo.email    },
      { campo: 'cidade',   label: 'Cidade',   pub: cnpjData.municipio_nome, crm: cliMemo.cidade   },
      { campo: 'uf',       label: 'UF',       pub: cnpjData.uf,             crm: cliMemo.uf       },
      { campo: 'endereco', label: 'Endereço', pub: cnpjData.logradouro ? (cnpjData.logradouro + (cnpjData.numero ? ', ' + cnpjData.numero : '') + (cnpjData.bairro ? ' — ' + cnpjData.bairro : '')) : '', crm: cliMemo.endereco },
      { campo: 'cep',      label: 'CEP',      pub: cnpjData.cep,            crm: cliMemo.cep      },
    ].filter(function(s) { return s.pub && !s.crm; });
  }, [cnpjData, op]);

  // Guard: op pode ser undefined se dados ainda não carregaram ou opId não existe
  if (!op) {
    return (
      <div style={{ display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', height:'60vh', gap:12, color:'var(--tx-3)' }}>
        <i data-lucide="search-x" style={{ width:36, height:36, color:'var(--border-2)' }}></i>
        <div style={{ font:'500 15px/1 var(--ff-body)' }}>Oportunidade não encontrada</div>
        <div style={{ font:'400 12px/1.5 var(--ff-body)', textAlign:'center', maxWidth:260 }}>Os dados ainda podem estar carregando. Tente novamente em instantes.</div>
        {onBack && <button className="btn btn-ghost" style={{ marginTop:8 }} onClick={onBack}>← Voltar ao pipeline</button>}
      </div>
    );
  }

  // Acesso negado: oportunidade de outro vendedor
  if (_accessDenied) {
    return (
      <div style={{ display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', height:'60vh', gap:12, color:'var(--tx-3)' }}>
        <i data-lucide="lock" style={{ width:36, height:36, color:'var(--border-2)' }}></i>
        <div style={{ font:'500 15px/1 var(--ff-body)' }}>Acesso restrito</div>
        <div style={{ font:'400 12px/1.5 var(--ff-body)', textAlign:'center', maxWidth:320 }}>
          Esta oportunidade é de outro vendedor e não está na sua carteira.
        </div>
        {onBack && <button className="btn btn-ghost" style={{ marginTop:8 }} onClick={onBack}>← Voltar ao pipeline</button>}
      </div>
    );
  }

  const cli  = CRM_DATA.getCliente(op.cliente_id);
  const vend = CRM_DATA.getUsuario(op.vendedor_id);

  // ── Busca dados públicos via CNPJ API ────────────────
  async function buscarDadosPublicos() {
    if (!cli || !cli.cnpj) return;
    var fn = typeof fetchEmpresaCNPJ !== 'undefined' ? fetchEmpresaCNPJ : null;
    if (!fn) { setCnpjStatus('unavail'); return; }
    setCnpjStatus('loading'); setCnpjErr('');
    try {
      var d = await fn(cli.cnpj);
      setCnpjData(d); setCnpjStatus('ok');
    } catch(e) {
      if ((e.message || '').includes('não configurado')) { setCnpjStatus('unavail'); }
      else { setCnpjErr(e.message || 'Erro'); setCnpjStatus('err'); }
    }
  }

  // cnpjSugestoes declarado antes dos guards early-return (regra dos hooks)

  // ── Aceita todas as sugestões → PATCH Clientes SP ───
  async function aceitarSugestoes() {
    if (!cli || !cli._spId || !cnpjSugestoes.length) return;
    setAceitando(true);
    try {
      var patch = {};
      cnpjSugestoes.forEach(function(s) { patch[s.campo] = s.pub; });
      await CRM_API.updateItem('Clientes', cli._spId, patch);
      Object.keys(patch).forEach(function(k) { cli[k] = patch[k]; });
      setCnpjData(Object.assign({}, cnpjData)); // força re-render das sugestões (agora zeradas)
      if (typeof window.CRM_TOAST === 'function') window.CRM_TOAST(Object.keys(patch).length + ' campo(s) atualizados no cadastro do cliente.', 'ok');
    } catch(e) {
      if (typeof window.CRM_TOAST === 'function') window.CRM_TOAST('Erro ao atualizar: ' + (e.message || e), 'error');
    } finally {
      setAceitando(false);
    }
  }


  // Aceita uma sugestao individual
  async function aceitarSugestaoSingle(s) {
    if (!cli || !cli._spId) return;
    setAceitando(true);
    try {
      var patch = {}; patch[s.campo] = s.pub;
      await CRM_API.updateItem('Clientes', cli._spId, patch);
      cli[s.campo] = s.pub;
      setCnpjData(Object.assign({}, cnpjData));
      if (typeof window.CRM_TOAST === 'function') window.CRM_TOAST(s.label + ' atualizado.', 'ok');
    } catch(e) {
      if (typeof window.CRM_TOAST === 'function') window.CRM_TOAST('Erro ao atualizar: ' + (e.message || e), 'error');
    } finally { setAceitando(false); }
  }
  const ETAPAS_LIST   = ['lead','qualificacao','proposta','pedido','credito','fechamento_parcial','fechado'];
  const ETAPAS_LABELS = { lead:'Lead', qualificacao:'Qualificação', proposta:'Proposta enviada', pedido:'Pedido', credito:'Crédito', fechamento_parcial:'Ganho Parcial', fechado:'Fechado' };
  const PROB_BY_ETAPA = { lead:10, qualificacao:20, proposta:40, pedido:65, credito:80, fechamento_parcial:90, fechado:100 };
  const etapaIdx = ETAPAS_LIST.indexOf(op.etapa);

  var isGerente = !!(window.CRM_USER && window.CRM_USER.isGerente);
  var ETAPAS_FECHADAS_SET = new Set(['fechamento_parcial', 'fechado']);

  function mudarEtapa(novaEtapa) {
    // Lock: oportunidades fechadas só gerentes podem reabrir
    if (ETAPAS_FECHADAS_SET.has(op.etapa) && !ETAPAS_FECHADAS_SET.has(novaEtapa) && !isGerente) {
      window.CRM_TOAST('Esta oportunidade está fechada. Apenas gerentes podem reabri-la.', 'warn');
      return;
    }
    // Lock: não permite retroceder de crédito (05) para pedido ou antes se houver pedido assinado
    var novaIdx  = ETAPAS_LIST.indexOf(novaEtapa);
    var credIdx  = ETAPAS_LIST.indexOf('credito');
    // I-D8: lê do CRM_DATA (fonte global) ao invés do estado local, evita race entre sessões
    var propostasGlobais = (CRM_DATA.propostas || []).filter(function(p){ return p.op_id === op.id; });
    var temPedAssinado = propostasGlobais.some(function(p) { return p.status === 'pedido_assinado'; });
    if (temPedAssinado && novaIdx < credIdx) {
      window.CRM_TOAST('Existe um pedido assinado nesta oportunidade. Para corrigir, cancele o pedido dentro da proposta e reemita.', 'warn');
      return;
    }
    var novaProb = PROB_BY_ETAPA[novaEtapa];
    // J-D4: só sobrescreve probabilidade se ainda está no default da etapa atual (não foi ajustada manualmente)
    var probFoiCustomizada = op.probabilidade !== undefined && op.probabilidade !== PROB_BY_ETAPA[op.etapa];
    var updates = (novaProb !== undefined && !probFoiCustomizada)
      ? { etapa: novaEtapa, probabilidade: novaProb }
      : { etapa: novaEtapa };
    // Se regredir abaixo de 'credito', limpa aprovação de crédito — ao reentrar em 'credito'
    // o vendedor precisa registrar novamente (evita botão "Fechar Oportunidade" prematuro).
    if (novaIdx < credIdx) {
      updates.credito_aprovado_em   = null;
      updates.credito_financiamento = null;
      updates.credito_data_entrega  = null;
      updates.credito_obs           = null;
    }
    setOp(function(prev){ return Object.assign({}, prev, updates); });
    // Lê _spId do CRM_DATA para evitar stale closure em listeners assíncronos
    var liveOp = CRM_DATA.oportunidades.find(function(o){ return o.id === op.id; });
    var spId = (liveOp && liveOp._spId) || op._spId;
    if (window.CRM_API && spId) {
      CRM_API.updateItem('Oportunidades', spId, updates)
        .catch(function(e){ console.warn('[CRM] mudarEtapa SP:', e.message); });
    }
    var mEIdx = CRM_DATA.oportunidades.findIndex(function(o){ return o.id === op.id; });
    if (mEIdx >= 0) CRM_DATA.oportunidades[mEIdx] = Object.assign({}, CRM_DATA.oportunidades[mEIdx], updates);
  }

  function avancarEtapa() {
    if (op.etapa === 'credito') { setModalFechamento(true); return; }
    const next = ETAPAS_LIST[Math.min(etapaIdx + 1, ETAPAS_LIST.length - 1)];
    mudarEtapa(next);
  }

  function confirmarAprovCredito(form) {
    // Persiste campos de crédito na oportunidade (já estamos em etapa 'credito')
    var liveOp = CRM_DATA.oportunidades.find(function(o){ return o.id === op.id; });
    var spId = (liveOp && liveOp._spId) || op._spId;
    var creditUpdates = {
      credito_financiamento: form.financiamento,
      credito_data_entrega:  form.data_entrega,
      credito_obs:           form.obs || '',
      credito_aprovado_em:   new Date().toISOString(),
    };
    setOp(function(prev){ return Object.assign({}, prev, creditUpdates); });
    var mIdx = CRM_DATA.oportunidades.findIndex(function(o){ return o.id === op.id; });
    if (mIdx >= 0) CRM_DATA.oportunidades[mIdx] = Object.assign({}, CRM_DATA.oportunidades[mIdx], creditUpdates);
    if (window.CRM_API && spId) {
      CRM_API.updateItem('Oportunidades', spId, creditUpdates)
        .catch(function(e){ console.warn('[CRM] credito patch:', e.message); });
    }

    // 3. Adiciona evento na timeline da proposta em status 'pedido'
    var propPedido = propostas.find(function(p){ return p.status === 'pedido'; });
    if (propPedido) {
      var autor = window.CRM_USER ? CRM_USER.nome : '';
      var entregaLabel = form.data_entrega ? form.data_entrega.replace('-', '/') : '';
      var novoEvento = {
        id: Date.now().toString(36) + Math.random().toString(36).slice(2,5),
        tipo: 'credito_aprovado',
        data: new Date().toISOString(),
        detalhe: 'Crédito aprovado — ' + form.financiamento + (entregaLabel ? ' · Entrega prevista: ' + entregaLabel : ''),
        autor: autor,
      };
      var novaTimeline = (propPedido.timeline || []).concat([novoEvento]);
      setPropostas(function(prev){ return prev.map(function(p){ return p.id === propPedido.id ? Object.assign({}, p, { timeline: novaTimeline }) : p; }); });
      if (propPedido._spId && window.CRM_API) {
        var propIdx = CRM_DATA.propostas ? CRM_DATA.propostas.findIndex(function(p){ return p.id === propPedido.id; }) : -1;
        if (propIdx >= 0) CRM_DATA.propostas[propIdx] = Object.assign({}, CRM_DATA.propostas[propIdx], { timeline: novaTimeline });
        CRM_API.updateItem('Propostas', propPedido._spId, { timeline: novaTimeline })
          .catch(function(e){ console.warn('[CRM] timeline credito:', e.message); });
      }
    }

    setModalAprovCredito(false);
    setShowFecharAposCredito(true);
  }

  // ── Abrir simulador em popup ──────────────────────────
  function abrirSimulador(itemExistente) {
    setEditingLocalId(itemExistente ? (itemExistente._localId || null) : null);
    var baseUrl = window.location.origin + '/hub/simulador.html';
    var url = baseUrl + '?mode=crm&op_id=' + encodeURIComponent(op.id);
    if (itemExistente && itemExistente._spId) {
      url += '&item_id=' + encodeURIComponent(itemExistente.id || '');
    }
    if (itemExistente && itemExistente.sim_params_json) {
      window._crmRestoreParams = itemExistente.sim_params_json;
      url += '&restore=1';
    } else {
      window._crmRestoreParams = null;
    }
    setSimIframeUrl(url);
    setShowSimIframe(true);
  }

  // ── Confirmar item vindo do simulador ─────────────────
  async function confirmarItem(qtd) {
    if (!pendingItem) return;
    const { id: _drop, item_id: _drop2, tabelas: _drop3, ...itemBase } = pendingItem;
    const isEdit = !!editingLocalId;
    var itemFinal = {
      ...itemBase,
      op_id:       op.id,
      cliente_id:  op.cliente_id,
      quantidade:  qtd,
      valor_total: (pendingItem.valor_unitario || 0) * qtd,
    };
    // Busca specs do SP e embute no item — uma chamada por fabricante (cache em memória)
    if (window.CRM_API && itemFinal.fabricante && itemFinal.modelo) {
      try {
        var specsJson = await CRM_API.getSpecsForItem(itemFinal.fabricante, itemFinal.modelo);
        if (specsJson) {
          itemFinal = Object.assign({}, itemFinal, { specs: specsJson });
          // Grava o Resumo do SP como descricao_tecnica — vai para a proposta e o pedido públicos
          if (specsJson.resumo && !itemFinal.descricao_tecnica) {
            itemFinal.descricao_tecnica = specsJson.resumo;
          }
        }
      } catch(e) {
        console.warn('[CRM] specs lookup falhou (não crítico):', e.message);
      }
    }
    if (isEdit) {
      setItens(prev => prev.map(it => {
        if (it._localId !== editingLocalId) return it;
        return { ...itemFinal, _localId: it._localId, _spId: it._spId };
      }));
      if (window.CRM_API) {
        try {
          var lists = window.BAUKO_AUTH && window.BAUKO_AUTH.SHAREPOINT && window.BAUKO_AUTH.SHAREPOINT.lists;
          var spId = itens.find(it => it._localId === editingLocalId)?._spId;
          if (lists && lists.Itens && spId) {
            CRM_API.updateItem('Itens', spId, itemFinal).catch(function(e) {
              console.error('[CRM] updateItem Itens FALHOU:', e.message, e);
              var modelo = itemFinal.modelo || itemFinal.sku || 'Item';
              if (typeof window.CRM_TOAST === 'function') {
                window.CRM_TOAST('⚠ Alteração em "' + modelo + '" não foi salva no servidor.', 'error', [{
                  label: 'Tentar novamente',
                  fn: function() {
                    CRM_API.updateItem('Itens', spId, itemFinal)
                      .then(function() {
                        if (typeof window.CRM_TOAST === 'function') {
                          window.CRM_TOAST('"' + modelo + '" atualizado com sucesso.', 'ok');
                        }
                      })
                      .catch(function(e2) {
                        console.error('[CRM] retry updateItem Itens FALHOU:', e2.message);
                        if (typeof window.CRM_TOAST === 'function') {
                          window.CRM_TOAST('Falha persistente ao atualizar "' + modelo + '". Verifique a conexão.', 'error');
                        }
                      });
                  }
                }]);
              }
            });
          }
        } catch(e) {}
      }
    } else {
      // J-D3: avisa se mesmo modelo+fabricante já existe na oportunidade (adição acidental de duplicata)
      var skuDup = itens.find(function(it) {
        return it.modelo && it.modelo === itemFinal.modelo && it.fabricante === itemFinal.fabricante;
      });
      if (skuDup) {
        var okDup = await window.CRM_CONFIRM(
          '"' + (itemFinal.modelo || itemFinal.sku || 'Este item') + '" já está nesta oportunidade. Adicionar mesmo assim?',
          { confirmLabel: 'Adicionar mesmo assim', danger: false }
        );
        if (!okDup) { setShowQtd(false); setPendingItem(null); setEditingLocalId(null); return; }
      }
      const novoItem = { ...itemFinal, _localId: 'LOCAL-' + Date.now() };
      setItens(prev => [...prev, novoItem]);
      if (window.CRM_API) {
        try {
          var lists = window.BAUKO_AUTH && window.BAUKO_AUTH.SHAREPOINT && window.BAUKO_AUTH.SHAREPOINT.lists;
          if (lists && lists.Itens) {
            CRM_API.addItem('Itens', novoItem)
              .then(function(created) {
                if (created && created.id) {
                  var spId = String(created.id);
                  // Atualiza _spId após confirmação do SP
                  setItens(function(prev) {
                    return prev.map(function(it) {
                      if (it._localId !== novoItem._localId) return it;
                      return Object.assign({}, it, { _spId: spId });
                    });
                  });
                }
              })
              .catch(function(e) {
                console.error('[CRM] addItem Itens FALHOU:', e.message, e);
                var modelo = novoItem.modelo || novoItem.sku || 'Item';
                if (typeof window.CRM_TOAST === 'function') {
                  window.CRM_TOAST(
                    '⚠ "' + modelo + '" não foi salvo no servidor.',
                    'error',
                    [{
                      label: 'Tentar novamente',
                      fn: function() {
                        CRM_API.addItem('Itens', novoItem)
                          .then(function(created) {
                            if (created && created.id) {
                              var spId = String(created.id);
                              setItens(function(prev) {
                                return prev.map(function(it) {
                                  if (it._localId !== novoItem._localId) return it;
                                  return Object.assign({}, it, { _spId: spId });
                                });
                              });
                              if (typeof window.CRM_TOAST === 'function') {
                                window.CRM_TOAST('"' + modelo + '" salvo com sucesso.', 'ok');
                              }
                            }
                          })
                          .catch(function(e2) {
                            console.error('[CRM] retry addItem Itens FALHOU:', e2.message);
                            if (typeof window.CRM_TOAST === 'function') {
                              window.CRM_TOAST('Falha persistente ao salvar "' + modelo + '". Verifique a conexão.', 'error');
                            }
                          });
                      }
                    }]
                  );
                }
              });
          }
        } catch(e) {}
      }
    }
    setEditingLocalId(null);
    setPendingItem(null);
    setShowQtd(false);
    // Auto-avança para Qualificação ao adicionar a primeira máquina (não em edição)
    if (!isEdit) {
      var opAtual2 = CRM_DATA.oportunidades.find(function(o){ return o.id === op.id; }) || op;
      if (opAtual2.etapa === 'lead') mudarEtapa('qualificacao');
    }
  }

  async function removerItem(localId) {
    const item = itens.find(function(it){ return it._localId === localId; });
    if (!item) return;

    // Propostas desta oportunidade que contêm este item
    var propsDaOp = (CRM_DATA.propostas || []).filter(function(p){ return p.op_id === opId; });
    var propostasAfetadas = propsDaOp.filter(function(prop) {
      var propItens = (CRM_DATA.itens_proposta && CRM_DATA.itens_proposta[prop.id])
        || (prop.itens || []);
      return propItens.some(function(it){ return it._localId === localId; });
    });

    // Monta mensagem de confirmação
    var modeloLabel = item.modelo || item.sku || 'esta máquina';
    var msg = 'Remover ' + modeloLabel + ' da oportunidade?';
    if (propostasAfetadas.length > 0) {
      var ids = propostasAfetadas.map(function(p){ return p.id; }).join(', ');
      msg += '\n\nAtenção: ' + propostasAfetadas.length + ' proposta(s) contendo esta máquina serão excluídas:\n' + ids;
    }
    if (!await window.CRM_CONFIRM(msg, { danger: true, confirmLabel: 'Remover', title: 'Remover equipamento' })) return;

    // Exclui as propostas afetadas
    propostasAfetadas.forEach(function(prop) {
      // Remove do CRM_DATA
      if (CRM_DATA.propostas) {
        var idx = CRM_DATA.propostas.findIndex(function(p){ return p.id === prop.id; });
        if (idx >= 0) CRM_DATA.propostas.splice(idx, 1);
      }
      // Deleta no SP (fire and forget)
      if (prop._spId && window.CRM_API) {
        CRM_API.deleteItem('Propostas', prop._spId).catch(function(e){ console.warn('[CRM] delete proposta cascade:', e.message); });
      }
      // Notifica modais (OportunidadeModal filtra da lista, PropostaModal fecha se aberta)
      window.dispatchEvent(new CustomEvent('crm_prop_deleted', {
        detail: { propId: prop.id, opId: opId }
      }));
    });

    // Remove o item da oportunidade no SP
    if (item._spId && window.CRM_API) {
      CRM_API.deleteItem('Itens', item._spId).catch(function(e){ console.warn('[CRM] deleteItem Itens:', e.message); });
    }
    setItens(function(prev){ return prev.filter(function(it){ return it._localId !== localId; }); });
  }

  // Abre o modal global de atividade (AtividadeModalGlobal — com IA)
  // O modal já salva no SP e em CRM_DATA; onSalvo só atualiza o state local
  function _abrirAtividade() {
    if (typeof window.CRM_OPEN_ATIVIDADE !== 'function') {
      if (typeof window.CRM_TOAST === 'function') window.CRM_TOAST('Modal de atividade indisponível.', 'warn');
      return;
    }
    window.CRM_OPEN_ATIVIDADE({
      opId:       op.id,
      clienteId:  op.cliente_id,
      onSalvo: function(atv) {
        setAtividades(function(prev){ return [atv].concat(prev); });
      }
    });
  }

  // Normaliza item da oportunidade para formato de proposta
  function normalizarItemParaProposta(it) {
    const sku  = it.modelo || it.fab || 'ITEM';
    const desc = ((it.familia || '') + ' ' + (it.modelo || '')).trim() || it.desc || sku;
    const unit = it.valor_unitario || 0;
    const tot  = it.valor_total || 0;
    return {
      ...it,
      sku, desc,
      qtd:   it.quantidade || it.qtd || 1,
      unit, total: tot,
      composicao: it.composicao || [
        { desc: desc + ' · ' + (it.tabela || 'T1'), qtd: 1, unit, total: unit },
        ...(it.opcionais && it.opcionais.trim() ? [{ desc: 'Opcionais: ' + it.opcionais, informativo: true }] : []),
        ...(it.frete > 0  ? [{ desc: 'Frete ' + (it.frete_tipo || ''), informativo: true }] : []),
        ...(it.pmp > 0    ? [{ desc: 'PMP · ' + (it.pmp_desc || 'pagamento antecipado'), informativo: true }] : []),
        ...(it.extras_itens && it.extras_itens.length > 0
          ? it.extras_itens.filter(function(ex){return ex.v>0;}).map(function(ex){return {desc:(ex.d||'Extra'),informativo:true};})
          : (it.extras > 0 ? [{desc:'Extras adicionais',informativo:true}] : [])),
        ...(it.subsidio > 0 ? [{ desc: 'Subsídio aplicado', informativo: true }] : []),
      ],
    };
  }

  function criarEAbrirProposta(itensSelecionados) {
    const itensParaProposta = itensSelecionados || itens;
    const valorItensAtual = itensParaProposta.reduce((s, it) => s + (it.valor_total || 0), 0);
    // Autor do log: sempre o usuário REAL logado, ignora "Visualizar como" (auditoria)
    var autorProp = (window.CRM_USER && window.CRM_USER.realEmail) || '';
    const nova = {
      id: (function(){ var d=new Date(); var yy=String(d.getFullYear()).slice(2); var mm=String(d.getMonth()+1).padStart(2,'0'); var dd=String(d.getDate()).padStart(2,'0'); var rand=Math.random().toString(36).slice(2,6); return 'BKP-'+yy+mm+dd+'-'+rand; })(),
      versao: 'v' + (propostas.length + 1),
      status: 'rascunho',
      desconto: 0,
      data: new Date().toISOString().slice(0, 10),
      validade: new Date(Date.now() + ((window.CRM_CONFIG && CRM_CONFIG.get('proposta_validade_default_dias')) || 30) * 864e5).toISOString().slice(0, 10),
      responsavel_id: op.vendedor_id,
      valor: valorItensAtual || op.valor || 0,
      op_id: op.id,
      timeline: [{
        id: Date.now().toString(36) + Math.random().toString(36).slice(2,5),
        tipo: 'criada',
        data: new Date().toISOString(),
        detalhe: 'Proposta criada com ' + itensParaProposta.length + ' item(s) · ' + fR(valorItensAtual || op.valor || 0),
        autor: autorProp,
      }],
    };
    if (!CRM_DATA.propostas) CRM_DATA.propostas = [];
    CRM_DATA.propostas.unshift(nova);
    if (!CRM_DATA.itens_proposta) CRM_DATA.itens_proposta = {};
    const itensNormalizados = itensParaProposta.map(normalizarItemParaProposta);
    CRM_DATA.itens_proposta[nova.id] = itensNormalizados;
    setPropostas(prev => [nova, ...prev]);
    // Persiste no SharePoint incluindo os itens
    if (window.CRM_API) {
      var lists = window.BAUKO_AUTH && window.BAUKO_AUTH.SHAREPOINT && window.BAUKO_AUTH.SHAREPOINT.lists;
      if (lists && lists.Propostas) {
        CRM_API.addItem('Propostas', { ...nova, itens: itensNormalizados })
          .then(function(created) {
            if (created && created.id) {
              var idx = CRM_DATA.propostas.findIndex(function(p){ return p.id === nova.id; });
              if (idx >= 0) CRM_DATA.propostas[idx]._spId = String(created.id);
            }
          })
          .catch(function(e) { console.warn('[CRM] addItem Propostas (criarEAbrirProposta):', e.message); });
      }
    }
    // Flag para PropostaModal suprimir o sync event no primeiro open
    // (evita loop: proposta abre → event → oportunidade adiciona item duplicado)
    window._baukoPropJustCreated = nova.id;
    onOpenProp && onOpenProp(nova.id);
  }

  function salvarProposta(data) {
    const valorItensAtual = itens.reduce((s, it) => s + (it.valor_total || 0), 0);
    const nova = {
      id: (function(){ var d=new Date(); var yy=String(d.getFullYear()).slice(2); var mm=String(d.getMonth()+1).padStart(2,'0'); var dd=String(d.getDate()).padStart(2,'0'); var rand=Math.random().toString(36).slice(2,6); return 'BKP-'+yy+mm+dd+'-'+rand; })(),
      versao: 'v' + (propostas.length + 1),
      status: 'rascunho',
      desconto: 0,
      data: new Date().toISOString().slice(0, 10),
      validade: new Date(Date.now() + ((window.CRM_CONFIG && CRM_CONFIG.get('proposta_validade_default_dias')) || 30) * 864e5).toISOString().slice(0, 10),
      responsavel_id: op.vendedor_id,
      valor: valorItensAtual,
      op_id: op.id,
      ...data,
    };
    // Publica no CRM_DATA para PropostaModal encontrar pelo id
    if (!CRM_DATA.propostas) CRM_DATA.propostas = [];
    CRM_DATA.propostas.unshift(nova);
    // Associa os itens atuais à proposta
    if (!CRM_DATA.itens_proposta) CRM_DATA.itens_proposta = {};
    CRM_DATA.itens_proposta[nova.id] = itens.map(normalizarItemParaProposta);
    setPropostas(prev => [nova, ...prev]);
    // Persiste no SharePoint incluindo os itens
    if (window.CRM_API) {
      var lists = window.BAUKO_AUTH && window.BAUKO_AUTH.SHAREPOINT && window.BAUKO_AUTH.SHAREPOINT.lists;
      if (lists && lists.Propostas) {
        var itensPropostaSP = CRM_DATA.itens_proposta[nova.id] || [];
        CRM_API.addItem('Propostas', { ...nova, itens: itensPropostaSP })
          .then(function(created) {
            if (created && created.id) {
              var idx = CRM_DATA.propostas.findIndex(function(p){ return p.id === nova.id; });
              if (idx >= 0) CRM_DATA.propostas[idx]._spId = String(created.id);
            }
          })
          .catch(function(e) { console.warn('[CRM] addItem Propostas:', e.message); });
      }
    }
  }

  function salvarEdicaoOp(form) {
    var oldVendId = op.vendedor_id;
    var newVendId = form.vendedor_id;
    // J-D6: detecta troca de responsável (só quando gerente preenche o campo)
    var vendedorMudou = !!(newVendId && newVendId !== oldVendId);

    setOp(prev => ({ ...prev, ...form }));

    // Mantém CRM_DATA.oportunidades sincronizado (outras telas leem daí)
    var opIdx = CRM_DATA.oportunidades ? CRM_DATA.oportunidades.findIndex(function(o){ return o.id === op.id; }) : -1;
    if (opIdx >= 0) CRM_DATA.oportunidades[opIdx] = Object.assign({}, CRM_DATA.oportunidades[opIdx], form);

    // Persiste no SP se tiver _spId
    if (window.CRM_API && op._spId) {
      CRM_API.updateItem('Oportunidades', op._spId, form).catch(e => console.warn('[CRM] updateItem Op:', e.message));
    } else if (window.CRM_API) {
      console.warn('[CRM] salvarEdicaoOp: op sem _spId — edição não persistida no SP', op.id);
      if (typeof window.CRM_TOAST === 'function') {
        window.CRM_TOAST('⚠ Oportunidade sem ID no servidor — edição salva localmente apenas.', 'warn');
      }
    }

    // J-D6: cascade de responsável → propostas e pedidos
    if (vendedorMudou) {
      // Propostas desta oportunidade
      var propsDestaOp = (CRM_DATA.propostas || []).filter(function(p){ return p.op_id === op.id; });
      propsDestaOp.forEach(function(prop) {
        prop.responsavel_id = newVendId;
        if (prop._spId && window.CRM_API) {
          CRM_API.updateItem('Propostas', prop._spId, { responsavel_id: newVendId })
            .catch(function(e){ console.warn('[CRM] cascade responsavel_id proposta:', e.message); });
        }
      });
      setPropostas(function(prev) {
        return prev.map(function(p) {
          return p.op_id === op.id ? Object.assign({}, p, { responsavel_id: newVendId }) : p;
        });
      });

      // Pedidos vinculados a essas propostas
      var propIds = new Set(propsDestaOp.map(function(p){ return p.id; }));
      var pedidosDestaOp = (CRM_DATA.pedidos || []).filter(function(ped){ return propIds.has(ped.proposta_id); });
      pedidosDestaOp.forEach(function(ped) {
        ped.vendedor_id = newVendId;
        if (ped._spId && window.CRM_API) {
          CRM_API.updateItem('CRMPedidos', ped._spId, { vendedor_id: newVendId })
            .catch(function(e){ console.warn('[CRM] cascade vendedor_id pedido:', e.message); });
        }
      });

      var novoVend = CRM_DATA.getUsuario ? CRM_DATA.getUsuario(newVendId) : null;
      var nomeVend = novoVend ? novoVend.nome : newVendId;
      if (typeof window.CRM_TOAST === 'function') {
        window.CRM_TOAST(
          'Oportunidade reatribuída para ' + nomeVend + '. ' +
          propsDestaOp.length + ' proposta(s) e ' + pedidosDestaOp.length + ' pedido(s) atualizados.',
          'ok'
        );
      }
    }
  }

  // Cancela a oportunidade (soft delete) + cascade pra propostas e pedidos.
  // Não toca SP de itens/atividades — eles ficam vinculados à oportunidade cancelada.
  async function handleCancelarOportunidade(motivo) {
    if (!op) return;
    var canceladaEm = new Date().toISOString();
    // Autor do log: sempre o usuário REAL logado (auditoria)
    var autor = (window.CRM_USER && window.CRM_USER.realEmail) || '';

    // 1. Atualiza oportunidade no SP + memória
    if (op._spId && window.CRM_API) {
      try {
        await CRM_API.updateItem('Oportunidades', op._spId, {
          cancelada_em: canceladaEm,
          motivo_cancelamento: motivo,
        });
      } catch(e) {
        console.warn('[CRM] updateItem Oportunidades cancelamento:', e.message);
      }
    }
    if (CRM_DATA.oportunidades) {
      var idxOp = CRM_DATA.oportunidades.findIndex(function(o){ return o.id === op.id; });
      if (idxOp >= 0) CRM_DATA.oportunidades[idxOp] = Object.assign({}, CRM_DATA.oportunidades[idxOp], { cancelada_em: canceladaEm, motivo_cancelamento: motivo });
    }

    // 2. Cascade pra propostas ativas
    var propsAtivas = (CRM_DATA.propostas || []).filter(function(p){
      return p.op_id === op.id && p.status !== 'cancelada';
    });
    var updatePromises = [];
    propsAtivas.forEach(function(prop) {
      var eventoProp = {
        id: Date.now().toString(36) + Math.random().toString(36).slice(2,5),
        tipo: 'cancelada_cascade',
        data: canceladaEm,
        detalhe: 'Proposta cancelada porque a oportunidade ' + op.id + ' foi cancelada. Motivo: ' + motivo,
        autor: autor,
      };
      var novaTimeline = (prop.timeline || []).concat([eventoProp]);
      prop.status = 'cancelada';
      prop.timeline = novaTimeline;
      prop.cancelada_em = canceladaEm;
      prop.motivo_cancelamento = motivo;
      if (prop._spId && window.CRM_API) {
        updatePromises.push(
          CRM_API.updateItem('Propostas', prop._spId, { status: 'cancelada', timeline: novaTimeline })
            .then(function(){ return { ok: true, tipo: 'proposta' }; })
            .catch(function(e){
              console.warn('[CRM] cascade proposta:', e.message);
              return { ok: false, tipo: 'proposta', err: e };
            })
        );
      }
      // 2a. Cascade pra pedidos ativos vinculados a esta proposta
      var pedidosDaProp = (CRM_DATA.pedidos || []).filter(function(ped){
        return ped.proposta_id === prop.id && ped.status !== 'cancelado' &&
               ped.status !== 'cancelado_correcao' && ped.status !== 'cancelado_apos_assinatura';
      });
      pedidosDaProp.forEach(function(ped) {
        var eventoPed = {
          id: Date.now().toString(36) + Math.random().toString(36).slice(2,5),
          tipo: 'cancelado_cascade',
          data: canceladaEm,
          detalhe: 'Pedido cancelado porque a oportunidade ' + op.id + ' foi cancelada. Motivo: ' + motivo,
          autor: autor,
        };
        var novaTimelinePed = (ped.timeline || []).concat([eventoPed]);
        ped.status = 'cancelado';
        ped.timeline = novaTimelinePed;
        ped.cancelado_em = canceladaEm;
        ped.motivo_cancelamento = motivo;
        if (ped._spId && window.CRM_API) {
          updatePromises.push(
            CRM_API.updateItem('CRMPedidos', ped._spId, { status: 'cancelado', timeline: novaTimelinePed })
              .then(function(){ return { ok: true, tipo: 'pedido' }; })
              .catch(function(e){
                console.warn('[CRM] cascade pedido:', e.message);
                return { ok: false, tipo: 'pedido', err: e };
              })
          );
        }
      });
    });
    // H13: rastreia falhas no cascade
    var resultados = await Promise.allSettled(updatePromises);
    var falhas = resultados.filter(function(r){
      return r.status === 'rejected' || (r.value && r.value.ok === false);
    });

    // 3. Notifica componentes
    window.dispatchEvent(new CustomEvent('crm_op_cancelada', { detail: { opId: op.id } }));
    propsAtivas.forEach(function(prop) {
      window.dispatchEvent(new CustomEvent('crm_prop_status_changed', {
        detail: { propId: prop.id, newStatus: 'cancelada', opId: op.id }
      }));
    });
    setModalExcluirOp(false);
    if (typeof window.CRM_TOAST === 'function') {
      if (falhas.length > 0) {
        window.CRM_TOAST('Oportunidade cancelada localmente, mas ' + falhas.length + ' atualização(ões) em cascata falharam no servidor. Recarregue a página para sincronizar.', 'warn');
      } else {
        window.CRM_TOAST('Oportunidade cancelada. ' + propsAtivas.length + ' proposta(s) também canceladas em cascata.', 'ok');
      }
    }
    onBack && onBack();
  }

  async function handleExcluirOportunidade() {
    // J-D2: exclusão definitiva (hard delete + cascade) restrita a gerentes/admins
    if (!isGerente) {
      if (typeof window.CRM_TOAST === 'function') {
        window.CRM_TOAST('Apenas gerentes podem excluir definitivamente uma oportunidade. Use "Cancelar oportunidade".', 'warn');
      }
      return;
    }
    var deletePromises = [];

    // 1. Propostas + pedidos vinculados
    var propsVinculadas = (CRM_DATA.propostas || []).filter(function(p){ return p.op_id === op.id; });
    propsVinculadas.forEach(function(prop) {
      var pedidosDaProp = (CRM_DATA.pedidos || []).filter(function(ped){ return ped.proposta_id === prop.id; });
      pedidosDaProp.forEach(function(ped) {
        if (ped._spId && window.CRM_API) {
          deletePromises.push(
            CRM_API.deleteItem('CRMPedidos', ped._spId)
              .then(function(){ return { ok: true, tipo: 'pedido' }; })
              .catch(function(e){ return { ok: false, tipo: 'pedido', err: e.message }; })
          );
        }
      });
      if (prop._spId && window.CRM_API) {
        deletePromises.push(
          CRM_API.deleteItem('Propostas', prop._spId)
            .then(function(){ return { ok: true, tipo: 'proposta' }; })
            .catch(function(e){ return { ok: false, tipo: 'proposta', err: e.message }; })
        );
      }
      window.dispatchEvent(new CustomEvent('crm_prop_deleted', { detail: { propId: prop.id, opId: op.id } }));
    });

    // 2. Itens — usa state local; fallback para CRM_DATA.itens_op se state ainda vazio
    var itensParaExcluir = (itens && itens.length > 0)
      ? itens
      : ((CRM_DATA.itens_op && CRM_DATA.itens_op[op.id]) || []);
    itensParaExcluir.forEach(function(it) {
      if (it._spId && window.CRM_API) {
        deletePromises.push(
          CRM_API.deleteItem('Itens', it._spId)
            .then(function(){ return { ok: true, tipo: 'item' }; })
            .catch(function(e){ return { ok: false, tipo: 'item', err: e.message }; })
        );
      }
    });

    // 3. Atividades
    var ativsVinculadas = (CRM_DATA.atividades || []).filter(function(a){ return a.op_id === op.id; });
    ativsVinculadas.forEach(function(at) {
      if (at._spId && window.CRM_API) {
        deletePromises.push(
          CRM_API.deleteItem('Atividades', at._spId)
            .then(function(){ return { ok: true, tipo: 'atividade' }; })
            .catch(function(e){ return { ok: false, tipo: 'atividade', err: e.message }; })
        );
      }
    });

    // 4. Oportunidade
    if (op._spId && window.CRM_API) {
      deletePromises.push(
        CRM_API.deleteItem('Oportunidades', op._spId)
          .then(function(){ return { ok: true, tipo: 'oportunidade' }; })
          .catch(function(e){ return { ok: false, tipo: 'oportunidade', err: e.message }; })
      );
    }

    var resultados = await Promise.allSettled(deletePromises);
    var falhas = resultados.map(function(r){ return r.value; }).filter(function(v){ return v && !v.ok; });

    // Limpa memória após aguardar SP
    if (CRM_DATA.pedidos) {
      propsVinculadas.forEach(function(prop) {
        CRM_DATA.pedidos = CRM_DATA.pedidos.filter(function(ped){ return ped.proposta_id !== prop.id; });
      });
    }
    if (CRM_DATA.propostas) CRM_DATA.propostas = CRM_DATA.propostas.filter(function(p){ return p.op_id !== op.id; });
    if (CRM_DATA.atividades) CRM_DATA.atividades = CRM_DATA.atividades.filter(function(a){ return a.op_id !== op.id; });
    if (CRM_DATA.oportunidades) CRM_DATA.oportunidades = CRM_DATA.oportunidades.filter(function(o){ return o.id !== op.id; });

    if (falhas.length > 0) {
      console.warn('[CRM] handleExcluirOportunidade: falhas no SP:', falhas);
      if (typeof window.CRM_TOAST === 'function') {
        var tipos = [...new Set(falhas.map(function(f){ return f.tipo; }))].join(', ');
        window.CRM_TOAST('⚠ Oportunidade removida localmente, mas ' + falhas.length + ' item(s) (' + tipos + ') não foram excluídos no servidor.', 'warn');
      }
    }

    setModalExcluirOp(false);
    onBack && onBack();
  }

  const statusPropMap = {
    rascunho:             { cls: 'bx', label: 'Rascunho' },
    enviada:              { cls: 'bw', label: 'Aguard. envio' },
    aprovada:             { cls: 'bo', label: 'Aprovada' },
    aguardando_aprovacao: { cls: 'bi', label: 'Aguard. aprovação' },
    em_negociacao:        { cls: 'bp', label: 'Em negociação' },
    pedido:               { cls: 'bo', label: 'Pedido emitido' },
    aceita:               { cls: 'bo', label: 'Aceita' },
    perdida:              { cls: 'bd', label: 'Perdida' },
  };
  const valorItens = itens.reduce((s, it) => s + (it.valor_total || 0), 0);
  const previsaoInfo = diasRestantes(op.previsao_fechamento);

  return (
    <div data-screen-label="03 Oportunidade">
      {/* Toolbar */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 16, marginBottom: 20, flexWrap: 'wrap' }}>
        <button className="btn btn-secondary" onClick={onBack} style={{ gap: 6 }}>
          <i data-lucide="arrow-left"></i> Pipeline
        </button>
        <div style={{ font: '400 13px/1 var(--ff-body)', color: 'var(--tx-3)' }}>
          {op.id} <span style={{ margin: '0 4px' }}>·</span>
          <span style={{ color: 'var(--tx-2)', fontWeight: 500 }}>{op.titulo}</span>
        </div>
        <div style={{ marginLeft: 'auto', display: 'flex', gap: 8 }}>
          {!CRM_DATA.isOportunidadeCancelada(op) && (
            <>
              <button className="btn btn-ghost" onClick={() => setModalEditar(true)} style={{ fontSize: 12, padding: '8px 12px' }}>
                <i data-lucide="edit-2"></i> Editar
              </button>
              {op.etapa !== 'fechado' && (
                <button className="btn btn-primary"
                  onClick={
                    op.etapa === 'fechamento_parcial' || (op.etapa === 'credito' && op.credito_aprovado_em)
                      ? function(){ setModalFechamento(true); }
                      : op.etapa === 'credito'
                        ? function(){ setModalAprovCredito(true); }
                        : avancarEtapa
                  }>
                  <i data-lucide={
                    op.etapa === 'fechamento_parcial' || (op.etapa === 'credito' && op.credito_aprovado_em)
                      ? 'flag'
                      : op.etapa === 'credito'
                        ? 'check-circle'
                        : 'arrow-right'
                  }></i>
                  {op.etapa === 'fechamento_parcial' || (op.etapa === 'credito' && op.credito_aprovado_em)
                    ? 'Fechar Oportunidade'
                    : op.etapa === 'credito'
                      ? 'Registrar Crédito'
                      : 'Avançar etapa'}
                </button>
              )}
              <button className="btn btn-secondary" onClick={() => _abrirAtividade()}>
                <i data-lucide="activity"></i> Atividade
              </button>
              <button className="btn btn-secondary" onClick={() => setModalSelecionarItens(true)}>
                <i data-lucide="file-plus"></i> Proposta
              </button>
              <button className="btn btn-ghost" onClick={() => setModalExcluirOp(true)} style={{ fontSize: 12, padding: '8px 12px', color: 'var(--warn)', borderColor: 'var(--warn)' }} title="Cancelar oportunidade">
                <i data-lucide="x-circle"></i> Cancelar
              </button>
            </>
          )}
        </div>
      </div>

      {/* Banner: oportunidade cancelada */}
      {CRM_DATA.isOportunidadeCancelada(op) && (
        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 12, padding: '14px 18px', marginBottom: 20, background: '#fef2f2', border: '1px solid var(--danger)', borderRadius: 'var(--r-md)' }}>
          <i data-lucide="x-circle" style={{ width: 20, height: 20, color: 'var(--danger)', flexShrink: 0, marginTop: 2 }}></i>
          <div style={{ flex: 1 }}>
            <div style={{ font: '600 13px/1.3 var(--ff-body)', color: 'var(--danger)' }}>Oportunidade cancelada</div>
            <div style={{ font: '400 12px/1.5 var(--ff-body)', color: 'var(--tx-2)', marginTop: 4 }}>
              {op.motivo_cancelamento ? <><strong>Motivo:</strong> {op.motivo_cancelamento}</> : 'Sem motivo registrado.'}
              {op.cancelada_em && <> · {new Date(op.cancelada_em).toLocaleString('pt-BR')}</>}
            </div>
            <div style={{ font: '400 11px/1.4 var(--ff-body)', color: 'var(--tx-3)', marginTop: 4 }}>
              Esta oportunidade é somente leitura. Propostas e pedidos vinculados também foram cancelados em cascata.
            </div>
          </div>
        </div>
      )}

      {/* Etapa stepper */}
      <div style={{ display: 'flex', gap: 0, marginBottom: 20, background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-lg)', overflow: 'hidden' }}>
        {ETAPAS_LIST.map((e, i) => {
          const isCurr = op.etapa === e;
          const isPast = etapaIdx > i;
          const isPartial = e === 'fechamento_parcial';
          const isClosed = e === 'fechado';
          var currBg    = isPartial ? 'var(--warn-050)'    : 'var(--grn-050)';
          var currColor = isPartial ? 'var(--warn)'        : 'var(--grn)';
          return (
            <div key={e} style={{ flex: 1, padding: '10px 6px', textAlign: 'center', background: isCurr ? currBg : isPast ? 'var(--surface-2)' : 'var(--surface)', borderRight: i < ETAPAS_LIST.length-1 ? '1px solid var(--border)' : 'none', font: '500 10px/1 var(--ff-body)', color: isCurr ? currColor : isPast ? 'var(--tx-2)' : 'var(--tx-3)', cursor: 'pointer', transition: 'all .15s' }}
              onClick={() => mudarEtapa(e)}>
              <div style={{ font: '400 14px/1 var(--ff-display)', letterSpacing: '.02em', marginBottom: 3, color: 'inherit' }}>{String(i+1).padStart(2,'0')}</div>
              {ETAPAS_LABELS[e]}
            </div>
          );
        })}
      </div>

      {/* Two-column layout */}
      <div style={{ display: 'grid', gridTemplateColumns: '1.1fr 1fr', gap: 16, alignItems: 'start' }}>

        {/* LEFT COL */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>

          {/* Cliente — com enriquecimento CNPJ */}
          <div className="panel">
            <div className="panel__hd" style={{ flexWrap: 'wrap', gap: 8 }}>
              <h3>Cliente</h3>
              <span className="meta"><Badge status={cli?.status} /></span>
              <div style={{ marginLeft: 'auto', display: 'flex', gap: 6, alignItems: 'center' }}>
                {cli?.cnpj && cnpjStatus === 'idle' && (
                  <button className="btn btn-ghost" style={{ fontSize: 11, padding: '4px 10px' }}
                    onClick={buscarDadosPublicos}>
                    <i data-lucide="building-2" style={{ width: 12, height: 12 }}></i> Dados públicos
                  </button>
                )}
                {cnpjStatus === 'loading' && (
                  <span style={{ font: '400 11px/1 var(--ff-body)', color: 'var(--tx-3)' }}>Buscando...</span>
                )}
                {cnpjStatus === 'ok' && (
                  <button className="btn btn-ghost" style={{ fontSize: 11, padding: '4px 10px' }}
                    onClick={buscarDadosPublicos} title="Buscar novamente">
                    <i data-lucide="refresh-cw" style={{ width: 11, height: 11 }}></i>
                  </button>
                )}
                {cnpjStatus === 'unavail' && (
                  <span style={{ font: '400 10px/1 var(--ff-body)', color: 'var(--tx-3)', fontStyle: 'italic' }}>API CNPJ indisponível</span>
                )}
                {cnpjStatus === 'err' && (
                  <span style={{ font: '400 10px/1 var(--ff-body)', color: 'var(--danger)' }} title={cnpjErr}>Erro ao buscar</span>
                )}
              </div>
            </div>
            <div className="panel__body">

              {/* Dados cadastrais CRM */}
              <div style={{ font: '600 15px/1.2 var(--ff-body)', color: 'var(--tx)', marginBottom: 4 }}>{cli?.razao}</div>
              <div style={{ font: '400 11px/1 var(--ff-mono)', color: 'var(--tx-3)', marginBottom: 14 }}>
                CNPJ {cli?.cnpj}
                {op.contato_nome && (
                  <span style={{ marginLeft: 10, font: '500 11px/1 var(--ff-body)', color: 'var(--grn)' }}>
                    A/C {op.contato_nome}
                  </span>
                )}
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 16 }}>
                {[['Segmento',cli?.segmento],['Cidade',cli?.cidade + (cli?.uf ? '/' + cli.uf : '')],['Contato',cli?.contato],['Telefone',cli?.fone],['E-mail',cli?.email],['Vol. anual',fR(cli?.volume_anual||0)]].map(function(_ref) {
                  var lbl = _ref[0], val = _ref[1];
                  return (
                    <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: val ? 'var(--tx)' : 'var(--tx-3)' }}>{val || '—'}</div>
                    </div>
                  );
                })}
              </div>

              {/* Dados públicos — seção informativa */}
              {cnpjStatus === 'ok' && cnpjData && (
                <div style={{ borderTop: '1px solid var(--border)', paddingTop: 14 }}>
                  <div style={{ font: '600 10px/1 var(--ff-body)', textTransform: 'uppercase', letterSpacing: '.06em', color: 'var(--tx-3)', marginBottom: 10 }}>
                    Dados públicos (Receita Federal)
                  </div>

                  {/* Grid informativo */}
                  <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6, marginBottom: 12 }}>
                    {[
                      ['Razão Oficial',    cnpjData.razao_social],
                      ['Fantasia',         cnpjData.nome_fantasia],
                      ['Situação',         cnpjData.situacao_cadastral],
                      ['Porte',            cnpjData.porte],
                      ['Abertura',         cnpjData.data_inicio_atividade],
                      ['Capital Social',   cnpjData.capital_social ? 'R$ ' + Number(cnpjData.capital_social).toLocaleString('pt-BR') : null],
                      ['UF',               cnpjData.uf],
                      ['Município',        cnpjData.municipio_nome],
                    ].filter(function(r) { return r[1]; }).map(function(_r) {
                      var l = _r[0], v = _r[1];
                      return (
                        <div key={l} style={{ padding: '6px 10px', background: 'rgba(0,124,68,.04)', border: '1px solid rgba(0,124,68,.12)', borderRadius: 'var(--r-xs)' }}>
                          <div style={{ font: '600 9px/1 var(--ff-body)', textTransform: 'uppercase', letterSpacing: '.06em', color: 'var(--grn)', opacity: .7, marginBottom: 3 }}>{l}</div>
                          <div style={{ font: '400 11px/1.2 var(--ff-body)', color: 'var(--tx-2)' }}>{v}</div>
                        </div>
                      );
                    })}
                  </div>

                  {/* Endereço completo */}
                  {cnpjData.logradouro && (
                    <div style={{ padding: '6px 10px', background: 'rgba(0,124,68,.04)', border: '1px solid rgba(0,124,68,.12)', borderRadius: 'var(--r-xs)', marginBottom: 12, font: '400 11px/1.4 var(--ff-body)', color: 'var(--tx-2)' }}>
                      <span style={{ font: '600 9px/1 var(--ff-body)', textTransform: 'uppercase', letterSpacing: '.06em', color: 'var(--grn)', opacity: .7, marginRight: 6 }}>Endereço</span>
                      {[cnpjData.logradouro, cnpjData.numero, cnpjData.bairro, cnpjData.municipio_nome + (cnpjData.uf ? '/' + cnpjData.uf : ''), cnpjData.cep].filter(Boolean).join(', ')}
                    </div>
                  )}

                  {/* Sugestões de preenchimento */}
                  {(() => {
                    var visiveis = cnpjSugestoes.filter(function(s){ return !cnpjDismissed.includes(s.campo); });
                    return visiveis.length > 0 ? (
                      <div style={{ padding: '10px 12px', background: 'rgba(230,150,0,.07)', border: '1px solid rgba(230,150,0,.3)', borderRadius: 'var(--r-md)' }}>
                        <div style={{ font: '600 11px/1 var(--ff-body)', color: '#8a5a00', marginBottom: 8 }}>
                          {visiveis.length} campo{visiveis.length > 1 ? 's' : ''} que podem ser preenchido{visiveis.length > 1 ? 's' : ''}:
                        </div>
                        <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                          {visiveis.map(function(s) {
                            return (
                              <div key={s.campo} style={{ display: 'flex', gap: 8, alignItems: 'center', font: '400 11px/1.2 var(--ff-body)' }}>
                                <span style={{ font: '600 9px/1 var(--ff-body)', textTransform: 'uppercase', letterSpacing: '.05em', color: 'var(--tx-3)', minWidth: 60 }}>{s.label}</span>
                                <span style={{ flex: 1, color: 'var(--tx-2)' }}>{s.pub}</span>
                                <button title="Aceitar" disabled={aceitando || !cli?._spId}
                                  onClick={function(){ aceitarSugestaoSingle(s); }}
                                  style={{ padding: '3px 8px', fontSize: 12, fontWeight: 700, color: 'var(--ok)', background: 'rgba(0,124,68,.08)', border: '1px solid rgba(0,124,68,.25)', borderRadius: 'var(--r-sm)', cursor: 'pointer', lineHeight: 1 }}>
                                  ✓
                                </button>
                                <button title="Descartar"
                                  onClick={function(){ setCnpjDismissed(function(prev){ return prev.concat([s.campo]); }); }}
                                  style={{ padding: '3px 8px', fontSize: 12, fontWeight: 700, color: 'var(--tx-3)', background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 'var(--r-sm)', cursor: 'pointer', lineHeight: 1 }}>
                                  ×
                                </button>
                              </div>
                            );
                          })}
                        </div>
                        {!cli?._spId && (
                          <div style={{ font: '400 10px/1.3 var(--ff-body)', color: 'var(--tx-3)', marginTop: 6 }}>
                            (cliente ainda não salvo no SP — recarregue a página)
                          </div>
                        )}
                      </div>
                    ) : (
                      <div style={{ font: '400 11px/1 var(--ff-body)', color: 'var(--grn)', display: 'flex', alignItems: 'center', gap: 6 }}>
                        <i data-lucide="check-circle" style={{ width: 13, height: 13 }}></i>
                        Cadastro completo — nenhum campo faltando
                      </div>
                    );
                  })()}
                </div>
              )}
            </div>
          </div>

          {/* Frota do cliente */}
          {cli && (
            <div className="panel" key={'frota-' + frotaTick}>
              <div className="panel__hd">
                <h3>Frota do cliente</h3>
                <span className="meta">{(CRM_DATA.frotaCliente || []).filter(function(m){ return m.cliente_id === cli.id; }).length} máquina(s)</span>
                <button className="btn btn-primary" style={{ fontSize: 11, padding: '5px 10px', marginLeft: 'auto' }}
                  onClick={function(){ setMaqFrotaEditar(null); setMaqFrotaOpen(true); }}>
                  <i data-lucide="plus" style={{ width: 12, height: 12 }}></i> Adicionar
                </button>
              </div>
              {(function() {
                var maquinas = (CRM_DATA.frotaCliente || []).filter(function(m){ return m.cliente_id === cli.id; });
                if (maquinas.length === 0) {
                  return (
                    <div style={{ padding: '28px 18px', textAlign: 'center', color: 'var(--tx-3)' }}>
                      <i data-lucide="truck" style={{ width: 22, height: 22, display: 'block', margin: '0 auto 8px' }}></i>
                      <div style={{ font: '400 12px/1.4 var(--ff-body)' }}>Nenhuma máquina cadastrada na frota deste cliente.</div>
                    </div>
                  );
                }
                return (
                  <div>
                    {maquinas.slice(0, 8).map(function(m, i) {
                      return (
                        <div key={m.id} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '9px 14px', borderBottom: i < Math.min(maquinas.length, 8) - 1 ? '1px solid var(--border)' : 'none' }}>
                          <i data-lucide="tractor" style={{ width: 15, height: 15, color: 'var(--tx-3)', flexShrink: 0 }}></i>
                          <div style={{ flex: 1, minWidth: 0 }}>
                            <div style={{ font: '500 12px/1 var(--ff-body)', color: 'var(--tx)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                              {[m.fabricante, m.modelo].filter(Boolean).join(' ') || 'Máquina'}
                              {m.ano ? <span style={{ color: 'var(--tx-3)', fontWeight: 400 }}> · {m.ano}</span> : null}
                            </div>
                            <div style={{ font: '400 10px/1 var(--ff-mono)', color: 'var(--tx-3)', marginTop: 3 }}>
                              {[m.serie ? 'Série ' + m.serie : null, m.horimetro ? m.horimetro + 'h' : null].filter(Boolean).join(' · ') || 'Sem série/horímetro'}
                            </div>
                          </div>
                          <button className="btn btn-ghost" style={{ padding: '4px 6px', flexShrink: 0 }}
                            onClick={function(){ setMaqFrotaEditar(m); setMaqFrotaOpen(true); }}>
                            <i data-lucide="pencil" style={{ width: 12, height: 12 }}></i>
                          </button>
                        </div>
                      );
                    })}
                    {maquinas.length > 8 && (
                      <div style={{ padding: '8px 14px', font: '400 11px/1 var(--ff-body)', color: 'var(--tx-3)', background: 'var(--surface-2)', borderTop: '1px solid var(--border)' }}>
                        + {maquinas.length - 8} máquina(s) — veja a ficha completa do cliente
                      </div>
                    )}
                  </div>
                );
              })()}
            </div>
          )}
          {cli && (
            typeof MaquinaFrotaModal !== 'undefined'
              ? React.createElement(MaquinaFrotaModal, {
                  open:      maqFrotaOpen,
                  onClose:   function(){ setMaqFrotaOpen(false); setMaqFrotaEditar(null); },
                  onSave:    function(){ setFrotaTick(function(t){ return t+1; }); setMaqFrotaOpen(false); setMaqFrotaEditar(null); },
                  clienteId: cli.id,
                  inicial:   maqFrotaEditar,
                })
              : null
          )}

          </div>


        {/* RIGHT COL */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>

          {/* Resumo */}
          <div className="panel">
            <div className="panel__hd"><h3>Resumo da oportunidade</h3></div>
            <div className="panel__body" style={{ padding: 0 }}>
              <div style={{ padding: '16px 18px', background: 'var(--grn)' }}>
                <div style={{ font: '600 10px/1 var(--ff-body)', textTransform: 'uppercase', letterSpacing: '.08em', color: 'rgba(255,255,255,.6)', marginBottom: 6 }}>Valor estimado</div>
                <div style={{ font: '400 36px/1 var(--ff-display)', letterSpacing: '.02em', color: '#fff' }}>{fR(valorItens > 0 ? valorItens : op.valor)}</div>
                {valorItens > 0 && (
                  <div style={{ marginTop: 6, font: '400 11px/1 var(--ff-mono)', color: 'rgba(255,255,255,.55)' }}>
                    {itens.length} máquina{itens.length > 1 ? 's' : ''} · soma dos equipamentos
                  </div>
                )}
              </div>
              <div style={{ padding: '14px 18px' }}>
                {[
                  ['Etapa atual',   <Badge key="et" status={op.etapa} />],
                  ['Prioridade',    <Badge key="pr" status={op.prioridade} />],
                  ['Origem',        <span key="ori" style={{ font: '400 12px/1 var(--ff-mono)', color: 'var(--tx)' }}>{op.origem || '—'}</span>],
                  ['Probabilidade', (
                    <div key="prob" style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                      <input
                        type="number" min={0} max={100}
                        value={op.probabilidade || 0}
                        onChange={e => setOp(prev => ({ ...prev, probabilidade: Math.min(100, Math.max(0, Number(e.target.value))) }))}
                        style={{ width: 52, padding: '2px 6px', font: '400 13px/1 var(--ff-mono)', color: op.probabilidade >= 70 ? 'var(--ok)' : op.probabilidade >= 40 ? 'var(--warn)' : 'var(--tx-3)', background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 'var(--r-sm)', textAlign: 'right' }}
                      />
                      <span style={{ font: '400 12px/1 var(--ff-mono)', color: 'var(--tx-3)' }}>%</span>
                    </div>
                  )],
                  ['Abertura',      <span key="ab" style={{ font: '400 12px/1 var(--ff-mono)' }}>{fDate(op.data_abertura)}</span>],
                  ['Previsão',      (
                    <div key="pv" style={{ display: 'flex', align: 'center', gap: 8 }}>
                      <span style={{ font: '400 12px/1 var(--ff-mono)' }}>{fDate(op.previsao_fechamento)}</span>
                      {previsaoInfo && <span style={{ font: '500 10px/1 var(--ff-mono)', color: previsaoInfo.color, background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 'var(--r-pill)', padding: '2px 7px' }}>{previsaoInfo.label}</span>}
                    </div>
                  )],
                  ['Máquinas',      <span key="mq" style={{ font: '400 12px/1 var(--ff-mono)', color: itens.length ? 'var(--ok)' : 'var(--tx-3)' }}>{itens.length > 0 ? itens.length + ' configurada' + (itens.length > 1 ? 's' : '') : '—'}</span>],
                ].map(([lbl, val]) => (
                  <div key={lbl} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px 0', borderBottom: '1px solid var(--border)' }}>
                    <span style={{ font: '400 11px/1 var(--ff-mono)', color: 'var(--tx-3)', textTransform: 'uppercase', letterSpacing: '.05em' }}>{lbl}</span>
                    {val}
                  </div>
                ))}
              </div>
            </div>
          </div>
          {/* Máquinas */}
          <div className="panel">
            <div className="panel__hd">
              <h3>Equipamentos da Oportunidade</h3>
              <button className="btn btn-ghost" style={{ fontSize: 12, padding: '6px 10px' }} onClick={() => abrirSimulador(null)}>
                <i data-lucide="plus"></i> Adicionar
              </button>
            </div>
            <div>
              {itens.length === 0 && (
                <div style={{ padding: '20px 18px', color: 'var(--tx-3)', font: '400 13px/1.4 var(--ff-body)' }}>
                  Nenhum equipamento adicionado.<br/>
                  <span style={{ font: '400 12px/1 var(--ff-mono)' }}>Clique em "Adicionar" para abrir o simulador.</span>
                </div>
              )}
              {itens.map((item, i) => (
                <div key={item._localId || item._spId || i} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '12px 18px', borderBottom: i < itens.length-1 ? '1px solid var(--border)' : 'none' }}>
                  <div style={{ width: 36, height: 36, borderRadius: 'var(--r-md)', background: 'var(--surface-2)', border: '1px solid var(--border)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                    <i data-lucide="tractor" style={{ width: 18, height: 18, color: 'var(--tx-3)' }}></i>
                  </div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ font: '500 13px/1 var(--ff-body)', color: 'var(--tx)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{item.modelo || item.fab || 'Máquina'}</div>
                    <div style={{ font: '400 11px/1.3 var(--ff-mono)', color: 'var(--tx-3)', marginTop: 3 }}>Tab {item.tabela} · Qtd {item.quantidade} · UF {item.uf_cliente}</div>
                  </div>
                  <div style={{ textAlign: 'right', flexShrink: 0 }}>
                    <div style={{ font: '600 13px/1 var(--ff-mono)', color: 'var(--tx)' }}>{fR(item.valor_total||0)}</div>
                    <div style={{ font: '400 11px/1 var(--ff-mono)', color: 'var(--tx-3)', marginTop: 3 }}>{fR2(item.valor_unitario||0)} × {item.quantidade}</div>
                  </div>
                  <button className="btn btn-ghost" style={{ padding: '6px 8px', flexShrink: 0, color: 'var(--grn)' }} title="Editar no simulador" onClick={() => abrirSimulador(item)}>
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                      <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
                      <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
                    </svg>
                  </button>
                  <button className="btn btn-ghost" style={{ padding: '6px 8px', flexShrink: 0, color: 'var(--danger)' }} title="Remover máquina"
                    onClick={() => removerItem(item._localId)}>
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                      <polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14H6L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4h6v2"/>
                    </svg>
                  </button>
                </div>
              ))}
              {itens.length > 0 && (
                <div style={{ padding: '10px 18px', borderTop: '1px solid var(--border)', display: 'flex', justifyContent: 'space-between', alignItems: 'center', background: 'var(--surface-2)' }}>
                  <span style={{ font: '500 11px/1 var(--ff-mono)', color: 'var(--tx-3)', textTransform: 'uppercase', letterSpacing: '.05em' }}>{itens.length} máquina{itens.length > 1 ? 's' : ''}</span>
                  <span style={{ font: '600 14px/1 var(--ff-mono)', color: 'var(--grn)' }}>{fR(valorItens)}</span>
                </div>
              )}
            </div>
          </div>

          {/* Propostas / Pedidos */}
          <div className="panel">
            <div className="panel__hd">
              <h3>Propostas / Pedidos</h3>
              <button className="btn btn-ghost" style={{ fontSize: 12, padding: '6px 10px' }} onClick={() => setModalSelecionarItens(true)}>
                <i data-lucide="plus"></i> Nova
              </button>
            </div>
            <div style={{ padding: '0 0' }}>
              {propostas.length === 0 && (
                <div style={{ padding: '20px 18px', color: 'var(--tx-3)', font: '400 13px/1 var(--ff-body)' }}>Nenhuma proposta registrada.</div>
              )}
              {propostas.map((p, i) => {
                // Lê status live do CRM_DATA — atualizado em tempo real pelo persistStatus
                var liveStatus = (CRM_DATA.propostas && CRM_DATA.propostas.find(function(cp){ return cp.id === p.id; }))?.status || p.status;
                const s = statusPropMap[liveStatus] || { cls: 'bx', label: liveStatus };
                // Pedido vinculado (quando proposta virou pedido)
                var pedidoVinc = liveStatus === 'pedido'
                  ? (CRM_DATA.pedidos || []).find(function(ped){ return ped.proposta_id === p.id; })
                  : null;
                return (
                  <div key={p.id} onClick={() => onOpenProp && onOpenProp(p.id)}
                    style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 18px', borderBottom: i < propostas.length-1 ? '1px solid var(--border)' : 'none', cursor: onOpenProp ? 'pointer' : 'default', transition: 'background 120ms' }}
                    onMouseEnter={e => { if (onOpenProp) e.currentTarget.style.background = 'var(--surface-2)'; }}
                    onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; }}>
                    <div style={{ flex: 1 }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                        <span style={{ font: '500 13px/1 var(--ff-mono)', color: 'var(--tx)' }}>{p.id}</span>
                        <span style={{ font: '400 11px/1 var(--ff-mono)', color: 'var(--tx-3)' }}>{p.versao}</span>
                      </div>
                      <div style={{ font: '400 11px/1 var(--ff-mono)', color: 'var(--tx-3)', marginTop: 3 }}>Válida até {fDate(p.validade)}</div>
                    </div>
                    <div style={{ textAlign: 'right' }}>
                      <div style={{ font: '400 14px/1 var(--ff-mono)', color: 'var(--tx)' }}>{fR(p.valor)}</div>
                      <div style={{ marginTop: 4 }}><span className={'b ' + s.cls}><span className="b-dot"></span>{s.label}</span></div>
                      {pedidoVinc && (
                        <div style={{ font: '500 11px/1 var(--ff-mono)', color: 'var(--ok)', marginTop: 3 }}>{pedidoVinc.id}</div>
                      )}
                    </div>
                  </div>
                );
              })}
            </div>
          </div>

          {/* Histórico de atividades — todas as atividades do cliente (M) */}
          <div className="panel">
            <div className="panel__hd">
              <h3>Histórico do cliente</h3>
              <button className="btn btn-ghost" style={{ fontSize: 12, padding: '6px 10px' }} onClick={() => _abrirAtividade()}>
                <i data-lucide="plus"></i> Registrar
              </button>
            </div>
            <div className="panel__body" style={{ paddingTop: 0 }}>
              {atividades.length === 0 && (
                <div style={{ paddingTop: 12, color: 'var(--tx-3)', font: '400 13px/1 var(--ff-body)' }}>Nenhuma atividade registrada para este cliente.</div>
              )}
              {atividades.map((at, i) => {
                // M: atividade pertence a outra op (ou sem op) — indica contexto
                var outraOp = at.op_id && at.op_id !== op.id;
                var semOp   = !at.op_id;
                return (
                  <div key={at.id} style={{ display: 'flex', gap: 12, alignItems: 'flex-start', paddingTop: i === 0 ? 12 : 14, paddingBottom: 14, borderBottom: i < atividades.length-1 ? '1px solid var(--border)' : 'none', opacity: outraOp ? .85 : 1 }}>
                    <AtivIcon tipo={at.tipo} />
                    <div style={{ flex: 1 }}>
                      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 8 }}>
                        <div style={{ font: '500 13px/1.2 var(--ff-body)', color: 'var(--tx)' }}>{at.titulo}</div>
                        <div style={{ display: 'flex', gap: 4, alignItems: 'center', flexShrink: 0 }}>
                          {(outraOp || semOp) && (
                            <span
                              title={outraOp ? 'Registrada em outra oportunidade deste cliente' : 'Atividade sem oportunidade vinculada'}
                              style={{ fontSize: 9, fontFamily: 'var(--ff-body)', fontWeight: 600,
                                textTransform: 'uppercase', letterSpacing: '.05em',
                                padding: '2px 5px', borderRadius: 'var(--r-pill)', whiteSpace: 'nowrap',
                                background: 'var(--surface-3)', color: 'var(--tx-3)',
                                border: '1px solid var(--border)' }}>
                              {outraOp ? 'outra op' : 'sem op'}
                            </span>
                          )}
                          <Badge status={at.resultado} dot={false} />
                        </div>
                      </div>
                      <div style={{ font: '400 11px/1 var(--ff-mono)', color: 'var(--tx-3)', marginTop: 4 }}>
                        {fDateTime(at.data)}{at.duracao !== '—' ? ' · ' + at.duracao : ''}
                      </div>
                      {at.notas && (
                        <div style={{ marginTop: 6, font: '400 12px/1.5 var(--ff-body)', color: 'var(--tx-2)', background: 'var(--surface-2)', borderRadius: 'var(--r-sm)', padding: '8px 10px', borderLeft: '3px solid var(--border-2)' }}>
                          {at.notas}
                        </div>
                      )}
                    </div>
                  </div>
                );
              })}
            </div>
          </div>

        </div>
      </div>

      {/* Modals */}
      <SelecionarItensModal open={modalSelecionarItens} onClose={() => setModalSelecionarItens(false)} itens={itens} onConfirm={criarEAbrirProposta} onAddMaquina={() => { setModalSelecionarItens(false); abrirSimulador(null); }} />
      <NovaPropostaModal open={modalProp} onClose={() => setModalProp(false)} op={op} itens={itens} onSave={salvarProposta} />
      <EditarOpModal open={modalEditar} onClose={() => setModalEditar(false)} op={op} onSave={salvarEdicaoOp} />
      <QuantidadeModal open={showQtd} item={pendingItem} onConfirm={confirmarItem} onClose={() => { setShowQtd(false); setPendingItem(null); setEditingLocalId(null); }} />
      <CancelarOpModal
        open={modalExcluirOp}
        onClose={() => setModalExcluirOp(false)}
        op={op}
        propostas={propostas}
        pedidos={(CRM_DATA.pedidos || []).filter(ped => propostas.some(p => p.id === ped.proposta_id))}
        onCancelConfirm={handleCancelarOportunidade}
        onDeleteConfirm={handleExcluirOportunidade}
      />
      {/* ── Modal: Registrar aprovação de crédito ────── */}
      <AprovCreditoModal
        open={modalAprovCredito}
        onClose={() => setModalAprovCredito(false)}
        op={op}
        propostas={propostas}
        onConfirm={confirmarAprovCredito}
      />

      {/* ── Mini-modal: oferta fechar após crédito ──── */}
      {showFecharAposCredito && (
        <Modal open={true} onClose={() => setShowFecharAposCredito(false)} title="Crédito registrado com sucesso!">
          <div style={{ padding: '4px 0 16px' }}>
            <div style={{ font: '400 13px/1.5 var(--ff-body)', color: 'var(--tx-2)', marginBottom: 16 }}>
              Crédito registrado. Deseja fechar a oportunidade agora?
            </div>
            <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
              <button className="btn btn-secondary" onClick={() => setShowFecharAposCredito(false)}>Agora não</button>
              <button className="btn btn-primary" onClick={() => { setShowFecharAposCredito(false); setModalFechamento(true); }}>
                <i data-lucide="flag"></i> Fechar Oportunidade
              </button>
            </div>
          </div>
        </Modal>
      )}

{/* ── Mini-modal: notificação de auto-avanço ──── */}
      {autoAvancNotif && (
        <Modal open={true} onClose={() => setAutoAvancNotif(null)} title="Oportunidade avançada automaticamente">
          <div style={{ padding: '4px 0 16px' }}>
            <div style={{ display:'flex', alignItems:'center', gap:10, padding:'12px 14px', background:'var(--grn-050)', border:'1px solid var(--grn)', borderRadius:'var(--r-md)', marginBottom:14 }}>
              <i data-lucide="zap" style={{ width:18, height:18, color:'var(--grn)', flexShrink:0 }}></i>
              <div style={{ font:'500 13px/1.4 var(--ff-body)', color:'var(--grn)' }}>
                A oportunidade avançou de <strong>{ETAPAS_LABELS[autoAvancNotif.de] || autoAvancNotif.de}</strong> para <strong>{ETAPAS_LABELS[autoAvancNotif.para] || autoAvancNotif.para}</strong> automaticamente com base na atualização da proposta.
              </div>
            </div>
            <p style={{ font:'400 12px/1.5 var(--ff-body)', color:'var(--tx-3)', margin:0 }}>
              Você pode ajustar a etapa manualmente clicando no passo desejado na barra de progresso acima.
            </p>
          </div>
          <div style={{ display:'flex', justifyContent:'flex-end' }}>
            <button className="btn btn-primary" onClick={() => setAutoAvancNotif(null)}>Entendido</button>
          </div>
        </Modal>
      )}

      {/* ── Overlay de Parabéns — dispara após FechamentoModal confirmar ─── */}
      {showCongrats && (function() {
        var isPartial = showCongrats === 'fechamento_parcial';
        var vendNome = vend && (vend.nome || vend.name) ? ', ' + (vend.nome || vend.name).split(' ')[0] : '';
        var bg = isPartial
          ? 'linear-gradient(135deg,#7a3e00 0%,#b05000 55%,#e07020 100%)'
          : 'linear-gradient(135deg,#004d2e 0%,#007c44 55%,#00a060 100%)';
        var emoji  = isPartial ? '🎊' : '🏆';
        var titulo = isPartial ? 'Ganho Parcial!' : 'Negócio fechado!';
        var subtit = isPartial
          ? 'Parte do negócio foi concluída com sucesso.'
          : 'Parabéns' + vendNome + '! O negócio foi fechado com sucesso.';
        var ConfetiComp = window.ConfetiAnimado || null;
        return (
          <div style={{ position:'fixed', inset:0, zIndex:10050, background: bg, display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', overflow:'hidden' }}>
            {ConfetiComp && React.createElement(ConfetiComp)}
            <div style={{ position:'relative', zIndex:2, textAlign:'center', color:'#fff', padding:'40px 32px', maxWidth:500 }}>
              <div style={{ fontSize:80, marginBottom:20, lineHeight:1, filter:'drop-shadow(0 6px 20px rgba(0,0,0,.35))' }}>{emoji}</div>
              <div style={{ font:'800 34px/1.1 var(--ff-display,sans-serif)', marginBottom:12, letterSpacing:'-0.5px', textShadow:'0 2px 12px rgba(0,0,0,.25)' }}>
                {titulo}
              </div>
              <div style={{ font:'600 18px/1.5 var(--ff-body,sans-serif)', opacity:0.92, marginBottom:10 }}>
                {subtit} {isPartial ? '' : '🎉'}
              </div>
              {op.valor > 0 && (
                <div style={{ font:'700 28px/1.2 var(--ff-display,sans-serif)', marginBottom:32, opacity:0.95, letterSpacing:'-0.5px' }}>
                  {fR(op.valor)}
                </div>
              )}
              <button
                onClick={function(){ setShowCongrats(null); }}
                style={{ background:'#fff', color: isPartial ? '#8a3e00' : '#006b3f', border:'none', borderRadius:14, padding:'15px 40px', font:'700 16px/1 var(--ff-body,sans-serif)', cursor:'pointer', boxShadow:'0 6px 28px rgba(0,0,0,.22)', transition:'transform .15s, box-shadow .15s' }}
                onMouseEnter={function(e){ e.currentTarget.style.transform='scale(1.06)'; }}
                onMouseLeave={function(e){ e.currentTarget.style.transform='scale(1)'; }}>
                Ver oportunidade →
              </button>
            </div>
          </div>
        );
      })()}

      {showPedAssinado && (function() {
        var ConfetiComp = window.ConfetiAnimado || null;
        var nomeItens = itens.length > 0
          ? itens.map(function(it) {
              return (it.quantidade > 1 ? it.quantidade + '× ' : '') + (it.modelo || it.fab || 'Máquina');
            }).join(' · ')
          : null;
        return (
          <div
            style={{ position:'fixed', inset:0, zIndex:10050, background:'linear-gradient(135deg,#002a5e 0%,#0052a3 55%,#006fd6 100%)', display:'flex', alignItems:'center', justifyContent:'center' }}
            onClick={function(e){ if (e.target === e.currentTarget) setShowPedAssinado(false); }}>
            {ConfetiComp && React.createElement(ConfetiComp)}
            <div style={{ position:'relative', zIndex:2, textAlign:'center', color:'#fff', padding:'40px 32px', maxWidth:520 }}>
              <div style={{ fontSize:76, marginBottom:18, lineHeight:1, filter:'drop-shadow(0 6px 20px rgba(0,0,0,.35))' }}>✍️</div>
              <div style={{ font:'800 32px/1.1 var(--ff-display,sans-serif)', marginBottom:10, letterSpacing:'-0.5px', textShadow:'0 2px 12px rgba(0,0,0,.25)' }}>
                Pedido assinado pelo cliente! 🎉
              </div>
              {nomeItens && (
                <div style={{ font:'600 16px/1.5 var(--ff-body,sans-serif)', opacity:0.88, marginBottom:8 }}>
                  {nomeItens}
                </div>
              )}
              {op.valor > 0 && (
                <div style={{ font:'700 26px/1.2 var(--ff-display,sans-serif)', marginBottom:22, opacity:0.95, letterSpacing:'-0.5px' }}>
                  {fR(op.valor)}
                </div>
              )}
              <div style={{ background:'rgba(255,255,255,0.14)', borderRadius:14, padding:'16px 20px', marginBottom:28, font:'500 14px/1.65 var(--ff-body,sans-serif)', textAlign:'left', backdropFilter:'blur(6px)' }}>
                <strong>Próximo passo:</strong> inicie o fluxo de <strong>crédito / pagamento</strong>. Colete a documentação do cliente, acione o financiamento e avance a oportunidade para fechar o negócio. 💪
              </div>
              <div style={{ display:'flex', gap:12, justifyContent:'center', flexWrap:'wrap' }}>
                <button
                  onClick={function(){ setShowPedAssinado(false); }}
                  style={{ background:'#fff', color:'#003d8c', border:'none', borderRadius:14, padding:'15px 34px', font:'700 15px/1 var(--ff-body,sans-serif)', cursor:'pointer', boxShadow:'0 6px 28px rgba(0,0,0,.22)', transition:'transform .15s, box-shadow .15s' }}
                  onMouseEnter={function(e){ e.currentTarget.style.transform='scale(1.06)'; }}
                  onMouseLeave={function(e){ e.currentTarget.style.transform='scale(1)'; }}>
                  Ver oportunidade →
                </button>
              </div>
            </div>
          </div>
        );
      })()}

      <FechamentoModal open={modalFechamento} onClose={() => setModalFechamento(false)} op={op} propostas={propostas} itens={itens} onConfirm={function(etapa, dados) {
        setModalFechamento(false);
        if (etapa === 'fechado' || etapa === 'fechamento_parcial') {
          setShowCongrats(etapa);
        }

        var autor = (window.CRM_USER && window.CRM_USER.realEmail) || '';
        var fechadoEm = new Date().toISOString();

        // 1. Atualiza status de cada proposta com base no resultado do fechamento
        propostas.forEach(function(prop) {
          if (prop.status === 'cancelada') return;
          var outcome = dados.statusPropostas && dados.statusPropostas[prop.id];
          // "ganho" ou "negociacao" → não altera status da proposta
          if (!outcome || outcome !== 'perdido') {
            // Atualiza status_fechamento dos itens desta proposta para 'ganho' (ou neutro)
            _atualizarItensDaProposta(prop, outcome === 'negociacao' ? null : 'ganho');
            return;
          }

          // outcome === 'perdido' → proposta vira 'perdida'
          var motivoProp = (dados.motivoPerdaProp && dados.motivoPerdaProp[prop.id]) || '';
          var obsProp    = (dados.obsPerdaProp    && dados.obsPerdaProp[prop.id])    || '';
          var novoEvento = {
            id: Date.now().toString(36) + Math.random().toString(36).slice(2, 5),
            tipo: 'perdida',
            data: fechadoEm,
            detalhe: 'Proposta marcada como perdida no fechamento da oportunidade' + (motivoProp ? '. Motivo: ' + motivoProp : ''),
            autor: autor,
          };
          var novaTimeline = (prop.timeline || []).concat([novoEvento]);

          // Atualiza state local
          setPropostas(function(prev) {
            return prev.map(function(p) {
              return p.id === prop.id
                ? Object.assign({}, p, { status: 'perdida', motivo_perda: motivoProp, obs_perda: obsProp, perdida_em: fechadoEm, timeline: novaTimeline })
                : p;
            });
          });

          // Atualiza CRM_DATA global
          if (CRM_DATA.propostas) {
            var propIdx = CRM_DATA.propostas.findIndex(function(p){ return p.id === prop.id; });
            if (propIdx >= 0) {
              CRM_DATA.propostas[propIdx] = Object.assign({}, CRM_DATA.propostas[propIdx], {
                status: 'perdida', motivo_perda: motivoProp, obs_perda: obsProp, perdida_em: fechadoEm, timeline: novaTimeline,
              });
            }
          }

          // Persiste no SP
          if (prop._spId && window.CRM_API) {
            CRM_API.updateItem('Propostas', prop._spId, {
              status: 'perdida', motivo_perda: motivoProp, obs_perda: obsProp, perdida_em: fechadoEm, timeline: novaTimeline,
            }).catch(function(e){ console.warn('[CRM] fechamento proposta perdida:', e.message); });
          }

          // Notifica outras telas (PropostasScreen, PedidosScreen, etc.)
          window.dispatchEvent(new CustomEvent('crm_prop_status_changed', {
            detail: { propId: prop.id, newStatus: 'perdida', opId: op.id }
          }));

          // Itens desta proposta → perdido em cascata
          _atualizarItensDaProposta(prop, 'perdido', motivoProp, obsProp);
        });

        // 2. Rascunhos → cancelada automático (nunca foram enviados; não contam como venda perdida)
        var rascunhosAbertos = propostas.filter(function(p) { return p.status === 'rascunho'; });
        rascunhosAbertos.forEach(function(prop) {
          var evRasc = {
            id: Date.now().toString(36) + Math.random().toString(36).slice(2, 5),
            tipo: 'cancelada_cascade',
            data: fechadoEm,
            detalhe: 'Rascunho cancelado automaticamente por fechamento da oportunidade (nunca submetido ao cliente).',
            autor: autor,
          };
          var tlRasc = (prop.timeline || []).concat([evRasc]);
          setPropostas(function(prev) {
            return prev.map(function(p) {
              return p.id === prop.id
                ? Object.assign({}, p, { status: 'cancelada', cancelada_em: fechadoEm, timeline: tlRasc })
                : p;
            });
          });
          if (CRM_DATA.propostas) {
            var rIdx = CRM_DATA.propostas.findIndex(function(p) { return p.id === prop.id; });
            if (rIdx >= 0) CRM_DATA.propostas[rIdx] = Object.assign({}, CRM_DATA.propostas[rIdx], { status: 'cancelada', cancelada_em: fechadoEm, timeline: tlRasc });
          }
          if (prop._spId && window.CRM_API) {
            CRM_API.updateItem('Propostas', prop._spId, { status: 'cancelada', timeline: tlRasc })
              .catch(function(e) { console.warn('[CRM] fechamento rascunho cascade:', e.message); });
          }
          window.dispatchEvent(new CustomEvent('crm_prop_status_changed', {
            detail: { propId: prop.id, newStatus: 'cancelada', opId: op.id }
          }));
        });

        mudarEtapa(etapa);

        // Helper local: atualiza status_fechamento dos itens vinculados a uma proposta
        function _atualizarItensDaProposta(prop, sfNew, motivo, obs) {
          if (!sfNew) return; // negociacao: não altera itens
          // Busca os _localIds dos itens desta proposta
          var propItens = (CRM_DATA.itens_proposta && CRM_DATA.itens_proposta[prop.id])
                          || (prop.itens || []);
          var propLocalIds = new Set(propItens.map(function(it){ return it._localId; }).filter(Boolean));
          if (propLocalIds.size === 0) return;

          itens.forEach(function(it) {
            if (!it._localId || !propLocalIds.has(it._localId)) return;
            var updates = { status_fechamento: sfNew };
            if (sfNew === 'perdido') {
              updates.motivo_perda = motivo || '';
              updates.obs_perda    = obs    || '';
            }
            setItens(function(prev) {
              return prev.map(function(i) {
                return i._localId === it._localId ? Object.assign({}, i, updates) : i;
              });
            });
            if (it._spId && window.CRM_API) {
              CRM_API.updateItem('Itens', it._spId, Object.assign({}, it, updates))
                .catch(function(e){ console.warn('[CRM] updateItem Itens status_fechamento:', e.message); });
            }
          });
        }
      }} />

      {/* Simulador iframe popup */}
      {showSimIframe && (
        <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.65)', zIndex: 9999, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}
          onClick={function(e){ if (e.target === e.currentTarget) setShowSimIframe(false); }}>
          <div style={{ width: '96vw', maxWidth: 1340, height: '94vh', display: 'flex', flexDirection: 'column', background: 'var(--surface)', borderRadius: 'var(--r-lg)', overflow: 'hidden', boxShadow: '0 24px 80px rgba(0,0,0,.45)' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '10px 16px', borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
              <span style={{ font: '600 13px/1 var(--ff-body)', color: 'var(--tx)' }}>Simulador de Equipamentos</span>
              <button className="btn btn-ghost" style={{ padding: '6px 10px' }} onClick={function(){ setShowSimIframe(false); }}>
                <i data-lucide="x"></i>
              </button>
            </div>
            <iframe src={simIframeUrl} style={{ flex: 1, border: 'none', width: '100%' }} title="Simulador" allow="clipboard-write" />
          </div>
        </div>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// CancelarOpModal — modal unificado: Cancelar (soft delete) + Excluir definitivamente (só gerente)
// ─────────────────────────────────────────────────────────────────────────────
function CancelarOpModal({ open, onClose, op, propostas, pedidos, onCancelConfirm, onDeleteConfirm }) {
  var _us = React.useState;
  var _motivo = _us(''),     motivo = _motivo[0],     setMotivo = _motivo[1];
  var _check  = _us(false),  check  = _check[0],      setCheck  = _check[1];
  var _modo   = _us('cancelar'), modo = _modo[0],     setModo   = _modo[1]; // 'cancelar' | 'excluir'
  var _saving = _us(false),  saving = _saving[0],     setSaving = _saving[1];

  var isGerente = !!(window.CRM_USER && window.CRM_USER.isGerente);

  React.useEffect(function() {
    if (!open) { setMotivo(''); setCheck(false); setModo('cancelar'); setSaving(false); return; }
    setTimeout(function() {
      try {
        if (window.lucide && typeof window.lucide.createIcons === 'function') {
          window.lucide.createIcons({ attrs: { 'stroke-width': 1.75 } });
        }
      } catch(e) { /* DOM já alterado por outra reconciliação; ignora pra não travar */ }
    }, 0);
  }, [open]);

  if (!open) return null;

  // Conta cascade — pra mostrar no aviso
  var propsAtivas = (propostas || []).filter(function(p){ return p.status !== 'cancelada'; });
  var nProps = propsAtivas.length;
  var pedidosVinculados = (pedidos || []).filter(function(ped) {
    return propsAtivas.some(function(prop){ return prop.id === ped.proposta_id; }) &&
           ped.status !== 'cancelado' && ped.status !== 'cancelado_correcao' && ped.status !== 'cancelado_apos_assinatura';
  });
  var nPedidos = pedidosVinculados.length;

  var motivoOk = motivo.trim().length >= 10;
  var podeConfirmarCancelar = motivoOk && check && !saving;
  var podeConfirmarExcluir  = check && !saving;
  var isExcluir = modo === 'excluir';

  function handleConfirmar() {
    if (saving) return;
    setSaving(true);
    var handler = isExcluir ? onDeleteConfirm : function() { return onCancelConfirm(motivo.trim()); };
    Promise.resolve(handler()).catch(function(e){
      setSaving(false);
      window.CRM_TOAST('Erro: ' + (e && e.message ? e.message : 'desconhecido'), 'error');
    });
  }

  var overlay = { position: 'fixed', inset: 0, background: 'rgba(0,0,0,.55)', zIndex: 9999, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 16 };
  var box = { background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-lg)', width: '100%', maxWidth: 500, boxShadow: 'var(--shadow-lg)' };

  return (
    <div style={overlay} onClick={function(e){ if (e.target === e.currentTarget && !saving) onClose(); }}>
      <div style={box}>
        {/* Header */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '18px 20px 14px', borderBottom: '1px solid var(--border)' }}>
          <div style={{ width: 32, height: 32, borderRadius: '50%', background: isExcluir ? '#fef2f2' : '#fff7ed', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
            <i data-lucide={isExcluir ? 'trash-2' : 'x-circle'} style={{ width: 16, height: 16, color: isExcluir ? 'var(--danger)' : '#c2410c' }}></i>
          </div>
          <div>
            <div style={{ font: '600 14px/1.2 var(--ff-body)', color: 'var(--tx)' }}>
              {isExcluir ? 'Excluir oportunidade definitivamente' : 'Cancelar oportunidade'}
            </div>
            <div style={{ font: '400 11px/1 var(--ff-mono)', color: 'var(--tx-3)', marginTop: 2 }}>{op && op.id} · {op && op.titulo}</div>
          </div>
          <button onClick={onClose} style={{ marginLeft: 'auto', background: 'none', border: 'none', cursor: 'pointer', color: 'var(--tx-3)', padding: 4 }}>
            <i data-lucide="x" style={{ width: 16, height: 16 }}></i>
          </button>
        </div>

        {/* Body */}
        <div style={{ padding: '18px 20px' }}>
          {/* Cascade impacts */}
          {(nProps > 0 || nPedidos > 0) && (
            <div style={{ background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', padding: '12px 14px', marginBottom: 14 }}>
              <div style={{ font: '600 10px/1 var(--ff-body)', textTransform: 'uppercase', letterSpacing: '.07em', color: 'var(--tx-3)', marginBottom: 8 }}>
                Itens vinculados que serão {isExcluir ? 'excluídos' : 'cancelados em cascata'}
              </div>
              <ul style={{ margin: 0, paddingLeft: 16, font: '400 12px/1.7 var(--ff-body)', color: 'var(--tx-2)' }}>
                {nProps > 0 && <li><strong>{nProps}</strong> proposta{nProps > 1 ? 's' : ''} ativa{nProps > 1 ? 's' : ''}</li>}
                {nPedidos > 0 && <li><strong>{nPedidos}</strong> pedido{nPedidos > 1 ? 's' : ''} ativo{nPedidos > 1 ? 's' : ''}</li>}
                {isExcluir && <li>Itens (máquinas), atividades e timeline também serão removidos do SharePoint</li>}
              </ul>
            </div>
          )}

          {/* Motivo — só pra cancelar */}
          {!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 do cancelamento <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={motivo} onChange={function(e){ setMotivo(e.target.value); }}
                placeholder="Ex: cliente sumiu, oportunidade criada por engano, ficou inviável…"
                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 }}>
                {motivo.trim().length}/10 caracteres
              </div>
            </div>
          )}

          {/* Aviso especial pra excluir */}
          {isExcluir && (
            <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="alert-triangle" 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>Atenção:</strong> esta ação é irreversível. Os registros serão removidos do SharePoint e não poderão ser recuperados. Use somente para limpeza de cadastros errados ou correções estruturais.
              </div>
            </div>
          )}

          {/* Checkbox confirmação */}
          <label style={{ display: 'flex', alignItems: 'flex-start', gap: 10, cursor: 'pointer', userSelect: 'none', background: isExcluir ? 'var(--danger-050)' : 'var(--surface-2)', border: '1px solid ' + (isExcluir ? 'var(--danger-100)' : 'var(--border)'), borderRadius: 'var(--r-md)', padding: '12px 14px' }}>
            <input type="checkbox" checked={check} onChange={function(e){ setCheck(e.target.checked); }}
              style={{ marginTop: 1, accentColor: isExcluir ? 'var(--danger)' : 'var(--warn)', flexShrink: 0, width: 15, height: 15 }} />
            <span style={{ font: '500 12px/1.5 var(--ff-body)', color: isExcluir ? 'var(--danger)' : 'var(--tx-2)' }}>
              {isExcluir
                ? <>Confirmo que desejo excluir definitivamente esta oportunidade e todos os registros vinculados. <strong>Esta ação não pode ser desfeita.</strong></>
                : <>Confirmo que a oportunidade <strong>{op && op.id}</strong>{nProps > 0 || nPedidos > 0 ? ' e os itens vinculados acima' : ''} serão cancelados.</>}
            </span>
          </label>

          {/* Link discreto pra gerente trocar de modo */}
          {isGerente && !isExcluir && (
            <div style={{ marginTop: 14, paddingTop: 12, borderTop: '1px dashed var(--border)', textAlign: 'right' }}>
              <button onClick={function(){ setModo('excluir'); setCheck(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={{ marginTop: 14, paddingTop: 12, borderTop: '1px dashed var(--border)', textAlign: 'right' }}>
              <button onClick={function(){ setModo('cancelar'); setCheck(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 cancelar (sem excluir)
              </button>
            </div>
          )}
        </div>

        {/* Footer */}
        <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', padding: '12px 20px 18px' }}>
          <button className="btn btn-secondary" onClick={onClose} disabled={saving}>Voltar</button>
          <button className="btn"
            disabled={isExcluir ? !podeConfirmarExcluir : !podeConfirmarCancelar}
            onClick={handleConfirmar}
            style={{
              background: isExcluir
                ? (podeConfirmarExcluir ? 'var(--danger)' : 'var(--danger-100)')
                : (podeConfirmarCancelar ? 'var(--warn)' : 'var(--border)'),
              color: '#fff', border: 'none',
              cursor: ((isExcluir && podeConfirmarExcluir) || (!isExcluir && podeConfirmarCancelar)) ? 'pointer' : 'not-allowed',
              gap: 6,
            }}>
            {saving
              ? <><i data-lucide="loader" style={{ width: 13, height: 13 }}></i> Processando…</>
              : isExcluir
                ? <><i data-lucide="trash-2" style={{ width: 13, height: 13 }}></i> Excluir definitivamente</>
                : <><i data-lucide="x-circle" style={{ width: 13, height: 13 }}></i> Cancelar oportunidade</>}
          </button>
        </div>
      </div>
    </div>
  );
}

window.Oportunidade = Oportunidade;
