/* ============================================================
   contrato-instalador.jsx
   ContratoInstaladorPage — wizard + dashboard + send modal,
   tudo integrado e persistindo no Supabase vpprd.
   ============================================================ */
const { useState: _ciUS, useEffect: _ciUE, useMemo: _ciUM, useRef: _ciUR } = React;

/* ============================================================
   FORM FIELDS (compartilhados pelo wizard)
   ============================================================ */
function CIField({ label, value, onChange, placeholder, mask, error, hint, required, mono, width }) {
  const handle = (e) => {
    let v = e.target.value;
    if (mask && window.CI[mask]) v = window.CI[mask](v);
    onChange(v);
  };
  return (
    <div className={'ci-field' + (width ? ' ci-field--' + width : '')}>
      <label>{label}{required && <span className="ci-req">*</span>}</label>
      <input
        className={'ci-input' + (mono ? ' ci-input--mono' : '') + (error ? ' ci-input--error' : '')}
        value={value || ''}
        onChange={handle}
        placeholder={placeholder}
        inputMode={mask === 'maskMoeda' || mask === 'maskCNPJ' || mask === 'maskCPF' || mask === 'maskCEP' ? 'numeric' : undefined}
      />
      {error ? <span className="ci-field-err">{error}</span> : (hint ? <span className="ci-field-hint">{hint}</span> : null)}
    </div>
  );
}

function CIMoneyField({ label, value, onChange, error, hint, required, width }) {
  const handle = (e) => onChange(window.CI.maskMoeda(e.target.value));
  const num = window.CI.moedaParaNumero(value);
  return (
    <div className={'ci-field' + (width ? ' ci-field--' + width : '')}>
      <label>{label}{required && <span className="ci-req">*</span>}</label>
      <div className={'ci-input ci-input--prefix' + (error ? ' ci-input--error' : '')}>
        <span className="ci-input-prefix">R$</span>
        <input className="ci-input-bare ci-input--mono" value={value || ''} onChange={handle} placeholder="0,00" inputMode="numeric" />
      </div>
      {num > 0 && <span className="ci-field-extenso">{window.CI.valorExtenso(num)}</span>}
      {error ? <span className="ci-field-err">{error}</span> : (hint ? <span className="ci-field-hint">{hint}</span> : null)}
    </div>
  );
}

function CITextArea({ label, value, onChange, placeholder, rows, hint, error }) {
  return (
    <div className="ci-field ci-field--full">
      <label>{label}</label>
      <textarea className={'ci-input ci-textarea' + (error ? ' ci-input--error' : '')} rows={rows || 3} value={value || ''} onChange={(e) => onChange(e.target.value)} placeholder={placeholder}></textarea>
      {error ? <span className="ci-field-err">{error}</span> : (hint ? <span className="ci-field-hint">{hint}</span> : null)}
    </div>
  );
}

function CISelect({ label, value, onChange, options, width }) {
  return (
    <div className={'ci-field' + (width ? ' ci-field--' + width : '')}>
      <label>{label}</label>
      <div className="ci-select-wrap">
        <select className="ci-input ci-select" value={value} onChange={(e) => onChange(e.target.value)}>
          {options.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
        </select>
        <span className="ci-select-caret">▾</span>
      </div>
    </div>
  );
}

function CIRadioCards({ value, onChange, options, columns }) {
  return (
    <div className="ci-radio-cards" style={{ gridTemplateColumns: `repeat(${columns || options.length}, 1fr)` }}>
      {options.map(o => (
        <button key={o.id} type="button"
          className={'ci-radio-card' + (value === o.id ? ' ci-radio-card--on' : '')}
          onClick={() => onChange(o.id)}>
          <span className="ci-radio-card-dot"></span>
          <span className="ci-radio-card-label">{o.label}</span>
          {o.desc && <span className="ci-radio-card-desc">{o.desc}</span>}
        </button>
      ))}
    </div>
  );
}

function CISegmented({ value, onChange, options }) {
  return (
    <div className="ci-segmented">
      {options.map(o => (
        <button key={o.value} type="button"
          className={'ci-seg' + (value === o.value ? ' ci-seg--on' : '')}
          onClick={() => onChange(o.value)}>{o.label}</button>
      ))}
    </div>
  );
}

function CICheckRow({ checked, onChange, label }) {
  return (
    <button type="button" className={'ci-check-row' + (checked ? ' ci-check-row--on' : '')} onClick={() => onChange(!checked)}>
      <span className="ci-check-box">{checked && <span className="ci-check-mark">✓</span>}</span>
      <span className="ci-check-label">{label}</span>
    </button>
  );
}

function CIStepper({ label, value, onChange, min, max }) {
  const v = parseInt(value, 10) || 0;
  const set = (n) => onChange(Math.max(min ?? 0, Math.min(max ?? 999, n)));
  return (
    <div className="ci-field ci-field--qty">
      <label>{label}</label>
      <div className="ci-stepper">
        <button type="button" onClick={() => set(v - 1)}>−</button>
        <input className="ci-input--mono" value={value} onChange={(e) => onChange(e.target.value.replace(/\D/g, ''))} inputMode="numeric" />
        <button type="button" onClick={() => set(v + 1)}>+</button>
      </div>
    </div>
  );
}

function CIStepHeader({ kicker, title, desc }) {
  return (
    <div className="ci-step-head">
      <span className="ci-step-kicker">{kicker}</span>
      <h2 className="ci-step-title">{title}</h2>
      {desc && <p className="ci-step-desc">{desc}</p>}
    </div>
  );
}

/* ============================================================
   WIZARD STEPS
   ============================================================ */
function CIStepModalidade({ s, set }) {
  return (
    <div className="ci-step">
      <CIStepHeader kicker="Passo 1 — Início" title="Modalidade do contrato" desc="Escolha o tipo de serviço. As cláusulas se adaptam automaticamente." />
      <CIRadioCards value={s.modalidade} onChange={(v) => set('modalidade', v)} options={window.CI.MODALIDADES} columns={1} />
    </div>
  );
}

/* Aviso de homologação (achado "Melhoria" da auditoria de código):
   a seleção de parceiro instalador no Contrato Instalador não checava
   RHHomologacao — o alerta só existia rio abaixo, na liberação da
   Instalação (InstalacaoObraStore). Aqui é aviso, não bloqueio: uma
   contratação pode legitimamente começar antes da homologação estar
   completa, mas quem está criando o contrato precisa ver isso na hora,
   não só quando a obra já estiver pronta pra começar. */
function CIHomologacaoAviso({ cnpj }) {
  const [match, setMatch] = React.useState(null); // null | {found:false} | {found:true, nome, status}

  React.useEffect(() => {
    let vivo = true;
    if (!window.CI.isCNPJValid(cnpj) || !window.RHHomologacao) { setMatch(null); return; }
    const digits = window.CI.onlyDigits(cnpj);
    window.RHHomologacao.listarMontadores().then(async (lista) => {
      if (!vivo) return;
      const m = (lista || []).find((p) => window.CI.onlyDigits(p.cnpj || '') === digits);
      if (!m) { setMatch({ found: false }); return; }
      // statusGeralPorColaboradores lê parceiros_documentos_colaborador (schema
      // pós-migração 25/08) — statusGeral() antigo lia certificacoes (jsonb
      // obsoleto, ninguém mais grava nele desde a migração).
      const { status } = await window.RHHomologacao.statusGeralPorColaboradores(m.id);
      if (!vivo) return;
      setMatch({ found: true, nome: m.nome, status });
    }).catch(() => { if (vivo) setMatch(null); });
    return () => { vivo = false; };
  }, [cnpj]);

  if (!match) return null;
  if (!match.found) {
    return <div className="ci-cond-alert"><span className="ci-cond-dot"></span>Este CNPJ não está cadastrado em Homologação de Parceiros Instaladores — o contrato pode ser criado, mas a obra só libera com parceiro homologado.</div>;
  }
  if (match.status !== 'ok') {
    return <div className="ci-cond-alert"><span className="ci-cond-dot"></span>Parceiro "{match.nome}" encontrado em Homologação, mas o status está <b>{match.status}</b> — confira certificações antes de iniciar a obra.</div>;
  }
  return null;
}

function CIStepPartes({ s, set, errors }) {
  return (
    <div className="ci-step">
      <CIStepHeader kicker="Passo 2 — Partes" title="Dados da Contratada" desc="A Contratante (Vertical Parts) já está fixada. Preencha os dados da empresa contratada." />
      <div className="ci-contratante-note">
        <span className="ci-cn-label">Contratante (fixo)</span>
        <strong>VERTICAL PARTS LTDA-ME</strong>
        <span className="ci-cn-meta">CNPJ 15.822.325/0001-27 · Guarulhos/SP · Rep. Diego Yutaka Maeno</span>
      </div>
      <div className="ci-field-group">
        <h3 className="ci-group-title">Empresa</h3>
        <div className="ci-grid">
          <CIField label="Razão social" required width="full" value={s.c_razao} onChange={(v) => set('c_razao', v)} placeholder="Ex.: Montagem Vertical Serviços Ltda." error={errors.c_razao} />
          <CIField label="CNPJ" required mask="maskCNPJ" mono value={s.c_cnpj} onChange={(v) => set('c_cnpj', v)} placeholder="00.000.000/0000-00" error={errors.c_cnpj} />
        </div>
        <CIHomologacaoAviso cnpj={s.c_cnpj} />
        <div className="ci-grid">
          <CIField label="Logradouro (Rua/Av.)" required width="wide" value={s.c_rua} onChange={(v) => set('c_rua', v)} placeholder="Rua Exemplo" error={errors.c_rua} />
          <CIField label="Número" required width="narrow" value={s.c_numero} onChange={(v) => set('c_numero', v)} placeholder="123" error={errors.c_numero} />
        </div>
        <div className="ci-grid">
          <CIField label="Bairro" required value={s.c_bairro} onChange={(v) => set('c_bairro', v)} placeholder="Centro" error={errors.c_bairro} />
          <CIField label="Cidade" required value={s.c_cidade} onChange={(v) => set('c_cidade', v)} placeholder="São Paulo" error={errors.c_cidade} />
          <CIField label="UF" required width="narrow" value={s.c_estado} onChange={(v) => set('c_estado', v.toUpperCase().slice(0, 2))} placeholder="SP" error={errors.c_estado} />
          <CIField label="CEP" required mask="maskCEP" mono value={s.c_cep} onChange={(v) => set('c_cep', v)} placeholder="00000-000" error={errors.c_cep} />
        </div>
      </div>
      <div className="ci-field-group">
        <h3 className="ci-group-title">Responsável legal</h3>
        <div className="ci-grid">
          <CIField label="Nome completo" required width="full" value={s.r_nome} onChange={(v) => set('r_nome', v)} placeholder="Nome do responsável" error={errors.r_nome} />
        </div>
        <div className="ci-grid">
          <CIField label="Nacionalidade" required value={s.r_nacionalidade} onChange={(v) => set('r_nacionalidade', v)} placeholder="brasileiro(a)" error={errors.r_nacionalidade} />
          <CIField label="Estado civil" required value={s.r_estadoCivil} onChange={(v) => set('r_estadoCivil', v)} placeholder="casado(a)" error={errors.r_estadoCivil} />
          <CIField label="Profissão" required value={s.r_profissao} onChange={(v) => set('r_profissao', v)} placeholder="empresário(a)" error={errors.r_profissao} />
        </div>
        <div className="ci-grid">
          <CIField label="RG" required mask="maskRG" mono value={s.r_rg} onChange={(v) => set('r_rg', v)} placeholder="00.000.000-0" error={errors.r_rg} />
          <CIField label="CPF" required mask="maskCPF" mono value={s.r_cpf} onChange={(v) => set('r_cpf', v)} placeholder="000.000.000-00" error={errors.r_cpf} />
        </div>
        <CICheckRow checked={s.r_mesmoEndereco} onChange={(v) => set('r_mesmoEndereco', v)} label="Responsável reside no mesmo endereço da empresa" />
        {!s.r_mesmoEndereco && (
          <div className="ci-grid ci-grid--indent">
            <CIField label="Logradouro" required width="wide" value={s.r_rua} onChange={(v) => set('r_rua', v)} placeholder="Rua Exemplo" error={errors.r_rua} />
            <CIField label="Número" required width="narrow" value={s.r_numero} onChange={(v) => set('r_numero', v)} placeholder="123" error={errors.r_numero} />
            <CIField label="Bairro" required value={s.r_bairro} onChange={(v) => set('r_bairro', v)} placeholder="Centro" error={errors.r_bairro} />
            <CIField label="Cidade" required value={s.r_cidade} onChange={(v) => set('r_cidade', v)} placeholder="São Paulo" error={errors.r_cidade} />
            <CIField label="UF" required width="narrow" value={s.r_estado} onChange={(v) => set('r_estado', v.toUpperCase().slice(0, 2))} placeholder="SP" error={errors.r_estado} />
            <CIField label="CEP" required mask="maskCEP" mono value={s.r_cep} onChange={(v) => set('r_cep', v)} placeholder="00000-000" error={errors.r_cep} />
          </div>
        )}
      </div>
    </div>
  );
}

/* Master ID (Fase 2) — puxa uma Proposta já com Master ID (vinda da
   Precificação) e permite marcar QUAIS ativos (elevadores individuais)
   este contrato de instalação cobre — ex.: Instalador X monta os ativos
   1 e 2, Instalador Y monta o ativo 3, na mesma obra. Some silenciosamente
   se ainda não houver nenhuma Proposta com Master ID. */
function CISeletorProposta({ propostaId, ativosIndices, onSelecionarProposta, onLimparProposta, onToggleAtivo }) {
  const [propostas, setPropostas] = _ciUS(null);

  _ciUE(() => {
    window.__VP_SB?.sb.from('propostas')
      .select('id, numero_documento, numero_cotacao, master_id, titulo, data_json')
      .not('master_id', 'is', null)
      .order('criado_em', { ascending: false }).limit(200)
      .then(({ data }) => setPropostas(data || []));
  }, []);

  if (!propostas || !propostas.length) return null;
  const selecionada = propostas.find((p) => p.id === propostaId);
  const ativos = (selecionada && selecionada.data_json && selecionada.data_json.ativos) || [];

  return (
    <div className="ci-field-group">
      <h3 className="ci-group-title">Herdar de uma Proposta (Master ID)</h3>
      <CISelect label="Proposta" value={propostaId || ''}
        onChange={(id) => {
          /* "— selecione —" desvincula (antes não fazia nada, e não dava
             pra voltar a contrato avulso depois de escolher uma Proposta). */
          if (!id) { onLimparProposta(); return; }
          const p = propostas.find((x) => x.id === id); if (p) onSelecionarProposta(p);
        }}
        options={[{ value: '', label: '— selecione uma proposta —' }, ...propostas.map((p) => ({ value: p.id, label: `${p.master_id} · ${p.numero_documento} · ${p.titulo || ''}` }))]}
        width="full"/>
      {ativos.length > 0 && (
        <div style={{ marginTop: 10 }}>
          <div className="ci-group-title" style={{ fontSize: 12 }}>Ativos cobertos por este contrato</div>
          {ativos.map((a) => (
            <CICheckRow key={a.indice} checked={(ativosIndices || []).includes(a.indice)}
              onChange={() => onToggleAtivo(a.indice)}
              label={`${a.codigo || a.identificador} — ${a.identificador}${a.modelo ? ' · ' + a.modelo : ''}`}/>
          ))}
        </div>
      )}
    </div>
  );
}

/* Vínculo real com dossier_obra (Trilha B, 01/09) — sem isso, as parcelas
   do contrato não têm como saber qual checklist observar pra liberar
   pagamento automaticamente. Busca simples por cliente/obra/id; múltiplos
   dossiês cobertos pelo mesmo contrato (ex.: instalador monta 2 elevadores
   da mesma obra sob um único contrato). */
function CISeletorObras({ dossierIds, onToggle }) {
  const [busca, setBusca] = _ciUS('');
  const [resultados, setResultados] = _ciUS([]);
  const [selecionados, setSelecionados] = _ciUS([]);

  _ciUE(() => {
    if (!dossierIds || !dossierIds.length) { setSelecionados([]); return; }
    window.__VP_SB?.sb.from('dossier_obra').select('id, client_name, building_name').in('id', dossierIds)
      .then(({ data }) => setSelecionados(data || []));
  }, [JSON.stringify(dossierIds || [])]);

  _ciUE(() => {
    /* Tira caracteres que quebram a sintaxe do filtro .or() do PostgREST
       (vírgula, parênteses, %, *) — digitar "Silva, Jose" dava erro mudo. */
    const q = busca.replace(/[,()%*\\]/g, ' ').trim();
    if (q.length < 2) { setResultados([]); return; }
    const t = setTimeout(() => {
      window.__VP_SB?.sb.from('dossier_obra').select('id, client_name, building_name')
        .or(`client_name.ilike.%${q}%,building_name.ilike.%${q}%,id.ilike.%${q}%`)
        .limit(15).then(({ data }) => setResultados(data || []));
    }, 300);
    return () => clearTimeout(t);
  }, [busca]);

  return (
    <div className="ci-field-group">
      <h3 className="ci-group-title">Obra / Dossiê vinculado</h3>
      <p className="ci-field-hint" style={{ marginBottom: 8 }}>
        Vincule o(s) dossiê(s)/equipamento(s) reais cobertos por este contrato — é o que libera as parcelas automaticamente conforme a instalação avança.
      </p>
      {selecionados.length > 0 && (
        <div style={{ marginBottom: 10, display: 'flex', flexDirection: 'column', gap: 6 }}>
          {selecionados.map((d) => (
            <div key={d.id} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', background: '#f0f8f0', border: '1px solid #cce6cc', borderRadius: 6, padding: '6px 10px', fontSize: 13 }}>
              <span>✓ {d.id} — {d.building_name || d.client_name}</span>
              <button type="button" className="ci-btn ci-btn--ghost" onClick={() => onToggle(d.id)}>remover</button>
            </div>
          ))}
        </div>
      )}
      <input className="ci-input" placeholder="Buscar por cliente, obra ou nº do dossiê (DOS-...)" value={busca} onChange={(e) => setBusca(e.target.value)} />
      {resultados.length > 0 && (
        <div style={{ marginTop: 6, border: '1px solid #ddd', borderRadius: 6 }}>
          {resultados.map((d) => (
            <div key={d.id} style={{ padding: 8, cursor: 'pointer', display: 'flex', justifyContent: 'space-between', fontSize: 13 }}
              onClick={() => { onToggle(d.id); setBusca(''); setResultados([]); }}>
              <span>{d.id} — {d.building_name || d.client_name}</span>
              {(dossierIds || []).includes(d.id) && <span>✓</span>}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

/* Equipamentos cobertos + Nº do contrato (29/09). O Nº nasce assim que há ao
   menos um equipamento (ativo da Proposta marcado, ou nº de série/projeto
   digitado) e fica FIXO depois — trocar equipamentos não muda o número.
   Com Proposta: VPNI-<Nº da cotação>. Sem Proposta: VPNI-<1º nº informado>.
   Já existindo contrato com a mesma base (ex.: 2 montadores na mesma obra),
   o novo ganha -2, -3… */
function CIEquipamentosNumero({ s, set, errors }) {
  const [texto, setTexto] = _ciUS('');
  const [conflitos, setConflitos] = _ciUS([]);
  const lista = window.CI.equipamentosDoContrato(s);
  const base = window.CI.numeroBaseContrato(s);

  _ciUE(() => {
    let vivo = true;
    if (s.numeroContrato || !base) return;
    window.CIStore.reservarNumero(base).then((n) => {
      if (!vivo || !n) return;
      set('numeroContratoBase', base);
      set('numeroContrato', n);
    });
    return () => { vivo = false; };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [base, s.numeroContrato]);

  /* Aviso (não bloqueia): ativo já coberto por outro contrato da mesma Proposta. */
  _ciUE(() => {
    let vivo = true;
    setConflitos([]);
    const sb = window.__VP_SB && window.__VP_SB.sb;
    if (!sb || !s.propostaId || !(s.ativosIndices || []).length) return;
    sb.from('contratos_instalador').select('numero_documento, ativos_indices, status').eq('proposta_id', s.propostaId)
      .then(({ data }) => {
        if (!vivo) return;
        setConflitos((data || []).filter((c) => c.status !== 'recusado' && c.status !== 'expirado'
          && (c.ativos_indices || []).some((i) => (s.ativosIndices || []).includes(i))).map((c) => c.numero_documento));
      });
    return () => { vivo = false; };
  }, [s.propostaId, JSON.stringify(s.ativosIndices || [])]);

  const adicionar = () => {
    const novos = texto.split(/[,;\n]/).map((v) => v.trim().toUpperCase()).filter(Boolean);
    if (!novos.length) return;
    const atual = s.equipamentosManuais || [];
    set('equipamentosManuais', [...atual, ...novos.filter((v, i) => !atual.includes(v) && novos.indexOf(v) === i)]);
    setTexto('');
  };
  const remover = (v) => set('equipamentosManuais', (s.equipamentosManuais || []).filter((x) => x !== v));

  return (
    <div className="ci-field-group">
      <h3 className="ci-group-title">Equipamentos cobertos por este contrato *</h3>
      <p className="ci-field-hint" style={{ marginBottom: 8 }}>
        {s.propostaId
          ? 'Marque os ativos da Proposta acima e/ou digite nº de série. Cada montador tem o seu contrato, com os equipamentos que ele vai montar — o número do contrato nasce daqui.'
          : 'Sem Proposta: informe o nº do projeto ou o nº de série de cada equipamento (separe por vírgula). O número do contrato nasce do 1º informado.'}
      </p>
      {lista.length > 0 && (
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 8 }}>
          {lista.map((v) => {
            const manual = (s.equipamentosManuais || []).includes(v);
            return (
              <span key={v} className="ci-cond-pill ci-cond-pill--info" style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                {v}{manual && <button type="button" className="ci-btn ci-btn--ghost" style={{ padding: '0 4px' }} onClick={() => remover(v)} title="Remover">✕</button>}
              </span>
            );
          })}
        </div>
      )}
      <div style={{ display: 'flex', gap: 8 }}>
        <input className={'ci-input' + (errors.equipamentos ? ' ci-input--error' : '')} value={texto} placeholder="Nº do projeto ou nº de série (Enter para adicionar)"
          onChange={(e) => setTexto(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); adicionar(); } }} />
        <button type="button" className="ci-btn ci-btn--dark" onClick={adicionar}>Adicionar</button>
      </div>
      {errors.equipamentos && <span className="ci-field-err">{errors.equipamentos}</span>}
      <div className="ci-contratante-note" style={{ marginTop: 10 }}>
        <span className="ci-cn-label">Nº do contrato</span>
        <strong>{s.numeroContrato || (base ? 'gerando…' : 'nasce quando você informar os equipamentos')}</strong>
        {s.numeroContrato && <span className="ci-cn-meta">Fixo — trocar os equipamentos depois não muda o número.</span>}
      </div>
      {conflitos.length > 0 && (
        <div className="ci-cond-alert"><span className="ci-cond-dot"></span>Atenção: algum(ns) destes ativos já consta(m) em outro contrato desta Proposta ({conflitos.join(', ')}). Confirme que não é uma contratação em duplicidade.</div>
      )}
    </div>
  );
}

function CIStepObjeto({ s, set, errors }) {
  errors = errors || {};
  const isElevador = s.equipamento === 'elevador';
  const remocao = window.CI.isRemocao(s);
  const especial = window.CI.isCargaEspecial(s);

  const aplicarProposta = (p) => {
    set('masterId', p.master_id); set('propostaId', p.id); set('ativosIndices', []);
    set('numeroContrato', null); set('numeroContratoBase', null);  // número nasce de novo a partir da cotação
    /* Nº da cotação (alimenta EventosFluxo/Aval Financeiro no createDraft —
       antes nunca era preenchido). Cai pro parse do Master ID se a coluna
       vier vazia. */
    set('numeroCotacao', p.numero_cotacao ?? window.MasterIdEngine?.parseNumeroCotacao?.(p.master_id) ?? null);
    /* Snapshot dos ativos (com custoInstalacaoMaoDeObraRs por equipamento) —
       usado no Passo 5 pra sugerir o Valor total (Fase 3b da granularidade). */
    set('ativosSnapshot', (p.data_json && p.data_json.ativos) || []);
    const valores = p.data_json?.elevador?.valores;
    if (valores?.quantidade) set('quantidade', Number(valores.quantidade) || s.quantidade);
  };
  const limparProposta = () => {
    set('masterId', null); set('propostaId', null); set('ativosIndices', []); set('ativosSnapshot', []); set('numeroCotacao', null);
    set('numeroContrato', null); set('numeroContratoBase', null);
  };
  const toggleAtivo = (indice) => {
    const atual = s.ativosIndices || [];
    set('ativosIndices', atual.includes(indice) ? atual.filter((i) => i !== indice) : [...atual, indice]);
  };
  const toggleDossier = (dossierId) => {
    const atual = s.dossierIds || [];
    set('dossierIds', atual.includes(dossierId) ? atual.filter((i) => i !== dossierId) : [...atual, dossierId]);
  };

  return (
    <div className="ci-step">
      <CIStepHeader kicker="Passo 3 — Objeto" title="Objeto do contrato" desc="Defina o equipamento e o escopo. Os campos mudam conforme o tipo selecionado." />
      <CISeletorObras dossierIds={s.dossierIds} onToggle={toggleDossier} />
      <CISeletorProposta propostaId={s.propostaId} ativosIndices={s.ativosIndices} onSelecionarProposta={aplicarProposta} onLimparProposta={limparProposta} onToggleAtivo={toggleAtivo}/>
      {!s.propostaId && (
        <div className="ci-cond-alert"><span className="ci-cond-dot"></span>Contrato avulso (sem Proposta vinculada): o envio continua exigindo aprovação do CEO na Central de Decisões, e o vínculo automático de respostas por e-mail só funciona quando o instalador responde ao próprio e-mail enviado (sem Nº de Cotação não há vínculo por assunto). Se possível, selecione a Proposta de origem acima antes de continuar.</div>
      )}
      <CIEquipamentosNumero s={s} set={set} errors={errors} />
      <div className="ci-field-group">
        <h3 className="ci-group-title">Equipamento</h3>
        <CIRadioCards value={s.equipamento} onChange={(v) => set('equipamento', v)} options={window.CI.EQUIPAMENTOS} columns={3} />
        <div className="ci-grid" style={{ marginTop: 16 }}>
          <CIStepper label="Quantidade" value={s.quantidade} onChange={(v) => set('quantidade', v)} min={1} max={99} />
          <div className="ci-field ci-field--fixed-brand">
            <label>Marca</label>
            <div className="ci-brand-lock"><span>Vertical Parts</span><span className="ci-lock-ico">🔒</span></div>
          </div>
        </div>
      </div>
      {isElevador && (
        <div className="ci-field-group ci-field-group--cond">
          <h3 className="ci-group-title">Especificações do elevador</h3>
          <div className="ci-grid">
            <CISelect label="Tipo de elevador" value={s.tipoElevador} onChange={(v) => set('tipoElevador', v)} options={window.CI.TIPOS_ELEVADOR.map(t => ({ value: t.id, label: t.label }))} />
            <CISelect label="Quantidade de paradas" value={s.paradas} onChange={(v) => set('paradas', v)} options={window.CI.PARADAS_OPCOES.map(p => ({ value: p, label: p }))} />
            {s.paradas === 'Personalizado' && <CIField label="Paradas (qtd.)" required width="narrow" mono value={s.paradasCustom} onChange={(v) => set('paradasCustom', v.replace(/\D/g, ''))} placeholder="30" error={errors.paradasCustom} />}
          </div>
          <div className="ci-grid">
            <CIField label="Capacidade de carga (kg)" mono value={s.capacidadeCarga} onChange={(v) => set('capacidadeCarga', v.replace(/[^\d.,]/g, ''))} placeholder="1000" hint="Acima de 1.000 kg ativa a cláusula de equipamento especial." />
          </div>
          <CICheckRow checked={s.cargaEspecial} onChange={(v) => set('cargaEspecial', v)} label="Marcar como carga especial (içamento / reforço NR-12 e NR-18)" />
          {especial && <div className="ci-cond-alert"><span className="ci-cond-dot"></span>Cláusula de Equipamento Especial será inserida.</div>}
        </div>
      )}
      {remocao && (
        <div className="ci-field-group ci-field-group--cond">
          <h3 className="ci-group-title">{s.modalidade === 'remocao_adequacao' ? 'Nova instalação' : 'Destino da remoção'}</h3>
          <CIField label={s.modalidade === 'remocao_adequacao' ? 'Local da nova instalação' : 'Destino do equipamento removido'} required width="full" value={s.destino} onChange={(v) => set('destino', v)} placeholder="Endereço / destino" error={errors.destino} />
        </div>
      )}
      <div className="ci-field-group">
        <h3 className="ci-group-title">Escopo</h3>
        <CITextArea label="Descrição dos serviços *" value={s.descricaoServicos} onChange={(v) => set('descricaoServicos', v)} rows={3} placeholder="Detalhe os serviços (montagem, alinhamento, testes, etc.)" error={errors.descricaoServicos} />
        <CITextArea label="Local do serviço *" value={s.localServico} onChange={(v) => set('localServico', v)} rows={2} placeholder="Endereço completo de onde será realizado o serviço" hint="Endereço da obra onde o serviço acontece." error={errors.localServico} />
      </div>
    </div>
  );
}

function CIStepLogistica({ s, set, errors }) {
  errors = errors || {};
  const longa = window.CI.isLongaDistancia(s);
  const km = parseFloat(String(s.distancia).replace(',', '.')) || 0;
  return (
    <div className="ci-step">
      <CIStepHeader kicker="Passo 4 — Logística" title="Logística e local" desc="Localização da obra e distância de Guarulhos/SP. A partir de 100 km uma cláusula de deslocamento é inserida." />
      <div className="ci-field-group">
        <div className="ci-grid">
          <CIField label="Cidade da obra" required={longa} value={s.obraCidade} onChange={(v) => set('obraCidade', v)} placeholder="Campinas" error={errors.obraCidade} />
          <CIField label="UF" required={longa} width="narrow" value={s.obraEstado} onChange={(v) => set('obraEstado', v.toUpperCase().slice(0, 2))} placeholder="SP" error={errors.obraEstado} />
          <CIField label="Distância de Guarulhos/SP (km)" mono value={s.distancia} onChange={(v) => set('distancia', v.replace(/[^\d.,]/g, ''))} placeholder="0" hint="≥ 100 km ativa a cláusula de logística." />
        </div>
        <div className={'ci-distance-gauge' + (longa ? ' ci-distance-gauge--on' : '')}>
          <div className="ci-dg-track"><div className="ci-dg-fill" style={{ width: Math.min(100, (km / 200) * 100) + '%' }}></div><div className="ci-dg-mark" style={{ left: '50%' }}><span>100 km</span></div></div>
          <div className="ci-dg-status">{longa ? `${km} km — cláusula de logística ativada` : (km > 0 ? `${km} km — dentro do raio padrão` : 'Informe a distância')}</div>
        </div>
      </div>
      {longa && (
        <div className="ci-field-group ci-field-group--cond">
          <h3 className="ci-group-title">Cláusula de logística e deslocamento</h3>
          <label className="ci-mini-label">Forma de tratamento</label>
          <CISegmented value={s.logModo} onChange={(v) => set('logModo', v)} options={[{ value: 'despesas', label: 'Despesas à parte' }, { value: 'prazo', label: 'Extensão de prazo' }]} />
          {s.logModo === 'despesas' && (
            <div style={{ marginTop: 16 }}>
              <label className="ci-mini-label">Despesas por conta de</label>
              <CISegmented value={s.logResponsavel} onChange={(v) => set('logResponsavel', v)} options={[{ value: 'contratada', label: 'Contratada' }, { value: 'contratante', label: 'Contratante' }]} />
            </div>
          )}
          {s.logModo === 'prazo' && (
            <div className="ci-grid" style={{ marginTop: 16 }}>
              <CIField label="Dias adicionais de prazo" required width="narrow" mono value={s.logDiasExtra} onChange={(v) => set('logDiasExtra', v.replace(/\D/g, ''))} placeholder="10" error={errors.logDiasExtra} />
            </div>
          )}
        </div>
      )}
    </div>
  );
}

/* Pré-preenche banco/agência/conta/PIX a partir do cadastro da empresa
   (Cadastros → Empresas Instaladoras / RH Operacional → Homologação —
   mesma tabela parceiros_instaladores) pelo CNPJ já digitado no Passo 2.
   Só preenche se os 4 campos ainda estiverem vazios — nunca sobrescreve o
   que o usuário já digitou nesse contrato (reabrir rascunho, editar,
   corrigir dado desatualizado do cadastro etc.). Sem dado bancário
   cadastrado na empresa, some silenciosamente — comportamento de hoje
   (digitar na mão) continua igual. */
function CIBancoPrefill({ cnpj, s, set }) {
  const [status, setStatus] = _ciUS(null); // null | 'found'

  _ciUE(() => {
    let vivo = true;
    if (!window.CI.isCNPJValid(cnpj) || !window.RHHomologacao) return;
    if (s.banco || s.agencia || s.conta || s.pix) return;
    const digits = window.CI.onlyDigits(cnpj);
    window.RHHomologacao.listarMontadores().then((lista) => {
      if (!vivo) return;
      const m = (lista || []).find((p) => window.CI.onlyDigits(p.cnpj || '') === digits);
      if (!m || (!m.banco && !m.agencia && !m.conta && !m.pix)) return;
      set('banco', m.banco || '');
      set('agencia', m.agencia || '');
      set('conta', m.conta || '');
      set('pix', m.pix || '');
      setStatus('found');
    }).catch(() => {});
    return () => { vivo = false; };
  }, [cnpj]);

  if (status !== 'found') return null;
  return <div className="ci-cond-alert"><span className="ci-cond-dot"></span>Dados bancários preenchidos automaticamente a partir do cadastro da empresa (Cadastros → Empresas Instaladoras). Confira antes de gerar o contrato.</div>;
}

function CIStepPagamento({ s, set, errors }) {
  /* Sugestão de valor (Fase 3b da granularidade) — soma o custo de mão de
     obra de instalação (calculado por equipamento em Precificação) dos
     ativos vinculados a este contrato. Só sugere: pré-preenche o campo
     enquanto ele estiver vazio, nunca sobrescreve um valor já digitado. */
  const sugestaoInstalacao = (s.ativosSnapshot || [])
    .filter((a) => (s.ativosIndices || []).includes(a.indice))
    .reduce((t, a) => t + (Number(a.custoInstalacaoMaoDeObraRs) || 0), 0);
  _ciUE(() => {
    if (sugestaoInstalacao > 0 && !s.valorTotal) {
      set('valorTotal', window.CI.maskMoeda(String(Math.round(sugestaoInstalacao * 100))));
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [sugestaoInstalacao]);

  const addParcela = () => set('parcelas', [...(s.parcelas || []), { valor: '', data: '', descricao: '' }]);
  const updateParcela = (i, k, v) => {
    const arr = [...s.parcelas];
    arr[i] = { ...arr[i], [k]: k === 'valor' ? window.CI.maskMoeda(v) : v };
    set('parcelas', arr);
  };
  const removeParcela = (i) => set('parcelas', s.parcelas.filter((_, j) => j !== i));
  return (
    <div className="ci-step">
      <CIStepHeader kicker="Passo 5 — Pagamento" title="Valores e pagamento" desc="Valor total, parcelamento e dados bancários. O valor por extenso é gerado automaticamente." />
      <div className="ci-field-group">
        <div className="ci-grid">
          <CIMoneyField label="Valor total do contrato" required width="wide" value={s.valorTotal} onChange={(v) => set('valorTotal', v)} error={errors.valorTotal} />
        </div>
        {sugestaoInstalacao > 0 && (
          <p className="ci-field-hint">
            Sugestão com base na Precificação: <b>R$ {window.CI.fmtMoeda(sugestaoInstalacao)}</b> (mão de obra do(s) equipamento(s) vinculado(s)).{' '}
            <button type="button" className="ci-btn ci-btn--ghost" style={{ padding: '0 4px' }}
              onClick={() => set('valorTotal', window.CI.maskMoeda(String(Math.round(sugestaoInstalacao * 100))))}>
              Usar este valor
            </button>
          </p>
        )}
        <label className="ci-mini-label">Forma de pagamento</label>
        <CISegmented value={s.formaPagamento} onChange={(v) => set('formaPagamento', v)} options={[
          { value: '2', label: '2 parcelas (50/50)' },
          { value: '3', label: '3 parcelas' },
          { value: 'custom', label: 'Personalizado' },
        ]} />
        {s.formaPagamento === 'custom' && (
          <div className="ci-parcelas">
            {(s.parcelas || []).map((p, i) => (
              <div className="ci-parcela-row" key={i}>
                <span className="ci-parcela-n">{i + 1}ª</span>
                <div className="ci-parcela-fields">
                  <div className="ci-input ci-input--prefix ci-parcela-money">
                    <span className="ci-input-prefix">R$</span>
                    <input className="ci-input-bare ci-input--mono" value={p.valor} onChange={(e) => updateParcela(i, 'valor', e.target.value)} placeholder="0,00" inputMode="numeric" />
                  </div>
                  <input className="ci-input" value={p.descricao} onChange={(e) => updateParcela(i, 'descricao', e.target.value)} placeholder="Descrição / marco (ex.: na assinatura)" />
                </div>
                <button type="button" className="ci-parcela-del" onClick={() => removeParcela(i)}>✕</button>
              </div>
            ))}
            <button type="button" className="ci-add-parcela" onClick={addParcela}>+ Adicionar parcela</button>
            {(() => {
              const total = Math.round(window.CI.moedaParaNumero(s.valorTotal) * 100);
              const soma = (s.parcelas || []).reduce((t, p) => t + Math.round(window.CI.moedaParaNumero(p.valor) * 100), 0);
              if (!total) return null;
              const dif = total - soma;
              return <p className={dif === 0 ? 'ci-field-hint' : 'ci-field-err'}>
                {dif === 0 ? '✓ Soma das parcelas confere com o valor total.' : `Soma das parcelas: R$ ${window.CI.fmtMoeda(soma / 100)} — ${dif > 0 ? 'faltam' : 'sobram'} R$ ${window.CI.fmtMoeda(Math.abs(dif) / 100)} para fechar o valor total.`}
              </p>;
            })()}
            {errors.parcelas && <span className="ci-field-err">{errors.parcelas}</span>}
          </div>
        )}
        {(s.dossierIds || []).length === 0 && s.formaPagamento !== 'custom' && (
          <div className="ci-cond-alert"><span className="ci-cond-dot"></span>Sem obra/dossiê vinculado (Passo 3): só a 1ª parcela ("início dos trabalhos") nasce liberada para pagamento; as demais só liberam automaticamente quando o contrato estiver ligado a um dossiê.</div>
        )}
      </div>
      <div className="ci-field-group">
        <h3 className="ci-group-title">Dados bancários da Contratada</h3>
        <CIBancoPrefill cnpj={s.c_cnpj} s={s} set={set} />
        <div className="ci-grid">
          <CIField label="Banco" value={s.banco} onChange={(v) => set('banco', v)} placeholder="Banco do Brasil" />
          <CIField label="Agência" width="narrow" mono value={s.agencia} onChange={(v) => set('agencia', v)} placeholder="0000" />
          <CIField label="Conta" mono value={s.conta} onChange={(v) => set('conta', v)} placeholder="00000-0" />
        </div>
        <CIField label="Chave PIX" width="full" mono value={s.pix} onChange={(v) => set('pix', v)} placeholder="CNPJ, e-mail, telefone ou chave aleatória" />
      </div>
    </div>
  );
}

/* Anexos obrigatórios (Cláusula 2.13) — até 01/09 era um checklist manual
   solto (`s.anexos`, 10 itens fixos), sem nenhuma ligação com o status real
   de homologação da Contratada — marcar/desmarcar aqui não refletia (nem
   alimentava) parceiros_documentos_empresa/-colaborador. Vira leitura
   automática, somente leitura: busca a empresa pelo CNPJ do Passo 2 e
   mostra o status real (escopo empresa) vindo do catálogo completo via
   RHHomologacao.statusDocumentosEmpresa. Não bloqueia gerar o contrato
   mesmo com pendência — mesmo espírito não-bloqueante do aviso do Passo 2
   (CIHomologacaoAviso): quem está montando o contrato precisa VER o real,
   a decisão de seguir ou não é dela. */
function CIAnexosStatus({ cnpj }) {
  const [estado, setEstado] = _ciUS(null); // null=carregando | 'sem-cnpj' | 'nao-cadastrada' | {docs:[...]}

  _ciUE(() => {
    let vivo = true;
    if (!window.CI.isCNPJValid(cnpj) || !window.RHHomologacao) { setEstado('sem-cnpj'); return; }
    setEstado(null);
    const digits = window.CI.onlyDigits(cnpj);
    window.RHHomologacao.listarMontadores().then(async (lista) => {
      if (!vivo) return;
      const m = (lista || []).find((p) => window.CI.onlyDigits(p.cnpj || '') === digits);
      if (!m) { setEstado('nao-cadastrada'); return; }
      const docs = await window.RHHomologacao.statusDocumentosEmpresa(m.id);
      if (vivo) setEstado({ nome: m.nome, docs });
    }).catch(() => { if (vivo) setEstado('nao-cadastrada'); });
    return () => { vivo = false; };
  }, [cnpj]);

  const ICON = { entregue: '✓', vencido: '⚠', sem_registro: '—' };
  const COR = { entregue: '#00aa00', vencido: '#cc2222', sem_registro: '#999' };
  const LABEL = { entregue: 'Entregue', vencido: 'Vencido', sem_registro: 'Sem registro' };

  return (
    <div className="ci-field-group">
      <h3 className="ci-group-title">Anexos obrigatórios (Cláusula 2.13)</h3>
      {estado === null && <p className="ci-field-hint">Carregando status real de homologação…</p>}
      {estado === 'sem-cnpj' && <p className="ci-field-hint">Preencha o CNPJ da Contratada no Passo 2 pra ver o status real dos documentos.</p>}
      {estado === 'nao-cadastrada' && (
        <div className="ci-cond-alert"><span className="ci-cond-dot"></span>Empresa não encontrada em Homologação — cadastre em Cadastros → Empresas Instaladoras pra rastrear os documentos aqui. O contrato pode ser gerado mesmo assim; a lista de anexos exigidos continua na Cláusula 2.13.</div>
      )}
      {estado && typeof estado === 'object' && (
        <>
          <p className="ci-field-hint" style={{ marginBottom: 8 }}>Status real de homologação de <b>{estado.nome}</b> — não bloqueia gerar o contrato, é só pra você ver o que falta antes de enviar.</p>
          <div className="ci-anexos-grid">
            {estado.docs.map((d) => (
              <div key={d.documento_id} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, padding: '4px 0' }}>
                <span style={{ color: COR[d.status], fontWeight: 700, width: 16, textAlign: 'center' }}>{ICON[d.status]}</span>
                <span>{d.nome}{!d.obrigatorio && <span style={{ color: '#999' }}> · opcional</span>}</span>
                <span style={{ color: COR[d.status], marginLeft: 'auto', fontSize: 11 }}>{LABEL[d.status]}</span>
              </div>
            ))}
          </div>
        </>
      )}
    </div>
  );
}

function CIStepRevisao({ s, set, errors }) {
  errors = errors || {};
  const conds = window.CI.activeConditionals(s);
  return (
    <div className="ci-step">
      <CIStepHeader kicker="Passo 6 — Revisão" title="Anexos e assinatura" desc="Confirme os documentos obrigatórios e a data. Depois é só gerar o contrato." />
      <CIAnexosStatus cnpj={s.c_cnpj} />
      <div className="ci-field-group">
        <h3 className="ci-group-title">Local e data</h3>
        <div className="ci-grid">
          <CIField label="Cidade" value={s.cidadeAssinatura} onChange={(v) => set('cidadeAssinatura', v)} placeholder="Guarulhos" error={errors.cidadeAssinatura} />
          <CIField label="Dia" width="narrow" mono value={s.dataDia} onChange={(v) => set('dataDia', v.replace(/\D/g, '').slice(0, 2))} placeholder="01" error={errors.dataDia} />
          <CISelect label="Mês" value={s.dataMes} onChange={(v) => set('dataMes', v)} options={window.CI.MESES.map(m => ({ value: m, label: m }))} />
          <CIField label="Ano" width="narrow" mono value={s.dataAno} onChange={(v) => set('dataAno', v.replace(/\D/g, '').slice(0, 4))} placeholder="2026" error={errors.dataAno} />
        </div>
      </div>
      <div className="ci-field-group">
        <h3 className="ci-group-title">Cláusulas condicionais ativas</h3>
        {conds.length === 0
          ? <p className="ci-empty-conds">Nenhuma cláusula condicional ativada. O contrato usará apenas as cláusulas padrão.</p>
          : <div className="ci-cond-summary">{conds.map(c => <span key={c.id} className={'ci-cond-pill ci-cond-pill--' + c.tone}>{c.label}</span>)}</div>}
      </div>
    </div>
  );
}

/* ============================================================
   SEND MODAL — WhatsApp / E-mail / Link copiável
   ============================================================ */
function CISendModal({ record, onClose, onSent }) {
  const [channel, setChannel] = _ciUS(record.channel || 'whatsapp');
  const [contact, setContact] = _ciUS((record.recipient && record.recipient.contact) || '');
  const [name, setName] = _ciUS((record.recipient && record.recipient.name) || record.responsavel_nome || '');
  const [sent, setSent] = _ciUS(false);
  const [copied, setCopied] = _ciUS(false);
  const [sending, setSending] = _ciUS(false);

  const pretty = window.CIStore.prettyUrl(record.token);
  const real = window.CIStore.signUrl(record.token);
  const valorFmt = record.valor_total ? 'R$ ' + window.CI.fmtMoeda(record.valor_total) : '—';

  const message =
    `Olá${name ? ' ' + name.split(' ')[0] : ''}! A Vertical Parts enviou um contrato para sua assinatura digital.\n\n` +
    `Contrato ${record.numero_documento}\n${record.titulo}\nValor: ${valorFmt}\n\n` +
    `Assine pelo link seguro (válido por 7 dias):\n${real}`;

  const copyLink = () => {
    navigator.clipboard && navigator.clipboard.writeText(real);
    setCopied(true); setTimeout(() => setCopied(false), 1600);
  };

  const handleSend = async () => {
    if (sending) return;
    const numeroCotacao = window.MasterIdEngine?.parseNumeroCotacao?.(record.master_id) ?? null;
    if (window.DecisoesStore) {
      const gate = await window.DecisoesStore.podeContratarInstalador(numeroCotacao, {
        contratoInstaladorId: record.id, contratada: record.contratada_nome, valor: record.valor_total,
      });
      if (!gate.ok) { window.toast ? window.toast(gate.motivo, 'warning') : alert(gate.motivo); return; }
    }
    setSending(true);
    try {
      /* 30/09 — e-mail: só registra "enviado" DEPOIS de o send-email
         confirmar (antes marcava antes, e com falha + mailto o status
         ficava "enviado" sem nada ter saído). */
      let updated = null;
      if (channel !== 'email') updated = await window.CIStore.markSent(record.id, channel, { name, contact });
      if (channel === 'whatsapp') {
        window.open(window.CIStore.whatsAppHref(contact, message), '_blank');
      } else if (channel === 'email') {
        /* 29/09 — mesmo padrão já aplicado em RFQ, Proposta e Contrato de
           Venda: tenta send-email (SMTP direto) primeiro, com referenciaTipo/
           referenciaId (+ numeroCotacao quando existir, já resolvido acima
           pro gate de alçada) pra ficar em Enviados/Linha do Tempo e o
           read-inbox conseguir casar a resposta do instalador de volta a
           este contrato. Cai pro mailto: (como sempre foi) só se o envio
           direto falhar — nunca deixa o vendedor sem alternativa.
           29/09 (2) — achado real corrigido: contrato sem Proposta/
           Formulário de origem (numeroCotacao null) antes pulava o
           send-email inteiro — nunca tentava registrar nada. `send-email`/
           `read-inbox` agora aceitam vínculo só por `referenciaId` (sem
           numeroCotacao) — o vínculo por Message-ID não depende de Nº de
           Cotação nenhum. Sempre tenta send-email; numeroCotacao null não
           é mais motivo pra pular. */
        /* 01/10 — chamada em si extraída pro EmailEnvioHelper (compartilhado
           com RFQ/Proposta/Contrato de Venda); sucesso/falha continuam
           decididos aqui, sem mudança de comportamento. */
        const { enviouDireto } = await window.EmailEnvioHelper.tentarEnviarDireto({
          to: contact, subject: `Contrato ${record.numero_documento} — Assinatura digital | Vertical Parts`, text: message,
          numeroCotacao, referenciaTipo: 'contrato_instalador', referenciaId: record.id,
        });
        if (enviouDireto) {
          updated = await window.CIStore.markSent(record.id, channel, { name, contact });
          window.toast?.(`E-mail enviado para ${contact}.`, 'success');
        } else {
          window.open(window.CIStore.mailtoHref(contact, `Contrato ${record.numero_documento} — Assinatura digital | Vertical Parts`, message), '_blank');
          window.toast?.('O envio automático por e-mail FALHOU — nada foi enviado e o contrato NÃO foi marcado como enviado. Abrindo seu e-mail padrão para envio manual; depois de enviar, use WhatsApp/Link para registrar o envio.', 'error');
          return;
        }
      }
      setSent(true);
      onSent && onSent(updated);
    } catch (e) {
      alert('Erro ao registrar envio: ' + (e.message || e));
    } finally {
      setSending(false);
    }
  };

  return (
    <div className="ci-modal-backdrop" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="ci-modal">
        <div className="ci-modal-head">
          <div>
            <h2>{sent ? 'Enviado' : 'Enviar para assinatura'}</h2>
            <p>{sent ? 'O contrato está aguardando a assinatura do destinatário.' : 'Gere o link seguro e escolha o canal de envio.'}</p>
          </div>
          <button className="ci-modal-x" onClick={onClose}>✕</button>
        </div>
        <div className="ci-modal-body">
          <div className="ci-mini">
            <span className="ci-mini-num">{record.numero_documento}</span>
            <span className="ci-mini-title">{record.contratada_nome}</span>
            <div className="ci-mini-row"><span>{record.objeto_resumo}</span><b>{valorFmt}</b></div>
          </div>
          {!sent ? (
            <>
              <label className="ci-mini-label" style={{ display: 'block', marginBottom: 8 }}>Link único e seguro</label>
              <div className="ci-linkbox">
                <code>{real}</code>
                <button onClick={copyLink}>{copied ? 'Copiado ✓' : 'Copiar'}</button>
              </div>
              <div className="ci-channels">
                <button className={'ci-channel' + (channel === 'whatsapp' ? ' on' : '')} onClick={() => setChannel('whatsapp')}>
                  <span className="ci-channel-icon">📱</span><span>WhatsApp</span><small>Abre o WhatsApp Web</small>
                </button>
                <button className={'ci-channel' + (channel === 'email' ? ' on' : '')} onClick={() => setChannel('email')}>
                  <span className="ci-channel-icon">✉️</span><span>E-mail</span><small>Template profissional</small>
                </button>
                <button className={'ci-channel' + (channel === 'link' ? ' on' : '')} onClick={() => setChannel('link')}>
                  <span className="ci-channel-icon">🔗</span><span>Link</span><small>Copie e envie por fora</small>
                </button>
              </div>
              <div className="ci-field">
                <label>Nome do destinatário</label>
                <input className="ci-input" value={name} onChange={(e) => setName(e.target.value)} placeholder="Nome do signatário" />
              </div>
              {channel !== 'link' && (
                <div className="ci-field">
                  <label>{channel === 'whatsapp' ? 'WhatsApp (com DDD)' : 'E-mail'}</label>
                  <input className="ci-input" value={contact} onChange={(e) => setContact(e.target.value)} placeholder={channel === 'whatsapp' ? '(11) 99999-0000' : 'cliente@empresa.com.br'} />
                </div>
              )}
            </>
          ) : (
            <div className="ci-sent-ok">
              <div className="ci-check">✓</div>
              <h3>Link {channel === 'link' ? 'registrado' : 'enviado via ' + (channel === 'whatsapp' ? 'WhatsApp' : 'E-mail')}</h3>
              <p>Status atualizado para <b>ENVIADO</b>. Você acompanha a abertura e a assinatura no painel.</p>
              <div className="ci-linkbox">
                <code>{real}</code>
                <button onClick={copyLink}>{copied ? 'Copiado ✓' : 'Copiar'}</button>
              </div>
              <a className="ci-btn ci-btn--dark" href={real} target="_blank" rel="noopener" style={{ textDecoration: 'none', marginTop: 8, display:'inline-block' }}>Abrir página de assinatura →</a>
            </div>
          )}
        </div>
        <div className="ci-modal-foot">
          {!sent ? (
            <>
              <button className="ci-btn ci-btn--ghost" onClick={onClose}>Cancelar</button>
              <button className="ci-btn ci-btn--primary" onClick={handleSend} disabled={sending}>
                {sending ? 'Enviando…' : (channel === 'whatsapp' ? 'Enviar pelo WhatsApp →' : channel === 'email' ? 'Enviar por e-mail →' : 'Registrar e copiar link')}
              </button>
            </>
          ) : (
            <button className="ci-btn ci-btn--primary" onClick={onClose}>Concluir</button>
          )}
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   WIZARD CONTAINER (panel esquerdo com form, direito com preview)
   ============================================================ */
const CI_WIZ_STEPS = [
  { id: 'modalidade', label: 'Início', sub: 'Modalidade' },
  { id: 'partes', label: 'Partes', sub: 'Contratada' },
  { id: 'objeto', label: 'Objeto', sub: 'Equipamento' },
  { id: 'logistica', label: 'Logística', sub: 'Local & distância' },
  { id: 'pagamento', label: 'Pagamento', sub: 'Valores' },
  { id: 'revisao', label: 'Revisão', sub: 'Anexos & assinatura' },
];

function ciValidateStep(idx, s) {
  const e = {};
  const vazio = (v) => !v || !String(v).trim();
  const obrig = (k, msg) => { if (vazio(s[k])) e[k] = msg; };
  const endereco = (pre) => {
    obrig(pre + 'rua', 'Informe o logradouro.');
    obrig(pre + 'numero', 'Informe o número.');
    if (pre === 'c_') obrig('c_bairro', 'Informe o bairro.'); else obrig('r_bairro', 'Informe o bairro.');
    obrig(pre + 'cidade', 'Informe a cidade.');
    if (!/^[A-Za-z]{2}$/.test((s[pre + 'estado'] || '').trim())) e[pre + 'estado'] = 'UF com 2 letras.';
    if (!window.CI.isCEPValid(s[pre + 'cep'])) e[pre + 'cep'] = 'CEP incompleto (8 dígitos).';
  };
  if (idx === 1) {
    obrig('c_razao', 'Informe a razão social.');
    if (!window.CI.isCNPJValid(s.c_cnpj)) e.c_cnpj = 'CNPJ inválido (confira os dígitos).';
    else if (window.CI.isCNPJContratante(s.c_cnpj)) e.c_cnpj = 'Este é o CNPJ da Vertical Parts — informe o da empresa contratada.';
    endereco('c_');
    obrig('r_nome', 'Informe o responsável.');
    obrig('r_nacionalidade', 'Informe a nacionalidade.');
    obrig('r_estadoCivil', 'Informe o estado civil.');
    obrig('r_profissao', 'Informe a profissão.');
    obrig('r_rg', 'Informe o RG.');
    if (!window.CI.isCPFValid(s.r_cpf)) e.r_cpf = 'CPF inválido (confira os dígitos).';
    if (!s.r_mesmoEndereco) endereco('r_');
  }
  if (idx === 2) {
    if (!window.CI.equipamentosDoContrato(s).length) e.equipamentos = 'Informe ao menos um equipamento (nº de série, nº do projeto ou ativo da Proposta) coberto por este contrato.';
    obrig('descricaoServicos', 'Descreva os serviços a executar.');
    obrig('localServico', 'Informe o endereço onde o serviço será realizado.');
    if (s.equipamento === 'elevador' && s.paradas === 'Personalizado') obrig('paradasCustom', 'Informe a quantidade de paradas.');
    if (window.CI.isRemocao(s)) obrig('destino', 'Informe o destino / local.');
  }
  if (idx === 3 && window.CI.isLongaDistancia(s)) {
    obrig('obraCidade', 'Informe a cidade da obra.');
    if (!/^[A-Za-z]{2}$/.test((s.obraEstado || '').trim())) e.obraEstado = 'UF com 2 letras.';
    if (s.logModo === 'prazo') obrig('logDiasExtra', 'Informe os dias adicionais.');
  }
  if (idx === 4) {
    const total = Math.round(window.CI.moedaParaNumero(s.valorTotal) * 100);
    if (total <= 0) e.valorTotal = 'Informe o valor total.';
    if (s.formaPagamento === 'custom') {
      const parc = s.parcelas || [];
      const valores = parc.map((p) => Math.round(window.CI.moedaParaNumero(p.valor) * 100));
      if (!parc.length) e.parcelas = 'Adicione ao menos uma parcela.';
      else if (valores.some((v) => v <= 0)) e.parcelas = 'Todas as parcelas precisam ter valor maior que zero.';
      else if (total > 0 && valores.reduce((t, v) => t + v, 0) !== total) e.parcelas = 'A soma das parcelas precisa ser igual ao valor total do contrato.';
    }
  }
  if (idx === 5) {
    const d = parseInt(s.dataDia, 10);
    if (!(d >= 1 && d <= 31)) e.dataDia = 'Dia inválido.';
    if (!/^\d{4}$/.test(s.dataAno || '')) e.dataAno = 'Ano com 4 dígitos.';
    obrig('cidadeAssinatura', 'Informe a cidade.');
  }
  return e;
}

function CIWizard({ onCreated, initial }) {
  const [s, setS] = _ciUS(initial || window.CI.defaultState());
  const [step, setStep] = _ciUS(0);
  const [errors, setErrors] = _ciUS({});
  const [sendRec, setSendRec] = _ciUS(null);
  const [creating, setCreating] = _ciUS(false);
  const formScrollRef = _ciUR(null);

  _ciUE(() => { if (formScrollRef.current) formScrollRef.current.scrollTop = 0; }, [step]);

  const set = (k, v) => setS(prev => ({ ...prev, [k]: v }));

  const docPreview = _ciUM(() => window.CI.buildContract(s, s.numeroContrato || 'VPNI________'), [s]);
  const conds = window.CI.activeConditionals(s);

  const goNext = () => {
    const e = ciValidateStep(step, s);
    setErrors(e);
    if (Object.keys(e).length > 0) return;
    if (step < CI_WIZ_STEPS.length - 1) setStep(step + 1);
  };
  const goPrev = () => { setErrors({}); if (step > 0) setStep(step - 1); };
  const goTo = (i) => { setErrors({}); setStep(i); };

  const completeAll = () => {
    let all = {};
    let firstBad = -1;
    [1, 2, 3, 4, 5].forEach(i => {
      const er = ciValidateStep(i, s);
      if (Object.keys(er).length && firstBad < 0) firstBad = i;
      all = { ...all, ...er };
    });
    if (firstBad >= 0) {
      setErrors(all);
      setStep(firstBad);
      return false;
    }
    return true;
  };

  const handleCreateAndSend = async () => {
    if (!completeAll() || creating) return;
    setCreating(true);
    try {
      const rec = await window.CIStore.createDraft(s);
      /* Só abre o modal de envio. Antes chamava onCreated aqui, que troca a
         aba pro Painel e desmontava o wizard (e o modal junto) na hora —
         o usuário nunca via a tela de enviar. onCreated agora roda ao
         fechar o modal (abaixo). */
      setSendRec(rec);
    } catch (e) {
      alert('Erro ao gerar contrato: ' + (e.message || e));
    } finally {
      setCreating(false);
    }
  };

  const StepComp = [CIStepModalidade, CIStepPartes, CIStepObjeto, CIStepLogistica, CIStepPagamento, CIStepRevisao][step];

  return (
    <div className="ci-wiz">
      <nav className="ci-stepbar">
        {CI_WIZ_STEPS.map((st, i) => (
          <button key={st.id} className={'ci-stepbar-item' + (i === step ? ' is-active' : '') + (i < step ? ' is-done' : '')} onClick={() => goTo(i)}>
            <span className="ci-stepbar-num">{i < step ? '✓' : String(i + 1)}</span>
            <span className="ci-stepbar-text"><b>{st.label}</b><small>{st.sub}</small></span>
            {i < CI_WIZ_STEPS.length - 1 && <span className="ci-stepbar-line"></span>}
          </button>
        ))}
      </nav>

      <div className="ci-wiz-body">
        <div className="ci-form-col" ref={formScrollRef}>
          <div className="ci-form-inner">
            <StepComp s={s} set={set} errors={errors} goTo={goTo} />
          </div>
          <div className="ci-form-foot">
            <button className="ci-btn ci-btn--ghost" onClick={goPrev} disabled={step === 0}>← Voltar</button>
            <span className="ci-form-foot-meta">Passo {step + 1} de {CI_WIZ_STEPS.length}</span>
            {step < CI_WIZ_STEPS.length - 1
              ? <button className="ci-btn ci-btn--dark" onClick={goNext}>Avançar →</button>
              : <button className="ci-btn ci-btn--primary" onClick={handleCreateAndSend} disabled={creating}>{creating ? 'Gerando…' : 'Gerar e enviar para assinatura →'}</button>}
          </div>
        </div>
        <div className="ci-preview-col">
          <div className="ci-preview-bar">
            <span className="ci-preview-bar-title">Pré-visualização</span>
            <div className="ci-preview-bar-conds">
              {conds.length === 0
                ? <span className="ci-pv-cond ci-pv-cond--muted">Cláusulas padrão</span>
                : conds.map(c => <span key={c.id} className={'ci-pv-cond ci-pv-cond--' + c.tone}>{c.label}</span>)}
            </div>
          </div>
          <div className="ci-preview-scroll">
            <window.CIContractPreview doc={docPreview} highlightConditional={true} />
          </div>
        </div>
      </div>

      {sendRec && <CISendModal record={sendRec} onClose={() => { const r = sendRec; setSendRec(null); setS(window.CI.defaultState()); setStep(0); onCreated && onCreated(r); }} onSent={() => {}} />}
    </div>
  );
}

/* ============================================================
   DASHBOARD — listagem dos contratos, timeline, auditoria
   ============================================================ */
function CIBadge({ status }) {
  const st = window.CIStore.STATUS[status] || window.CIStore.STATUS.rascunho;
  return <span className={'ci-badge ci-badge--' + st.tone}><span className="ci-badge-dot"></span>{st.label}</span>;
}

/* 29/09 — indicador "respondeu por e-mail" (mesma investigação/correção do
   Contrato de Venda, ver CVEmailRespondidoBadge em contrato-venda.jsx).
   Só usa `referencia_id` (aponta pra ESTE contrato) — de propósito sem
   fallback por numero_cotacao, porque o mesmo numero_cotacao é
   compartilhado por RFQ/Proposta/Contrato de Venda/Contrato Instalador do
   mesmo negócio — um match só por numero_cotacao não sabe dizer se a
   resposta é sobre ESTE contrato ou outro documento da mesma cotação. */
function CIEmailRespondidoBadge({ contratoId }) {
  const [temResposta, setTemResposta] = React.useState(false);
  React.useEffect(() => {
    let cancelado = false;
    setTemResposta(false);
    const sb = window.__VP_SB && window.__VP_SB.sb;
    if (!sb || !contratoId) return;
    sb.from('emails_projeto').select('id', { count: 'exact', head: true })
      .eq('direcao', 'entrada').is('excluido_em', null).eq('referencia_id', String(contratoId))
      .then(({ count }) => { if (!cancelado) setTemResposta((count || 0) > 0); })
      .catch(() => { if (!cancelado) setTemResposta(false); });
    return () => { cancelado = true; };
  }, [contratoId]);
  if (!temResposta) return null;
  return (
    <span className="ci-badge ci-badge--blue" style={{ marginLeft: 6 }} title="Existe e-mail recebido vinculado a este contrato (Message-ID)">
      <span className="ci-badge-dot"></span>Respondeu por e-mail
    </span>
  );
}

const CI_TL_SEQ = ['rascunho', 'enviado', 'visualizado', 'assinado'];

function CITimeline({ rec }) {
  const byStatus = {};
  (rec.log || []).forEach(l => { byStatus[l.status] = l; });
  const terminal = (rec.status === 'expirado' || rec.status === 'recusado') ? rec.status : null;
  const curIdx = CI_TL_SEQ.indexOf(rec.status);
  const steps = CI_TL_SEQ.map((sid, i) => {
    const st = window.CIStore.STATUS[sid];
    const entry = byStatus[sid];
    let cls = 'pending';
    if (entry) cls = (i === curIdx && !terminal) ? 'current' : 'done';
    if (terminal && i <= curIdx) cls = 'done';
    return { st, entry, cls };
  });
  if (terminal) {
    const st = window.CIStore.STATUS[terminal];
    steps.push({ st, entry: byStatus[terminal], cls: 'current' });
  }
  return (
    <div className="ci-timeline">
      {steps.map((s, i) => (
        <div key={i} className={'ci-tl-step ' + s.cls}>
          <div className="ci-tl-rail">
            <div className="ci-tl-dot">{s.entry ? s.st.icon : (i + 1)}</div>
            <div className="ci-tl-line"></div>
          </div>
          <div className="ci-tl-body">
            <div className="ci-tl-title">{s.st.label}</div>
            <div className="ci-tl-meta">
              {s.entry ? window.CIStore.fmtDateTime(s.entry.at) : 'Pendente'}
              {s.entry && s.entry.meta && s.entry.meta.channel ? ' · ' + (s.entry.meta.channel === 'whatsapp' ? 'WhatsApp' : 'E-mail') : ''}
              {s.entry && s.entry.meta && s.entry.meta.ip ? ' · IP ' + s.entry.meta.ip : ''}
              {/* 01/10 — recusa agora traz quem recusou e o motivo (mesmo
                 achado real do Contrato de Venda, cotação 955/AKAI) */}
              {s.entry && s.entry.meta && s.entry.meta.nome ? ' · Por ' + s.entry.meta.nome : ''}
              {s.entry && s.entry.meta && s.entry.meta.motivo ? <div>Motivo: {s.entry.meta.motivo}</div> : ''}
            </div>
          </div>
        </div>
      ))}
    </div>
  );
}

function CIAuditRow({ k, v }) {
  return <div className="ci-audit-row"><span className="k">{k}</span><span className="v">{v || '—'}</span></div>;
}

function CIAuditDrawer({ rec, onClose, onResend, onRefresh }) {
  const a = rec.audit || {};
  const del = async () => {
    if (!window.confirm('Excluir este rascunho? Esta ação não pode ser desfeita.')) return;
    try {
      await window.CIStore.remove(rec.id);
    } catch (e) {
      alert('Não foi possível excluir: ' + (e.message || e));
      return;
    }
    onClose();
    onRefresh();
  };
  const valorFmt = rec.valor_total ? 'R$ ' + window.CI.fmtMoeda(rec.valor_total) : '—';
  const signUrl = window.CIStore.signUrl(rec.token);
  return (
    <div className="ci-drawer-backdrop" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="ci-drawer">
        <div className="ci-drawer-head">
          <div>
            <h2>{rec.numero_documento}</h2>
            <div className="ci-drawer-co">{rec.contratada_nome}</div>
            <div style={{ marginTop: 10 }}><CIBadge status={rec.status} /><CIEmailRespondidoBadge contratoId={rec.id}/></div>
          </div>
          <button className="ci-drawer-x" onClick={onClose}>✕</button>
        </div>
        <div className="ci-drawer-body">
          <div className="ci-drawer-sec">
            <h3 className="ci-drawer-sec-title">Linha do tempo</h3>
            <CITimeline rec={rec} />
          </div>
          <div className="ci-drawer-sec">
            <h3 className="ci-drawer-sec-title">Trilha de auditoria</h3>
            <div className="ci-audit">
              <CIAuditRow k="Responsável" v={rec.responsavel_nome} />
              <CIAuditRow k="Objeto" v={rec.objeto_resumo} />
              <CIAuditRow k="Valor" v={valorFmt} />
              <CIAuditRow k="Enviado em" v={window.CIStore.fmtDateTime(rec.sent_at)} />
              <CIAuditRow k="Canal" v={rec.channel === 'whatsapp' ? 'WhatsApp' : rec.channel === 'email' ? 'E-mail' : rec.channel === 'link' ? 'Link copiado' : '—'} />
              <CIAuditRow k="Destinatário" v={rec.recipient && rec.recipient.contact} />
              <CIAuditRow k="Validade do link" v={rec.expires_at ? window.CIStore.fmtDate(rec.expires_at) : '—'} />
              {a.viewedAt && <CIAuditRow k="Visualizado em" v={window.CIStore.fmtDateTime(a.viewedAt)} />}
              {a.viewedAt && <CIAuditRow k="IP (visualização)" v={a.viewIp || 'não capturado'} />}
              {a.viewedAt && <CIAuditRow k="Dispositivo (visualização)" v={a.viewDevice} />}
              {a.signedAt && <CIAuditRow k="Assinado em" v={window.CIStore.fmtDateTime(a.signedAt)} />}
              {a.signedAt && <CIAuditRow k="Signatário" v={a.signerName} />}
              {a.signedAt && <CIAuditRow k="Tipo de assinatura" v={a.signatureType === 'draw' ? 'Desenhada' : 'Digitada'} />}
              {a.signedAt && <CIAuditRow k="IP (assinatura)" v={a.signIp || 'não capturado'} />}
              {a.signedAt && <CIAuditRow k="Dispositivo (assinatura)" v={a.signDevice} />}
              {a.hash && <CIAuditRow k="Hash SHA-256" v={a.hash} />}
            </div>
            {a.signedAt && (
              <div className="ci-legal-note">
                Documento assinado eletronicamente nos termos da MP 2.200-2/2001 e da Lei 14.063/2020.
                A integridade é garantida pelo hash SHA-256 acima.
              </div>
            )}
          </div>
          <div className="ci-drawer-sec">
            <h3 className="ci-drawer-sec-title">Ações</h3>
            <div className="ci-drawer-actions">
              <a className="ci-btn ci-btn--ghost" href={signUrl} target="_blank" rel="noopener">Abrir link de assinatura ↗</a>
              {(rec.status === 'enviado' || rec.status === 'visualizado' || rec.status === 'expirado' || rec.status === 'rascunho') &&
                <button className="ci-btn ci-btn--primary" onClick={() => onResend(rec)}>{rec.status === 'rascunho' ? 'Enviar' : 'Reenviar link'}</button>}
              {rec.status === 'rascunho' && <button className="ci-btn ci-btn--danger" onClick={del}>Excluir rascunho</button>}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

function CIDashboard() {
  const [contracts, setContracts] = _ciUS([]);
  const [loading, setLoading] = _ciUS(true);
  const [filter, setFilter] = _ciUS('todos');
  const [query, setQuery] = _ciUS('');
  const [drawerId, setDrawerId] = _ciUS(null);
  const [sendRec, setSendRec] = _ciUS(null);

  const refresh = async () => {
    setLoading(true);
    const list = await window.CIStore.listPainel();
    setContracts(list);
    setLoading(false);
  };

  _ciUE(() => {
    window.CIStore.sweepExpired().then(refresh);
    const t = setInterval(refresh, 15000);
    return () => clearInterval(t);
  }, []);

  const drawerRec = drawerId ? contracts.find(c => c.id === drawerId) : null;

  const counts = _ciUM(() => {
    const c = { rascunho: 0, enviado: 0, visualizado: 0, assinado: 0 };
    contracts.forEach(x => { if (x.status === 'recusado' || x.status === 'expirado') return; c[x.status] = (c[x.status] || 0) + 1; });
    return c;
  }, [contracts]);

  const filtered = contracts.filter(c => {
    if (filter !== 'todos' && c.status !== filter) return false;
    if (query) {
      const q = query.toLowerCase();
      return ((c.numero_documento || '') + ' ' + (c.contratada_nome || '') + ' ' + (c.responsavel_nome || '')).toLowerCase().includes(q);
    }
    return true;
  });

  const statCards = [
    { id: 'rascunho', tone: 'gray' },
    { id: 'enviado', tone: 'blue' },
    { id: 'visualizado', tone: 'yellow' },
    { id: 'assinado', tone: 'green' },
  ];

  const lastActivity = (c) => {
    const l = (c.log || [])[c.log.length - 1];
    return l ? window.CIStore.relative(l.at) : '';
  };

  return (
    <div className="ci-dash">
      <div className="ci-dash-stats">
        <div className={'ci-stat' + (filter === 'todos' ? ' active' : '')} onClick={() => setFilter('todos')}>
          <div className="ci-stat-top"><span className="ci-stat-num">{contracts.length}</span></div>
          <div className="ci-stat-label">Total</div>
        </div>
        {statCards.map(sc => (
          <div key={sc.id} className={'ci-stat ci-stat--' + sc.tone + (filter === sc.id ? ' active' : '')} onClick={() => setFilter(filter === sc.id ? 'todos' : sc.id)}>
            <div className="ci-stat-top">
              <span className="ci-stat-dot"></span>
              <span className="ci-stat-num">{counts[sc.id] || 0}</span>
            </div>
            <div className="ci-stat-label">{window.CIStore.STATUS[sc.id].label}</div>
          </div>
        ))}
      </div>

      <div className="ci-panel">
        <div className="ci-panel-head">
          <h2>{filter === 'todos' ? 'Todos os contratos' : window.CIStore.STATUS[filter].label}</h2>
          <div className="ci-panel-search">
            <input placeholder="Buscar nº, empresa, responsável…" value={query} onChange={(e) => setQuery(e.target.value)} />
          </div>
        </div>
        {loading && contracts.length === 0 ? (
          <div className="ci-empty">Carregando…</div>
        ) : filtered.length === 0 ? (
          <div className="ci-empty">Nenhum contrato {filter !== 'todos' ? 'com este status' : ''}. Crie um novo no gerador.</div>
        ) : (
          <table className="ci-table">
            <thead>
              <tr>
                <th>Contrato</th><th>Objeto</th><th>Valor</th><th>Status</th><th>Atividade</th><th style={{ textAlign: 'right' }}>Ações</th>
              </tr>
            </thead>
            <tbody>
              {filtered.map(c => (
                <tr key={c.id} onClick={() => setDrawerId(c.id)}>
                  <td><div className="ci-cell-co">{c.contratada_nome}</div><div className="ci-cell-num">{c.numero_documento}</div></td>
                  <td><div className="ci-cell-obj">{c.objeto_resumo}</div><div className="ci-cell-resp">{c.responsavel_nome || '—'}</div></td>
                  <td className="ci-cell-val">{c.valor_total ? 'R$ ' + window.CI.fmtMoeda(c.valor_total) : '—'}</td>
                  <td><CIBadge status={c.status} /></td>
                  <td className="ci-cell-time">{lastActivity(c)}</td>
                  <td onClick={(e) => e.stopPropagation()}>
                    <div className="ci-cell-actions">
                      <button className="ci-icon-btn" title="Auditoria" onClick={() => setDrawerId(c.id)}>👁</button>
                      <a className="ci-icon-btn" title="Abrir assinatura" href={window.CIStore.signUrl(c.token)} target="_blank" rel="noopener">🔗</a>
                      {(c.status === 'enviado' || c.status === 'visualizado' || c.status === 'rascunho' || c.status === 'expirado') &&
                        <button className="ci-mini-btn" onClick={() => setSendRec(c)}>{c.status === 'rascunho' ? 'Enviar' : 'Reenviar'}</button>}
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </div>

      {drawerRec && <CIAuditDrawer rec={drawerRec} onClose={() => setDrawerId(null)} onResend={(r) => setSendRec(r)} onRefresh={refresh} />}
      {sendRec && <CISendModal record={sendRec} onClose={() => setSendRec(null)} onSent={refresh} />}
    </div>
  );
}

/* ============================================================
   PAGE — abas Wizard (novo) / Dashboard (painel)
   ============================================================ */
function ContratoInstaladorPage() {
  const [tab, setTab] = _ciUS('painel');
  return (
    <EnterprisePageBase
      module="JURÍDICO"
      page="Contrato Instalador"
      title="CONTRATO INSTALADOR"
      subtitle="Contrato de prestação de serviços para terceiros / montadores · assinatura digital com auditoria"
      actions={
        <div className="ci-page-actions">
          <button className={'ci-tab' + (tab === 'painel' ? ' on' : '')} onClick={() => setTab('painel')}>▦ Painel</button>
          <button className={'ci-tab' + (tab === 'novo' ? ' on' : '')} onClick={() => setTab('novo')}>+ Novo contrato</button>
        </div>
      }
    >
      {tab === 'painel' ? <CIDashboard /> : <CIWizard onCreated={() => setTab('painel')} />}
    </EnterprisePageBase>
  );
}

/* Fallback: se o host não definiu EnterprisePageBase, usa wrapper simples */
function EnterprisePageBase(props) {
  if (window.EnterprisePageBase && window.EnterprisePageBase !== EnterprisePageBase) {
    return window.EnterprisePageBase(props);
  }
  return (
    <div className="ci-page">
      <div className="ci-page-head">
        <div className="ci-page-titles">
          <div className="ci-page-kicker">{props.module} · {props.page}</div>
          <h1 className="ci-page-title">{props.title}</h1>
          {props.subtitle && <p className="ci-page-sub">{props.subtitle}</p>}
        </div>
        {props.actions ? <div className="ci-page-actions-wrap">{props.actions}</div> : null}
      </div>
      <div className="ci-page-body">{props.children}</div>
    </div>
  );
}

window.ContratoInstaladorPage = ContratoInstaladorPage;
