// ===== Elejemais — Inteligência Territorial =====

const { useState: useState_in, useEffect: useEffect_in, useRef: useRef_in } = React;

const PARTIDOS = [
  'PT','PL','MDB','PP','PSD','Republicanos','União Brasil','PDT','PSDB','PSB',
  'Solidariedade','Avante','PCdoB','Novo','Cidadania','PSOL','PODE','PRD',
  'PSC','PV','Agir','DC','PMN','PMB','UP','PCO','PSTU','PCB','PRB',
];

const CARGOS_BY_TYPE = {
  proporcional: ['Vereador', 'Dep. Estadual', 'Dep. Federal'],
  majoritario:  ['Prefeito', 'Governador', 'Senador', 'Presidente'],
};

// ── Mapeamento de cargos TSE → formato interno ────────────────────────────
const TSE_CARGO_NORM = {
  'VEREADOR':          { cargo: 'Vereador',      cargo_type: 'proporcional' },
  'VEREADORA':         { cargo: 'Vereador',      cargo_type: 'proporcional' },
  'DEPUTADO ESTADUAL': { cargo: 'Dep. Estadual', cargo_type: 'proporcional' },
  'DEPUTADA ESTADUAL': { cargo: 'Dep. Estadual', cargo_type: 'proporcional' },
  'DEPUTADO FEDERAL':  { cargo: 'Dep. Federal',  cargo_type: 'proporcional' },
  'DEPUTADA FEDERAL':  { cargo: 'Dep. Federal',  cargo_type: 'proporcional' },
  'SENADOR':           { cargo: 'Senador',        cargo_type: 'majoritario'  },
  'SENADORA':          { cargo: 'Senador',        cargo_type: 'majoritario'  },
  'PREFEITO':          { cargo: 'Prefeito',       cargo_type: 'majoritario'  },
  'PREFEITA':          { cargo: 'Prefeito',       cargo_type: 'majoritario'  },
  'GOVERNADOR':        { cargo: 'Governador',     cargo_type: 'majoritario'  },
  'GOVERNADORA':       { cargo: 'Governador',     cargo_type: 'majoritario'  },
  'PRESIDENTE':        { cargo: 'Presidente',     cargo_type: 'majoritario'  },
  'PRESIDENTA':        { cargo: 'Presidente',     cargo_type: 'majoritario'  },
};

function normMunName(s) {
  return (s || '')
    .toUpperCase()
    .normalize('NFD')
    .replace(/[̀-ͯ]/g, '')      // strip diacritics
    .replace(/[‘’'ʼ`]/g, ' ') // apostrophe → space
    .replace(/\s+/g, ' ')
    .trim();
}

function isTSEFormat(headers) {
  const h = new Set(headers);
  return h.has('ano_eleicao') || (h.has('cd_municipio') && h.has('sg_partido'));
}

// ── Classificador de diagnóstico ───────────────────────────────────────────
function diagnose(avgVotos, meta, apoiadores, cargoType) {
  if (avgVotos === 0 && apoiadores === 0) return null;
  const ALTO     = cargoType === 'majoritario' ? 2000 : 400;
  const isAlto   = avgVotos >= ALTO;
  const metaFill = meta > 0 ? apoiadores / meta : 0;
  const metaRatio = avgVotos > 0 && meta > 0 ? meta / avgVotos : 0;
  if (isAlto && metaFill >= 0.70)             return 'CONSOLIDADO';
  if (!isAlto && metaRatio >= 3.0)            return 'RISCO';
  if (isAlto && metaFill < 0.25)              return 'MINA';
  return 'ANDAMENTO';
}

const DIAG = {
  CONSOLIDADO: {
    emoji: '🟩', label: 'Consolidado',
    color: 'oklch(0.44 0.11 155)', bg: 'oklch(0.96 0.03 155)',
    desc: 'Reduto histórico forte com boa mobilização de lideranças. Manter o ritmo e garantir a entrega até o dia da eleição.',
  },
  RISCO: {
    emoji: '🟨', label: 'Risco Eleitoral',
    color: 'oklch(0.55 0.16 75)', bg: 'oklch(0.97 0.04 75)',
    desc: 'Meta muito acima da média histórica neste município. Revisar a meta ou investigar possível inconsistência no cadastro de apoiadores.',
  },
  MINA: {
    emoji: '🟦', label: 'Mina de Ouro',
    color: 'oklch(0.46 0.13 250)', bg: 'oklch(0.96 0.03 250)',
    desc: 'Reduto com histórico favorável, mas sem mobilização suficiente no campo. Alta oportunidade — fortalecer lideranças locais é urgente.',
  },
  ANDAMENTO: {
    emoji: '⬜', label: 'Em Andamento',
    color: 'var(--muted)', bg: 'var(--surface-2)',
    desc: 'Perfil em formação. Continue cadastrando apoiadores e lideranças para gerar um diagnóstico mais preciso.',
  },
};

// ── Gráfico de linha (histórico temporal) ─────────────────────────────────
function HistoricoChart({ data, meta }) {
  const W = 560, H = 210;
  const pad = { t: 24, r: 48, b: 38, l: 64 };
  const iw = W - pad.l - pad.r;
  const ih = H - pad.t - pad.b;

  if (!data || data.length === 0) return null;

  const maxV = Math.max(meta || 0, ...data.map(d => d.votes_nominal)) * 1.18 || 100;
  const xOf  = (i) => pad.l + (data.length > 1 ? (i / (data.length - 1)) * iw : iw / 2);
  const yOf  = (v) => pad.t + ih - Math.max(0, (v / maxV)) * ih;
  const TICKS = 5;

  const linePath = data.map((d, i) => `${i === 0 ? 'M' : 'L'} ${xOf(i).toFixed(1)} ${yOf(d.votes_nominal).toFixed(1)}`).join(' ');
  const areaPath = data.length > 1
    ? linePath + ` L ${xOf(data.length - 1).toFixed(1)} ${(pad.t + ih).toFixed(1)} L ${xOf(0).toFixed(1)} ${(pad.t + ih).toFixed(1)} Z`
    : '';

  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ overflow: 'visible' }}>
      {/* Grid lines + Y labels */}
      {Array.from({ length: TICKS + 1 }).map((_, i) => {
        const v = (maxV / TICKS) * i;
        const yp = yOf(v);
        return (
          <g key={i}>
            <line x1={pad.l} y1={yp} x2={W - pad.r} y2={yp}
              stroke="var(--line)" strokeWidth={i === 0 ? 1.2 : 0.6} strokeDasharray={i > 0 ? "3 4" : ""} />
            <text x={pad.l - 8} y={yp + 4} textAnchor="end" fontSize={10} fill="var(--muted)">
              {window.fmt(Math.round(v))}
            </text>
          </g>
        );
      })}

      {/* Meta line (pontilhada) */}
      {meta > 0 && meta <= maxV && (
        <g>
          <line x1={pad.l} y1={yOf(meta)} x2={W - pad.r} y2={yOf(meta)}
            stroke="oklch(0.62 0.16 40)" strokeDasharray="6 3" strokeWidth={1.8} />
          <text x={W - pad.r + 6} y={yOf(meta) + 4} fontSize={10} fontWeight="600"
            fill="oklch(0.62 0.16 40)">Meta</text>
        </g>
      )}

      {/* Area fill */}
      {areaPath && <path d={areaPath} fill="var(--accent)" opacity={0.07} />}

      {/* Data line */}
      <path d={linePath} fill="none" stroke="var(--accent)" strokeWidth={2.4}
        strokeLinejoin="round" strokeLinecap="round" />

      {/* Pontos + labels */}
      {data.map((d, i) => (
        <g key={i}>
          <circle cx={xOf(i)} cy={yOf(d.votes_nominal)} r={5.5} fill="var(--accent)" opacity={0.18} />
          <circle cx={xOf(i)} cy={yOf(d.votes_nominal)} r={3.5} fill="var(--accent)" />
          <text x={xOf(i)} y={yOf(d.votes_nominal) - 12} textAnchor="middle"
            fontSize={11} fontWeight="700" fill="var(--ink)">
            {window.fmt(d.votes_nominal)}
          </text>
          <text x={xOf(i)} y={H - 4} textAnchor="middle" fontSize={11} fill="var(--muted)">
            {d.year}
          </text>
        </g>
      ))}
    </svg>
  );
}

// ── Gráfico comparativo (barras horizontais) ──────────────────────────────
function ComparativoChart({ media, meta, apoiadores }) {
  const items = [
    { label: 'Média histórica',   value: media,      color: 'oklch(0.55 0.14 250)' },
    { label: 'Meta de votos',     value: meta,        color: 'oklch(0.62 0.16 40)'  },
    { label: 'Apoiadores campo',  value: apoiadores,  color: 'var(--accent)'         },
  ];
  const maxV = Math.max(...items.map(i => i.value), 1);
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
      {items.map((item, idx) => (
        <div key={idx}>
          <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6, alignItems: 'center' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
              <span style={{ width: 10, height: 10, borderRadius: '50%', background: item.color, display: 'inline-block' }} />
              <span className="dim" style={{ fontSize: 12.5 }}>{item.label}</span>
            </div>
            <strong className="mono" style={{ fontSize: 14 }}>{window.fmt(item.value)}</strong>
          </div>
          <div style={{ height: 26, background: 'var(--surface-2)', borderRadius: 999, overflow: 'hidden' }}>
            <div style={{
              width: `${Math.max(1.5, (item.value / maxV) * 100)}%`,
              height: '100%', background: item.color, borderRadius: 999,
              transition: 'width 700ms cubic-bezier(0.4,0,0.2,1)',
            }} />
          </div>
        </div>
      ))}
    </div>
  );
}

// ── Regressão linear simples ──────────────────────────────────────────────
function linearRegression(points) {
  var n = points.length;
  if (n < 2) return null;
  var xs = points.map(function (d) { return d.year; });
  var ys = points.map(function (d) { return d.votes_nominal; });
  var xm = xs.reduce(function (a, b) { return a + b; }, 0) / n;
  var ym = ys.reduce(function (a, b) { return a + b; }, 0) / n;
  var num = xs.reduce(function (acc, x, i) { return acc + (x - xm) * (ys[i] - ym); }, 0);
  var den = xs.reduce(function (acc, x) { return acc + Math.pow(x - xm, 2); }, 0);
  if (den === 0) return null;
  var slope = num / den;
  var intercept = ym - slope * xm;
  var ssTot = ys.reduce(function (acc, y) { return acc + Math.pow(y - ym, 2); }, 0);
  var ssRes = ys.reduce(function (acc, y, i) { return acc + Math.pow(y - (slope * xs[i] + intercept), 2); }, 0);
  var r2 = ssTot > 0 ? 1 - ssRes / ssTot : 0;
  return { slope: slope, intercept: intercept, r2: r2 };
}

// ── Alerta de eleição próxima ─────────────────────────────────────────────
function AlertaBanner({ campaign }) {
  if (!campaign || !campaign.eleicaoDataRaw) return null;
  var today   = new Date();
  var eleicao = new Date(campaign.eleicaoDataRaw);
  var days    = Math.floor((eleicao - today) / (1000 * 60 * 60 * 24));
  if (days < 0 || days > 90) return null;
  var bg      = days <= 30 ? 'oklch(0.97 0.03 30)' : days <= 60 ? 'oklch(0.97 0.04 75)' : 'oklch(0.96 0.03 250)';
  var border  = days <= 30 ? 'oklch(0.55 0.18 30)' : days <= 60 ? 'oklch(0.62 0.16 40)' : 'oklch(0.55 0.14 250)';
  var ink     = days <= 30 ? 'oklch(0.42 0.18 30)' : days <= 60 ? 'oklch(0.48 0.16 40)' : 'oklch(0.40 0.14 250)';
  var urgency = days <= 30 ? 'Atenção máxima' : days <= 60 ? 'Fase crítica' : 'Alerta preventivo';
  var dica    = days <= 30
    ? 'Acione lideranças com baixo desempenho e maximize o cadastro de apoiadores.'
    : days <= 60
    ? 'Revise municípios com baixa cobertura e priorize regiões com alta força histórica.'
    : 'Boa janela para estratégia territorial — analise os municípios de alta oportunidade.';
  return (
    <div style={{
      padding: '12px 18px', borderRadius: 10, marginBottom: 16,
      background: bg, border: '1.5px solid ' + border + '60',
      display: 'flex', alignItems: 'center', gap: 14,
    }}>
      <div style={{ fontSize: 26, lineHeight: 1, flexShrink: 0 }}>
        {days <= 30 ? '🔴' : days <= 60 ? '🟡' : '🔵'}
      </div>
      <div style={{ flex: 1 }}>
        <div style={{ fontWeight: 700, fontSize: 13, color: ink }}>
          {urgency} — {days} dias até a eleição de {campaign.candidato}
        </div>
        <div style={{ fontSize: 12, color: ink, opacity: 0.8, marginTop: 2 }}>
          {campaign.dataEleicao && campaign.dataEleicao + ' · '}{dica}
        </div>
      </div>
    </div>
  );
}

// ── Card: Projeção de resultado (regressão linear) ────────────────────────
function ProjecaoCard({ historico, meta, cargo, nextYear }) {
  if (!historico || historico.length < 2) return null;
  var reg = linearRegression(historico);
  if (!reg) return null;
  var ny        = nextYear || (Math.max.apply(null, historico.map(function (d) { return d.year; })) + 4);
  var projected = Math.max(0, Math.round(reg.slope * ny + reg.intercept));
  var growthPct = historico[0].votes_nominal > 0
    ? Math.round((reg.slope * 4 / historico[0].votes_nominal) * 100) : 0;
  var trend = reg.slope > 50 ? 'Crescente' : reg.slope < -50 ? 'Decrescente' : 'Estável';
  var trendColor = reg.slope > 50 ? 'var(--accent)' : reg.slope < -50 ? 'oklch(0.55 0.18 30)' : 'oklch(0.62 0.16 40)';
  var trendArrow = reg.slope > 50 ? '↑' : reg.slope < -50 ? '↓' : '→';
  var confidence = reg.r2 > 0.7 ? 'Alta' : reg.r2 > 0.4 ? 'Média' : 'Baixa';
  var confColor  = reg.r2 > 0.7 ? 'var(--accent)' : reg.r2 > 0.4 ? 'oklch(0.62 0.16 40)' : 'oklch(0.55 0.18 30)';
  var metaGap    = meta > 0 ? projected - meta : 0;
  var alcancavel = meta > 0 && projected >= meta * 0.85;
  return (
    <div className="card" style={{ padding: '20px 24px' }}>
      <div className="eyebrow">Projeção de resultado</div>
      <h3 className="serif" style={{ fontSize: 20, margin: '4px 0 18px', letterSpacing: '-0.01em' }}>
        {cargo || 'Cargo'} · eleição {ny}
      </h3>
      <div style={{ display: 'grid', gridTemplateColumns: meta > 0 ? '1fr 1fr 1fr' : '1fr 1fr', gap: 14, marginBottom: 16 }}>
        <div style={{ textAlign: 'center', padding: '16px 10px', background: 'var(--surface-2)', borderRadius: 10 }}>
          <div className="dim" style={{ fontSize: 11, marginBottom: 5 }}>Projeção {ny}</div>
          <div className="serif" style={{ fontSize: 34, color: trendColor, lineHeight: 1 }}>{window.fmt(projected)}</div>
          <div style={{ fontSize: 11, color: trendColor, marginTop: 4, fontWeight: 600 }}>
            {trendArrow} {trend} · {growthPct > 0 ? '+' : ''}{growthPct}% / ciclo
          </div>
        </div>
        <div style={{ textAlign: 'center', padding: '16px 10px', background: 'var(--surface-2)', borderRadius: 10 }}>
          <div className="dim" style={{ fontSize: 11, marginBottom: 5 }}>Confiança</div>
          <div className="serif" style={{ fontSize: 34, color: confColor, lineHeight: 1 }}>{confidence}</div>
          <div className="mono dim" style={{ fontSize: 11, marginTop: 4 }}>R² = {reg.r2.toFixed(2)}</div>
        </div>
        {meta > 0 && (
          <div style={{
            textAlign: 'center', padding: '16px 10px', borderRadius: 10,
            background: alcancavel ? 'oklch(0.96 0.03 155)' : 'oklch(0.97 0.03 30)',
            border: '1.5px solid ' + (alcancavel ? 'oklch(0.44 0.11 155)30' : 'oklch(0.55 0.18 30)30'),
          }}>
            <div className="dim" style={{ fontSize: 11, marginBottom: 5 }}>vs. Meta</div>
            <div className="serif" style={{ fontSize: 24, color: alcancavel ? 'var(--accent)' : 'oklch(0.55 0.18 30)', lineHeight: 1 }}>
              {alcancavel ? 'Alcançável' : 'Em risco'}
            </div>
            <div style={{ fontSize: 12, marginTop: 4, fontWeight: 600, color: metaGap >= 0 ? 'var(--accent)' : 'oklch(0.55 0.18 30)' }}>
              {metaGap >= 0 ? '+' : ''}{window.fmt(metaGap)} votos
            </div>
          </div>
        )}
      </div>
      <div style={{ fontSize: 12, color: 'var(--muted)', lineHeight: 1.6 }}>
        Regressão linear sobre {historico.length} eleições.
        {reg.r2 < 0.4 && ' Tendência irregular — interprete com cautela.'}
      </div>
    </div>
  );
}

// ── Card: Comparativo entre partidos ─────────────────────────────────────
function ComparativoPartidosCard({ ibgeCode, cargoType, cargo, myParty, municipioName }) {
  const [data,    setData]    = useState_in(null);
  const [loading, setLoading] = useState_in(false);
  useEffect_in(function () {
    if (!ibgeCode) return;
    setLoading(true);
    window.API.getPartiesForMunicipality(ibgeCode, cargoType, cargo)
      .then(setData)
      .catch(console.error)
      .finally(function () { setLoading(false); });
  }, [ibgeCode, cargoType, cargo]);

  if (loading) return (
    <div className="card" style={{ padding: '32px 24px', textAlign: 'center' }}>
      <div className="dim" style={{ fontSize: 13 }}>Carregando comparativo de partidos…</div>
    </div>
  );
  if (!data || data.length === 0) return null;

  var top7   = data.slice(0, 7);
  var maxAvg = Math.max.apply(null, top7.map(function (d) { return d.avg; }));
  var myUpper = (myParty || '').toUpperCase().trim();

  return (
    <div className="card" style={{ padding: '20px 24px' }}>
      <div className="eyebrow">Comparativo entre partidos</div>
      <h3 className="serif" style={{ fontSize: 20, margin: '4px 0 20px', letterSpacing: '-0.01em' }}>
        {cargo || cargoType} · {municipioName}
      </h3>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
        {top7.map(function (p, idx) {
          var isMe  = p.party === myUpper;
          var pct   = maxAvg > 0 ? (p.avg / maxAvg) * 100 : 0;
          var color = isMe ? 'var(--accent)' : 'oklch(0.55 0.14 250)';
          return (
            <div key={p.party}>
              <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4, alignItems: 'center' }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
                  <span style={{ fontSize: 11, color: 'var(--muted)', width: 16, textAlign: 'right' }}>#{idx + 1}</span>
                  <strong style={{ fontSize: 13, color: isMe ? 'var(--accent)' : 'var(--ink)' }}>{p.party}</strong>
                  {isMe && (
                    <span style={{ fontSize: 10, background: 'var(--accent)', color: 'var(--accent-ink)', padding: '1px 7px', borderRadius: 999, fontWeight: 600 }}>
                      sua campanha
                    </span>
                  )}
                </div>
                <div style={{ display: 'flex', gap: 14, alignItems: 'center' }}>
                  <span className="mono dim" style={{ fontSize: 11 }}>{p.count} eleições</span>
                  <strong className="mono" style={{ fontSize: 14, color: color }}>{window.fmt(p.avg)}</strong>
                </div>
              </div>
              <div style={{ height: 20, background: 'var(--surface-2)', borderRadius: 999, overflow: 'hidden' }}>
                <div style={{
                  width: pct + '%', height: '100%', borderRadius: 999,
                  background: color, opacity: isMe ? 1 : 0.55,
                  transition: 'width 700ms cubic-bezier(0.4,0,0.2,1)',
                }} />
              </div>
            </div>
          );
        })}
      </div>
      <div style={{ fontSize: 11.5, color: 'var(--muted)', marginTop: 12 }}>
        Média de votos por eleição · fonte: TSE importado
      </div>
    </div>
  );
}

// ── Painel: Ranking estadual (Mapa de força territorial) ──────────────────
function RankingEstadualCard({ campaign, partido, cargoType, cargo }) {
  const [ranking, setRanking]   = useState_in([]);
  const [loading, setLoading]   = useState_in(false);
  const [loaded,  setLoaded]    = useState_in(false);

  useEffect_in(function () {
    if (!partido.trim()) { setRanking([]); setLoaded(false); return; }
    setLoading(true);
    Promise.all([
      window.API.getMunicipalities(campaign.uf),
      window.API.getElectoralHistoryAll(partido, cargoType, cargo),
      window.API.getMunicipalityStats(campaign.id),
    ])
    .then(function (results) {
      var muns  = results[0];
      var hist  = results[1];
      var stats = results[2];

      var ibgeToMun = {};
      muns.forEach(function (m) { if (m.ibge_code) ibgeToMun[m.ibge_code] = m; });

      var ibgeToStats = {};
      stats.forEach(function (s) { if (s.ibge_code) ibgeToStats[s.ibge_code] = { apoio: s.apoio, lider: s.lider }; });

      var byIbge = {};
      hist.forEach(function (h) {
        if (!h.ibge_code || !ibgeToMun[h.ibge_code]) return;
        if (!byIbge[h.ibge_code]) byIbge[h.ibge_code] = { total: 0, count: 0, lastVotes: 0, lastYear: 0 };
        byIbge[h.ibge_code].total += h.votes_nominal;
        byIbge[h.ibge_code].count += 1;
        if (h.year > byIbge[h.ibge_code].lastYear) {
          byIbge[h.ibge_code].lastYear  = h.year;
          byIbge[h.ibge_code].lastVotes = h.votes_nominal;
        }
      });

      var rows = Object.keys(byIbge).map(function (ibge) {
        var agg  = byIbge[ibge];
        var mun  = ibgeToMun[ibge];
        var st   = ibgeToStats[ibge] || { apoio: 0, lider: 0 };
        var avg  = Math.round(agg.total / agg.count);
        return {
          ibge_code:   ibge,
          name:        mun.name,
          avg:         avg,
          lastVotes:   agg.lastVotes,
          count:       agg.count,
          apoio:       st.apoio,
          lider:       st.lider,
          oportunidade: avg > 0 ? avg / (st.apoio + 1) : 0,
        };
      }).sort(function (a, b) { return b.avg - a.avg; });

      setRanking(rows);
      setLoaded(true);
    })
    .catch(console.error)
    .finally(function () { setLoading(false); });
  }, [campaign.id, campaign.uf, partido, cargoType, cargo]);

  if (!partido.trim()) return (
    <div className="card" style={{ padding: '48px 32px', textAlign: 'center' }}>
      <div style={{ color: 'var(--muted)', marginBottom: 14 }}><Icon.Target size={36} /></div>
      <div className="dim">Selecione um partido para ver o ranking de força territorial.</div>
    </div>
  );

  if (loading) return (
    <div style={{ textAlign: 'center', padding: '56px 0' }} className="dim">
      Carregando força territorial de {partido}…
    </div>
  );

  if (loaded && ranking.length === 0) return (
    <div className="card" style={{ padding: '48px 32px', textAlign: 'center' }}>
      <div className="dim" style={{ marginBottom: 10 }}>
        Nenhum dado histórico encontrado para <strong>{partido}</strong> no estado {campaign.uf}.
      </div>
      <div className="callout" style={{ fontSize: 12, textAlign: 'left', maxWidth: 420, margin: '14px auto 0' }}>
        Importe o arquivo TSE via <strong>"Importar histórico TSE"</strong> no topo da tela.
      </div>
    </div>
  );

  if (!loaded) return null;

  var maxAvg = Math.max.apply(null, ranking.map(function (r) { return r.avg; }));
  var maxOp  = Math.max.apply(null, ranking.map(function (r) { return r.oportunidade; }));

  return (
    <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
      <div style={{ padding: '16px 20px', borderBottom: '1px solid var(--line)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <div>
          <div className="eyebrow">Mapa de força territorial</div>
          <h3 className="serif" style={{ fontSize: 20, margin: '2px 0 0', letterSpacing: '-0.01em' }}>
            {partido} · {cargo || cargoType} · {campaign.uf} — {ranking.length} municípios
          </h3>
        </div>
        <div className="dim" style={{ fontSize: 12 }}>
          Ordenado por média histórica de votos
        </div>
      </div>
      <div style={{ overflowX: 'auto' }}>
        <table className="tbl">
          <thead>
            <tr>
              <th style={{ width: 36 }}>#</th>
              <th>Município</th>
              <th className="num">Média histórica</th>
              <th style={{ width: 170 }}>Força</th>
              <th className="num">Apoiadores</th>
              <th className="num">Lideranças</th>
              <th style={{ width: 120 }}>Oportunidade</th>
            </tr>
          </thead>
          <tbody>
            {ranking.map(function (r, idx) {
              var forcaPct  = maxAvg > 0 ? (r.avg / maxAvg) * 100 : 0;
              var opPct     = maxOp  > 0 ? (r.oportunidade / maxOp) * 100 : 0;
              var forcaColor = forcaPct >= 66 ? 'var(--accent)' : forcaPct >= 33 ? 'oklch(0.62 0.16 40)' : 'oklch(0.55 0.14 250)';
              var opColor   = opPct >= 66 ? 'oklch(0.55 0.18 30)' : opPct >= 33 ? 'oklch(0.62 0.16 40)' : 'var(--accent)';
              var opLabel   = opPct >= 66 ? 'Alta' : opPct >= 33 ? 'Média' : 'Coberto';
              return (
                <tr key={r.ibge_code}>
                  <td className="num mono dim" style={{ fontSize: 12 }}>{idx + 1}</td>
                  <td>
                    <strong style={{ fontSize: 13 }}>{r.name}</strong>
                    <div className="mono dim" style={{ fontSize: 10 }}>{r.ibge_code}</div>
                  </td>
                  <td className="num mono" style={{ fontWeight: 700 }}>{window.fmt(r.avg)}</td>
                  <td>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
                      <div style={{ flex: 1, height: 8, background: 'var(--surface-2)', borderRadius: 999, minWidth: 60 }}>
                        <div style={{ width: forcaPct + '%', height: '100%', background: forcaColor, borderRadius: 999 }} />
                      </div>
                      <span className="mono" style={{ fontSize: 11, color: forcaColor, minWidth: 32 }}>
                        {Math.round(forcaPct)}%
                      </span>
                    </div>
                  </td>
                  <td className="num mono">{r.apoio > 0 ? window.fmt(r.apoio) : <span className="dim">—</span>}</td>
                  <td className="num mono">{r.lider > 0 ? r.lider : <span className="dim">—</span>}</td>
                  <td>
                    <span style={{
                      display: 'inline-flex', alignItems: 'center', gap: 4,
                      fontSize: 11.5, fontWeight: 600, color: opColor,
                      background: opColor + '18', padding: '2px 8px', borderRadius: 6,
                    }}>
                      <span style={{ width: 6, height: 6, borderRadius: '50%', background: opColor, display: 'inline-block', flexShrink: 0 }} />
                      {opLabel}
                    </span>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </div>
  );
}

// ── Modal de importação de dados TSE ─────────────────────────────────────
function ImportarHistoricoModal({ onClose, onImported }) {
  // steps: info | upload | processing | preview | done
  const [step,      setStep]      = useState_in('info');
  const [rows,      setRows]      = useState_in([]);
  const [fileName,  setFileName]  = useState_in(null);
  const [loading,   setLoading]   = useState_in(false);
  const [result,    setResult]    = useState_in(null);
  const [matchInfo, setMatchInfo] = useState_in(null);
  const fileRef = useRef_in(null);

  useEffect_in(() => {
    const k = (e) => e.key === 'Escape' && onClose();
    window.addEventListener('keydown', k);
    return () => window.removeEventListener('keydown', k);
  }, [onClose]);

  const baixarTemplate = () => {
    const csv = '﻿year;ibge_code;party;cargo_type;cargo;votes_nominal;votes_party\n2024;2800308;PT;proporcional;Vereador;1250;80\n2020;2800308;PT;proporcional;Vereador;1100;60\n';
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
    const url  = URL.createObjectURL(blob);
    const a    = document.createElement('a');
    a.href = url; a.download = 'template_historico_eleitoral.csv'; a.click();
    URL.revokeObjectURL(url);
  };

  const readFileAs = (file, enc) => new Promise((resolve, reject) => {
    const r = new FileReader();
    r.onload  = e => resolve(e.target.result.replace(/^﻿/, ''));
    r.onerror = reject;
    r.readAsText(file, enc);
  });

  const handleFile = async (e) => {
    const f = e.target.files && e.target.files[0];
    if (!f) return;
    e.target.value = '';
    setFileName(f.name);
    setStep('processing');
    try {
      // Detect format from headers (all ASCII, encoding irrelevant here)
      const utf8text = await readFileAs(f, 'UTF-8');
      const lines0   = utf8text.trim().split(/\r?\n/);
      if (lines0.length < 2) throw new Error('Arquivo sem linhas de dados.');
      const nSemi  = (lines0[0].match(/;/g) || []).length;
      const nComma = (lines0[0].match(/,/g) || []).length;
      const delim  = nSemi >= nComma ? ';' : ',';
      const headers = lines0[0].split(delim).map(h => h.trim().toLowerCase().replace(/"/g, ''));

      const tse = isTSEFormat(headers);

      // TSE files are historically ISO-8859-1; re-read with correct encoding
      const lines = tse
        ? (await readFileAs(f, 'ISO-8859-1')).trim().split(/\r?\n/)
        : lines0;

      let parsed;
      if (tse) {
        parsed = await parseTSELines(lines, headers, delim);
      } else {
        parsed = parseInternalLines(lines, headers, delim);
      }

      if (!parsed || parsed.length === 0) {
        alert(tse
          ? 'Nenhum município do arquivo foi encontrado no sistema. Cadastre os municípios antes de importar.'
          : 'Nenhuma linha válida encontrada. Verifique o cabeçalho do arquivo.'
        );
        setStep('upload');
        return;
      }
      setRows(parsed);
      setStep('preview');
    } catch (err) {
      alert('Erro ao processar arquivo: ' + err.message);
      setStep('upload');
    }
  };

  const parseTSELines = async (lines, headers, delim) => {
    // Load municipalities once; build name+state lookup map
    const allMuns = await window.API.getMunicipalities();
    const munMap  = {};
    for (const m of allMuns) {
      const key = normMunName(m.name) + '|' + (m.state || '').toUpperCase().trim();
      munMap[key] = m;
    }

    const rawRows = [];
    for (let i = 1; i < lines.length; i++) {
      if (!lines[i].trim()) continue;
      const parts = lines[i].split(delim).map(v => v.trim().replace(/^"|"$/g, ''));
      const o = {};
      headers.forEach((h, idx) => { o[h] = parts[idx] || ''; });

      const year       = parseInt(o.ano_eleicao) || 0;
      const munNameRaw = (o.nm_municipio || '').trim();
      const state      = (o.sg_uf || '').toUpperCase().trim();
      const party      = (o.sg_partido || '').toUpperCase().trim();
      const cargoRaw   = (o.ds_cargo   || '').toUpperCase().trim();
      const nomStr     = o.qt_votos_nominais || o.qt_votos || '0';
      const legStr     = o.qt_votos_de_legenda || o.qt_votos_legenda || '0';

      // Somente 1º turno; ignora branco/nulo e partidos inválidos
      if (o.nr_turno && o.nr_turno.trim() !== '1') continue;
      if (!year || !munNameRaw) continue;
      if (!party || party.startsWith('#') || party === 'INVALIDO') continue;
      const cargoDef = TSE_CARGO_NORM[cargoRaw];
      if (!cargoDef) continue;

      const lookupKey = normMunName(munNameRaw) + '|' + state;
      const mun = munMap[lookupKey];

      rawRows.push({
        year,
        ibge_code:    mun?.ibge_code || null,
        mun_key:      lookupKey,
        mun_name_raw: munNameRaw,
        state,
        party,
        cargo_type:    cargoDef.cargo_type,
        cargo:         cargoDef.cargo,
        votes_nominal: parseInt(nomStr.replace(/\D/g, '')) || 0,
        votes_party:   parseInt(legStr.replace(/\D/g, '')) || 0,
        _matched:      !!(mun && mun.ibge_code),
      });
    }

    // Collect match stats
    const matchedKeys  = new Set();
    const unmatchedSet = new Set();
    for (const r of rawRows) {
      if (r._matched) matchedKeys.add(r.mun_key);
      else unmatchedSet.add(r.mun_name_raw + ' (' + r.state + ')');
    }

    setMatchInfo({
      rawCount:    rawRows.length,
      matchedMuns: matchedKeys.size,
      unmatched:   Array.from(unmatchedSet).sort(),
    });

    // Aggregate by (year, ibge_code, party, cargo) — TSE files are zone-level
    const aggMap = {};
    for (const r of rawRows) {
      if (!r._matched) continue;
      const key = r.year + '|' + r.ibge_code + '|' + r.party + '|' + r.cargo;
      if (!aggMap[key]) {
        aggMap[key] = { year: r.year, ibge_code: r.ibge_code, party: r.party, cargo_type: r.cargo_type, cargo: r.cargo, votes_nominal: 0, votes_party: 0 };
      }
      aggMap[key].votes_nominal += r.votes_nominal;
      aggMap[key].votes_party   += r.votes_party;
    }
    return Object.values(aggMap);
  };

  const parseInternalLines = (lines, headers, delim) => {
    const parsed = [];
    for (let i = 1; i < lines.length; i++) {
      if (!lines[i].trim()) continue;
      const parts = lines[i].split(delim).map(v => v.trim().replace(/^"|"$/g, ''));
      const o = {};
      headers.forEach((h, idx) => { o[h] = parts[idx] || ''; });
      if (!o.ibge_code || !o.party || !o.year) continue;
      parsed.push({
        year:          parseInt(o.year) || 0,
        ibge_code:     o.ibge_code.trim(),
        party:         o.party.trim().toUpperCase(),
        cargo_type:    (o.cargo_type || '').toLowerCase().includes('maj') ? 'majoritario' : 'proporcional',
        cargo:         (o.cargo || '').trim(),
        votes_nominal: parseInt((o.votes_nominal || '0').replace(/\D/g, '')) || 0,
        votes_party:   parseInt((o.votes_party   || '0').replace(/\D/g, '')) || 0,
      });
    }
    setMatchInfo(null);
    return parsed;
  };

  const handleImport = async () => {
    setLoading(true);
    try {
      const erros = await window.API.importElectoralHistory(rows);
      setResult({ total: rows.length, erros });
      setStep('done');
      if (onImported) onImported();
    } catch (err) {
      alert('Erro: ' + err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="modal-back" onClick={onClose}>
      <div className="modal" style={{ maxWidth: 720 }} onClick={e => e.stopPropagation()}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 18 }}>
          <div>
            <div className="eyebrow">Dados TSE</div>
            <h3 className="serif" style={{ fontSize: 24, margin: '2px 0 0', letterSpacing: '-0.01em' }}>Importar histórico eleitoral</h3>
          </div>
          <button className="btn btn--ghost btn--icon" onClick={onClose}><Icon.X size={14} /></button>
        </div>

        {step === 'info' && (
          <>
            <div style={{ fontSize: 13, lineHeight: 1.7, marginBottom: 14, color: 'var(--ink-2)' }}>
              Importe resultados eleitorais diretamente do <strong>portal de dados abertos do TSE</strong> — sem conversão manual.
              O sistema detecta o formato automaticamente e ignora as colunas desnecessárias.
            </div>
            <div className="callout" style={{ marginBottom: 14 }}>
              <div style={{ marginBottom: 6 }}>
                <strong>Arquivos aceitos do TSE</strong> (dadosabertos.tse.jus.br):
              </div>
              <ul style={{ margin: 0, paddingLeft: 18, lineHeight: 1.8 }}>
                <li><code>votacao_candidato_munzona_ANO_SE.csv</code></li>
                <li><code>votacao_partido_munzona_ANO_SE.csv</code></li>
              </ul>
              <div style={{ marginTop: 8, fontSize: 12, color: 'var(--muted)' }}>
                Colunas usadas: ANO_ELEICAO, NM_MUNICIPIO, SG_UF, SG_PARTIDO, DS_CARGO, QT_VOTOS_NOMINAIS.
                Votos de zona são somados automaticamente por município.
              </div>
            </div>
            <div style={{ fontSize: 12, color: 'var(--muted)', marginBottom: 18, lineHeight: 1.6 }}>
              Também aceita CSV no formato interno (template abaixo) para dados fora do TSE.
              Municípios são vinculados pelo nome + UF ao cadastro do sistema.
            </div>
            <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end', paddingTop: 18, borderTop: '1px solid var(--line)' }}>
              <button className="btn" onClick={onClose}>Cancelar</button>
              <button className="btn" onClick={baixarTemplate}><Icon.Download size={13} /> Baixar template interno</button>
              <button className="btn btn--primary" onClick={() => setStep('upload')}>
                <Icon.Plus size={13} /> Selecionar arquivo
              </button>
            </div>
          </>
        )}

        {step === 'upload' && (
          <>
            <input ref={fileRef} type="file" accept=".csv,.txt" style={{ display: 'none' }} onChange={handleFile} />
            <div className="dropzone" style={{ marginBottom: 20 }}
              onClick={() => fileRef.current && fileRef.current.click()}>
              <Icon.Download size={22} style={{ transform: 'rotate(180deg)' }} />
              <strong style={{ marginTop: 8 }}>Clique para selecionar o CSV</strong>
              <span className="dim" style={{ fontSize: 12 }}>Aceita arquivos brutos do TSE ou no formato interno</span>
            </div>
            <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, paddingTop: 18, borderTop: '1px solid var(--line)' }}>
              <button className="btn" onClick={() => setStep('info')}>← Voltar</button>
            </div>
          </>
        )}

        {step === 'processing' && (
          <div style={{ padding: '72px 0', textAlign: 'center' }}>
            <div className="dim" style={{ fontSize: 13 }}>Processando arquivo e vinculando municípios…</div>
          </div>
        )}

        {step === 'preview' && (
          <>
            {/* Match info — shown only for TSE imports */}
            {matchInfo && (
              <div style={{ marginBottom: 14 }}>
                <div style={{ display: 'flex', gap: 20, marginBottom: 10, flexWrap: 'wrap', fontSize: 13 }}>
                  <span><strong>{window.fmt(rows.length)}</strong> <span className="dim">registros para importar</span></span>
                  <span><strong style={{ color: 'var(--accent)' }}>{matchInfo.matchedMuns}</strong> <span className="dim">municípios vinculados</span></span>
                  {matchInfo.unmatched.length > 0 && (
                    <span style={{ color: 'oklch(0.55 0.16 30)' }}>
                      <strong>{matchInfo.unmatched.length}</strong> não encontrado{matchInfo.unmatched.length !== 1 ? 's' : ''}
                    </span>
                  )}
                </div>
                {matchInfo.unmatched.length > 0 && (
                  <div style={{ background: 'oklch(0.97 0.02 30)', borderRadius: 8, padding: '10px 14px', fontSize: 12, marginBottom: 10 }}>
                    <strong style={{ color: 'oklch(0.55 0.18 30)' }}>Municípios não encontrados</strong>
                    <span className="dim"> — cadastre-os no módulo Municípios para importar esses dados:</span>
                    <div style={{ marginTop: 6, color: 'var(--muted)', columnCount: 2, columnGap: 12 }}>
                      {matchInfo.unmatched.slice(0, 20).map((n, i) => <div key={i}>{n}</div>)}
                      {matchInfo.unmatched.length > 20 && <div>…e mais {matchInfo.unmatched.length - 20}</div>}
                    </div>
                  </div>
                )}
              </div>
            )}
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
              <strong>{rows.length} registro{rows.length !== 1 ? 's' : ''} a importar</strong>
              <span className="dim" style={{ fontSize: 12 }}>{fileName}</span>
            </div>
            <div style={{ maxHeight: 300, overflowY: 'auto', borderRadius: 8, border: '1px solid var(--line)', marginBottom: 16 }}>
              <table className="tbl">
                <thead>
                  <tr><th>Ano</th><th>IBGE</th><th>Partido</th><th>Tipo</th><th>Cargo</th><th className="num">Votos nominais</th></tr>
                </thead>
                <tbody>
                  {rows.slice(0, 100).map((r, i) => (
                    <tr key={i}>
                      <td className="mono">{r.year}</td>
                      <td className="mono dim">{r.ibge_code}</td>
                      <td><strong>{r.party}</strong></td>
                      <td className="dim" style={{ fontSize: 11 }}>{r.cargo_type}</td>
                      <td>{r.cargo}</td>
                      <td className="num mono">{window.fmt(r.votes_nominal)}</td>
                    </tr>
                  ))}
                  {rows.length > 100 && (
                    <tr><td colSpan={6} style={{ textAlign: 'center', padding: 12 }} className="dim">…e mais {rows.length - 100} registros</td></tr>
                  )}
                </tbody>
              </table>
            </div>
            <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, paddingTop: 18, borderTop: '1px solid var(--line)' }}>
              <button className="btn" onClick={() => { setStep('upload'); setRows([]); setFileName(null); setMatchInfo(null); }}>← Voltar</button>
              <button className="btn btn--primary" disabled={loading} onClick={handleImport}>
                {loading ? 'Importando…' : `Importar ${rows.length} registros`}
              </button>
            </div>
          </>
        )}

        {step === 'done' && result && (
          <div style={{ textAlign: 'center', padding: '28px 0' }}>
            <div style={{ width: 56, height: 56, borderRadius: 999, background: 'var(--accent-soft)', display: 'grid', placeItems: 'center', margin: '0 auto 16px' }}>
              <Icon.Check size={26} />
            </div>
            <div className="serif" style={{ fontSize: 24, marginBottom: 6 }}>Importação concluída</div>
            <div className="dim" style={{ marginBottom: result.erros.length > 0 ? 16 : 0 }}>
              {result.total - result.erros.length} de {result.total} registros importados com sucesso.
            </div>
            {result.erros.length > 0 && (
              <div style={{ background: 'oklch(0.97 0.02 30)', borderRadius: 8, padding: '10px 14px', maxWidth: 480, marginInline: 'auto', fontSize: 12 }}>
                <strong style={{ color: 'oklch(0.55 0.18 30)' }}>{result.erros.length} erro{result.erros.length !== 1 ? 's' : ''}:</strong>
                <ul style={{ margin: '6px 0 0', paddingLeft: 18, color: 'var(--dim)', textAlign: 'left' }}>
                  {result.erros.slice(0, 5).map((e, i) => <li key={i}>Linha {e.linha}: {e.erro}</li>)}
                </ul>
              </div>
            )}
            <button className="btn btn--primary" style={{ marginTop: 20 }} onClick={onClose}>Fechar</button>
          </div>
        )}
      </div>
    </div>
  );
}

// ── Tela principal ────────────────────────────────────────────────────────
function InteligenciaScreen({ campaign, openModal }) {
  // Inferir partido e tipo a partir da campanha ativa
  const tipoInicial = ['vereador','dep-estadual','dep-federal'].includes(campaign?.tipo)
    ? 'proporcional' : 'majoritario';
  const cargoInicial = {
    vereador: 'Vereador', 'dep-estadual': 'Dep. Estadual', 'dep-federal': 'Dep. Federal',
    prefeito: 'Prefeito', governador: 'Governador', senador: 'Senador',
  }[campaign?.tipo] || '';

  const [partido,    setPartido]   = useState_in('');
  const [cargoType,  setCargoType] = useState_in(tipoInicial);
  const [cargo,      setCargo]     = useState_in(cargoInicial);
  const [munSearch,  setMunSearch] = useState_in('');
  const [munSugs,    setMunSugs]   = useState_in([]);
  const [municipio,  setMunicipio] = useState_in(null);
  const [showSugs,   setShowSugs]  = useState_in(false);
  const [showImport, setShowImport] = useState_in(false);
  const munRef = useRef_in(null);

  const [historico,  setHistorico] = useState_in([]);
  const [intelData,  setIntelData] = useState_in(null);
  const [loading,    setLoading]   = useState_in(false);
  const [refreshKey, setRefreshKey] = useState_in(0);
  const [viewMode,   setViewMode]  = useState_in('municipal');

  // Ano da próxima eleição: usa a data da campanha ou infere pelo histórico
  const campaignYear = campaign?.eleicaoDataRaw
    ? new Date(campaign.eleicaoDataRaw).getFullYear() : null;
  const maxHistYear  = historico.length > 0
    ? Math.max.apply(null, historico.map(function (d) { return d.year; })) : 0;
  const nextYear = (campaignYear && maxHistYear > 0 && campaignYear > maxHistYear)
    ? campaignYear
    : (maxHistYear > 0 ? maxHistYear + 4 : campaignYear || new Date().getFullYear() + 4);

  // ── Autocomplete de município ──
  useEffect_in(() => {
    if (munSearch.length < 2) { setMunSugs([]); return; }
    const t = setTimeout(() => {
      window.API.getMunicipalities()
        .then(all => {
          const q = munSearch.toLowerCase();
          setMunSugs(all.filter(m => m.name.toLowerCase().includes(q)).slice(0, 7));
        })
        .catch(console.error);
    }, 220);
    return () => clearTimeout(t);
  }, [munSearch]);

  useEffect_in(() => {
    const handler = (e) => {
      if (munRef.current && !munRef.current.contains(e.target)) setShowSugs(false);
    };
    window.addEventListener('mousedown', handler);
    return () => window.removeEventListener('mousedown', handler);
  }, []);

  // ── Carrega dados ao mudar filtros ──
  useEffect_in(() => {
    if (!municipio || !partido.trim()) { setHistorico([]); setIntelData(null); return; }
    setLoading(true);
    Promise.all([
      window.API.getElectoralHistory(municipio.ibge_code, partido, cargoType, cargo || null),
      window.API.getMunicipalityIntelData(campaign.id, municipio.id),
    ])
      .then(([hist, intel]) => { setHistorico(hist); setIntelData(intel); })
      .catch(err => { console.error(err); setHistorico([]); setIntelData(null); })
      .finally(() => setLoading(false));
  }, [municipio?.id, partido, cargoType, cargo, campaign?.id, refreshKey]);

  // ── Métricas derivadas ──
  const avgVotos   = historico.length > 0
    ? Math.round(historico.reduce((s, h) => s + h.votes_nominal, 0) / historico.length)
    : 0;
  const metaTotal  = intelData?.metaTotal  || 0;
  const apoiadores = intelData?.apoiadores || 0;
  const metaRef    = metaTotal > 0 ? metaTotal : (campaign?.metaVotos || 0);

  const diagKey = municipio ? diagnose(avgVotos, metaRef, apoiadores, cargoType) : null;
  const diag    = diagKey ? DIAG[diagKey] : null;
  const metaFillPct = metaRef > 0 ? Math.min(100, Math.round(apoiadores / metaRef * 100)) : 0;
  const histFillPct = metaRef > 0 && avgVotos > 0 ? Math.round(avgVotos / metaRef * 100) : 0;

  const hasData     = !loading && municipio;
  const hasHistorico = historico.length > 0;
  const noIbge      = municipio && !municipio.ibge_code;

  return (
    <div className="dash">
      {/* ── Cabeçalho ── */}
      <div className="dash__header">
        <div>
          <div className="eyebrow">Inteligência Partidária</div>
          <h1 className="serif" style={{ fontSize: 36, lineHeight: 1.05, margin: '8px 0 4px', letterSpacing: '-0.02em' }}>
            Expansão Territorial
          </h1>
          <p className="dim" style={{ margin: 0, maxWidth: 520 }}>
            Cruze o histórico eleitoral do TSE com o desempenho em tempo real das lideranças de campo.
          </p>
        </div>
        <div style={{ display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap', justifyContent: 'flex-end' }}>
          <div className="seg">
            <button className={'seg__btn ' + (viewMode === 'municipal' ? 'is-active' : '')}
              onClick={() => setViewMode('municipal')}>Análise Municipal</button>
            <button className={'seg__btn ' + (viewMode === 'estadual'  ? 'is-active' : '')}
              onClick={() => setViewMode('estadual')}>Visão Estadual</button>
          </div>
          {campaign?.roleRaw === 'administrador' && (
            <button className="btn" onClick={() => setShowImport(true)}>
              <Icon.Download size={14} /> Importar histórico TSE
            </button>
          )}
        </div>
      </div>

      <AlertaBanner campaign={campaign} />

      {/* ── Filtros ── */}
      <div className="card" style={{ padding: '16px 20px', marginBottom: 20 }}>
        <div className="intel-filters">

          {/* Partido */}
          <div>
            <div className="eyebrow" style={{ marginBottom: 6 }}>Partido</div>
            <input
              className="input"
              list="partidos-dl"
              placeholder="Ex: PT, PL, MDB…"
              value={partido}
              onChange={e => setPartido(e.target.value.toUpperCase())}
            />
            <datalist id="partidos-dl">
              {PARTIDOS.map(p => <option key={p} value={p} />)}
            </datalist>
          </div>

          {/* Tipo */}
          <div>
            <div className="eyebrow" style={{ marginBottom: 6 }}>Tipo</div>
            <div className="seg">
              <button className={'seg__btn ' + (cargoType === 'proporcional' ? 'is-active' : '')}
                onClick={() => { setCargoType('proporcional'); setCargo(''); }}>Prop.</button>
              <button className={'seg__btn ' + (cargoType === 'majoritario'  ? 'is-active' : '')}
                onClick={() => { setCargoType('majoritario');  setCargo(''); }}>Maj.</button>
            </div>
          </div>

          {/* Cargo */}
          <div>
            <div className="eyebrow" style={{ marginBottom: 6 }}>Cargo</div>
            <select className="input" value={cargo} onChange={e => setCargo(e.target.value)}>
              <option value="">— Todos —</option>
              {CARGOS_BY_TYPE[cargoType].map(c => <option key={c} value={c}>{c}</option>)}
            </select>
          </div>

          {/* Município autocomplete */}
          <div ref={munRef} style={{ position: 'relative' }}>
            <div className="eyebrow" style={{ marginBottom: 6 }}>Município</div>
            <div style={{ position: 'relative' }}>
              <span style={{ position: 'absolute', left: 10, top: '50%', transform: 'translateY(-50%)', color: 'var(--muted)', pointerEvents: 'none', display: 'flex' }}>
                <Icon.Search size={14} />
              </span>
              <input
                className="input"
                placeholder="Buscar município…"
                value={munSearch}
                autoComplete="off"
                onChange={e => { setMunSearch(e.target.value); setMunicipio(null); setShowSugs(true); }}
                onFocus={() => munSearch.length >= 2 && setShowSugs(true)}
                style={{ paddingLeft: 32 }}
              />
            </div>
            {showSugs && munSugs.length > 0 && (
              <div style={{
                position: 'absolute', top: '100%', left: 0, right: 0, zIndex: 99,
                background: 'var(--surface)', border: '1px solid var(--line)',
                borderRadius: 8, marginTop: 4, overflow: 'hidden',
                boxShadow: '0 6px 24px rgba(0,0,0,0.1)',
              }}>
                {munSugs.map(m => (
                  <button key={m.id}
                    style={{
                      width: '100%', padding: '9px 14px', textAlign: 'left',
                      background: 'none', border: 0, borderBottom: '1px solid var(--line)',
                      cursor: 'pointer', fontSize: 13,
                    }}
                    onMouseDown={() => { setMunicipio(m); setMunSearch(m.name); setShowSugs(false); }}
                  >
                    <strong>{m.name}</strong>
                    <span className="dim mono" style={{ marginLeft: 8, fontSize: 11 }}>
                      {m.state}{m.ibge_code ? ' · ' + m.ibge_code : ''}
                    </span>
                  </button>
                ))}
              </div>
            )}
          </div>
        </div>

        {/* Chips de filtro ativo */}
        {municipio && (
          <div className="row" style={{ marginTop: 12, gap: 6, flexWrap: 'wrap' }}>
            <span className="dim" style={{ fontSize: 11.5 }}>Filtros:</span>
            {partido && <span className="badge badge--accent">{partido}</span>}
            <span className="badge">{cargoType === 'proporcional' ? 'Proporcional' : 'Majoritário'}</span>
            {cargo && <span className="badge">{cargo}</span>}
            <span className="badge"><Icon.Pin size={11} /> {municipio.name} · {municipio.state}</span>
            {noIbge && (
              <span style={{ fontSize: 11.5, color: 'oklch(0.55 0.18 30)' }}>
                ⚠ Sem código IBGE — cadastre-o no módulo Municípios para ver o histórico.
              </span>
            )}
          </div>
        )}
      </div>

      {/* ── Visão estadual ── */}
      {viewMode === 'estadual' && (
        <RankingEstadualCard
          campaign={campaign}
          partido={partido}
          cargoType={cargoType}
          cargo={cargo}
        />
      )}

      {/* ── Estado vazio (modo municipal) ── */}
      {viewMode === 'municipal' && !municipio && !loading && (
        <div className="card" style={{ padding: '72px 32px', textAlign: 'center' }}>
          <div style={{ color: 'var(--muted)', marginBottom: 18, display: 'flex', justifyContent: 'center' }}>
            <Icon.Target size={44} />
          </div>
          <div className="serif" style={{ fontSize: 24, marginBottom: 8 }}>Selecione os filtros</div>
          <p className="dim" style={{ margin: '0 auto', maxWidth: 400, lineHeight: 1.6 }}>
            Escolha o partido, tipo de cargo e busque um município para ver o diagnóstico de inteligência eleitoral.
          </p>
        </div>
      )}

      {/* ── Loading (modo municipal) ── */}
      {viewMode === 'municipal' && loading && (
        <div style={{ textAlign: 'center', padding: '56px 0' }} className="dim">
          Cruzando dados…
        </div>
      )}

      {/* ── Conteúdo principal (modo municipal) ── */}
      {viewMode === 'municipal' && hasData && (
        <>
          {/* Cards de métricas */}
          <div className="dash__grid" style={{ marginBottom: 20 }}>
            <div className="card" style={{ padding: '18px 20px' }}>
              <div className="eyebrow">Média histórica</div>
              <div className="serif" style={{ fontSize: 36, margin: '6px 0 2px', color: 'oklch(0.55 0.14 250)', lineHeight: 1 }}>
                {window.fmt(avgVotos)}
              </div>
              <div className="dim" style={{ fontSize: 11.5 }}>
                {hasHistorico ? `votos · ${historico.length} eleições` : 'sem histórico cadastrado'}
              </div>
            </div>

            <div className="card" style={{ padding: '18px 20px' }}>
              <div className="eyebrow">Meta de campo</div>
              <div className="serif" style={{ fontSize: 36, margin: '6px 0 2px', color: 'oklch(0.62 0.16 40)', lineHeight: 1 }}>
                {window.fmt(metaRef)}
              </div>
              <div className="dim" style={{ fontSize: 11.5 }}>
                {metaTotal > 0 ? 'soma das metas das lideranças' : 'meta global da campanha'}
              </div>
            </div>

            <div className="card" style={{ padding: '18px 20px' }}>
              <div className="eyebrow">Apoiadores cadastrados</div>
              <div className="serif" style={{ fontSize: 36, margin: '6px 0 2px', color: 'var(--accent)', lineHeight: 1 }}>
                {window.fmt(apoiadores)}
              </div>
              <div className="dim" style={{ fontSize: 11.5 }}>
                {metaRef > 0 ? `${metaFillPct}% da meta · ${municipio.name}` : `em ${municipio.name}`}
              </div>
            </div>

            {diag && (
              <div className="card" style={{
                padding: '18px 20px',
                background: diag.bg,
                border: `1.5px solid ${diag.color}40`,
              }}>
                <div className="eyebrow">Diagnóstico</div>
                <div style={{ fontSize: 20, fontWeight: 700, marginTop: 6, color: diag.color, lineHeight: 1 }}>
                  {diag.emoji} {diag.label}
                </div>
                <div className="dim" style={{ fontSize: 11, marginTop: 5 }}>análise automática abaixo</div>
              </div>
            )}
          </div>

          {/* Gráficos */}
          <div className="intel-charts">
            <div className="card" style={{ padding: '20px 24px' }}>
              <div className="eyebrow">Histórico temporal</div>
              <h3 className="serif" style={{ fontSize: 20, margin: '4px 0 18px', letterSpacing: '-0.01em' }}>
                {partido || '—'} {cargo || cargoType} · {municipio.name}
              </h3>
              {hasHistorico ? (
                <HistoricoChart data={historico} meta={metaRef} />
              ) : (
                <div style={{ padding: '40px 0', textAlign: 'center' }}>
                  <div className="dim" style={{ fontSize: 13, marginBottom: 10 }}>
                    Nenhum dado histórico para este filtro.
                  </div>
                  <div className="callout" style={{ fontSize: 12, textAlign: 'left' }}>
                    Importe os resultados via <strong>"Importar histórico TSE"</strong> no topo da tela.
                    Use o código IBGE do município como chave de vínculo.
                  </div>
                </div>
              )}
            </div>

            <div className="card" style={{ padding: '20px 24px' }}>
              <div className="eyebrow">Comparativo</div>
              <h3 className="serif" style={{ fontSize: 20, margin: '4px 0 20px', letterSpacing: '-0.01em' }}>
                Passado × Meta × Campo
              </h3>
              <ComparativoChart media={avgVotos} meta={metaRef} apoiadores={apoiadores} />

              {/* Mini indicadores */}
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10, marginTop: 22, paddingTop: 18, borderTop: '1px solid var(--line)' }}>
                <div>
                  <div className="dim" style={{ fontSize: 11 }}>Cobertura do campo</div>
                  <div style={{ fontSize: 22, fontWeight: 700, color: diag?.color || 'var(--accent)' }}>
                    {metaFillPct}%
                  </div>
                </div>
                <div>
                  <div className="dim" style={{ fontSize: 11 }}>Base histórica</div>
                  <div style={{ fontSize: 22, fontWeight: 700, color: 'oklch(0.55 0.14 250)' }}>
                    {histFillPct}%
                  </div>
                </div>
              </div>
            </div>
          </div>

          {/* Projeção + Comparativo de partidos */}
          {hasHistorico && (
            <div className="intel-proj">
              <ProjecaoCard
                historico={historico}
                meta={metaRef}
                cargo={cargo || cargoType}
                nextYear={nextYear}
              />
              <ComparativoPartidosCard
                ibgeCode={municipio?.ibge_code}
                cargoType={cargoType}
                cargo={cargo}
                myParty={partido}
                municipioName={municipio?.name}
              />
            </div>
          )}

          {/* Diagnóstico detalhado */}
          {diag && (
            <div className="card" style={{
              padding: '24px 28px', marginBottom: 20,
              border: `2px solid ${diag.color}30`,
              background: diag.bg,
            }}>
              <div style={{ display: 'flex', gap: 20, alignItems: 'flex-start' }}>
                <div style={{ fontSize: 42, lineHeight: 1, flexShrink: 0 }}>{diag.emoji}</div>
                <div style={{ flex: 1 }}>
                  <div className="eyebrow" style={{ marginBottom: 4 }}>Diagnóstico automatizado · {municipio.name}</div>
                  <h3 className="serif" style={{ fontSize: 26, margin: '0 0 10px', color: diag.color, letterSpacing: '-0.01em' }}>
                    {diag.label}
                  </h3>
                  <p style={{ margin: 0, fontSize: 14, lineHeight: 1.7, maxWidth: 600 }}>{diag.desc}</p>

                  <div style={{ display: 'flex', gap: 28, marginTop: 18, flexWrap: 'wrap' }}>
                    <div>
                      <div className="dim" style={{ fontSize: 11 }}>Campo vs. meta</div>
                      <div style={{ fontSize: 20, fontWeight: 700, color: diag.color }}>{metaFillPct}%</div>
                    </div>
                    <div>
                      <div className="dim" style={{ fontSize: 11 }}>Histórico vs. meta</div>
                      <div style={{ fontSize: 20, fontWeight: 700, color: diag.color }}>{histFillPct}%</div>
                    </div>
                    <div>
                      <div className="dim" style={{ fontSize: 11 }}>Lideranças ativas</div>
                      <div style={{ fontSize: 20, fontWeight: 700, color: diag.color }}>
                        {intelData?.liderancas?.filter(l => l.status === 'ativa').length || 0}
                        <span style={{ fontSize: 13, fontWeight: 400, color: 'var(--muted)', marginLeft: 4 }}>
                          / {intelData?.liderancas?.length || 0}
                        </span>
                      </div>
                    </div>
                    <div>
                      <div className="dim" style={{ fontSize: 11 }}>Eleições analisadas</div>
                      <div style={{ fontSize: 20, fontWeight: 700, color: diag.color }}>{historico.length}</div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          )}

          {/* Tabela de lideranças */}
          <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
            <div style={{ padding: '16px 20px', borderBottom: '1px solid var(--line)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
              <div>
                <div className="eyebrow">Auditoria e gamificação</div>
                <h3 className="serif" style={{ fontSize: 20, margin: '2px 0 0', letterSpacing: '-0.01em' }}>
                  Lideranças em {municipio.name}
                </h3>
              </div>
              {openModal && (
                <button className="btn" onClick={() => openModal('nova-lideranca', { municipio: municipio.id })}>
                  <Icon.Plus size={13} /> Atribuir liderança
                </button>
              )}
            </div>

            {!intelData?.liderancas?.length ? (
              <div style={{ textAlign: 'center', padding: '48px 20px' }}>
                <div className="dim" style={{ fontSize: 13, marginBottom: 14 }}>
                  Nenhuma liderança cadastrada neste município para a campanha ativa.
                </div>
                {openModal && (
                  <button className="btn btn--primary"
                    onClick={() => openModal('nova-lideranca', { municipio: municipio.id })}>
                    <Icon.Plus size={13} /> Cadastrar primeira liderança
                  </button>
                )}
              </div>
            ) : (
              <div className="tbl-wrap"><table className="tbl">
                <thead>
                  <tr>
                    <th>Liderança</th>
                    <th className="num">Meta atribuída</th>
                    <th className="num">Cadastrados</th>
                    <th style={{ width: 180 }}>Eficiência</th>
                    <th>Entrega</th>
                  </tr>
                </thead>
                <tbody>
                  {intelData.liderancas.map(l => {
                    const pct = l.meta > 0 ? Math.min(100, Math.round(l.apoiadores / l.meta * 100)) : 0;
                    const cor = pct >= 75 ? 'var(--accent)' : pct >= 40 ? 'oklch(0.62 0.16 40)' : 'oklch(0.55 0.18 30)';
                    const status = pct >= 75 ? 'No prazo' : pct >= 40 ? 'Em progresso' : 'Atenção';
                    return (
                      <tr key={l.id}>
                        <td>
                          <div className="row" style={{ gap: 10 }}>
                            <span className="avatar avatar--sm">
                              {l.nome.split(' ').map(x => x[0]).slice(0, 2).join('')}
                            </span>
                            <div>
                              <strong>{l.nome}</strong>
                              <div>
                                <span className={"badge " + (l.status === 'ativa' ? 'badge--accent' : '')} style={{ fontSize: 10 }}>
                                  {l.status}
                                </span>
                              </div>
                            </div>
                          </div>
                        </td>
                        <td className="num mono">{l.meta > 0 ? window.fmt(l.meta) : <span className="dim">—</span>}</td>
                        <td className="num mono">{window.fmt(l.apoiadores)}</td>
                        <td>
                          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                            <div style={{ flex: 1, height: 8, background: 'var(--surface-2)', borderRadius: 999 }}>
                              <div style={{
                                width: `${pct}%`, height: '100%',
                                background: cor, borderRadius: 999,
                                transition: 'width 600ms ease',
                              }} />
                            </div>
                            <span className="mono" style={{ fontSize: 12, fontWeight: 600, color: cor, minWidth: 34 }}>
                              {pct}%
                            </span>
                          </div>
                        </td>
                        <td>
                          <span style={{
                            display: 'inline-flex', alignItems: 'center', gap: 5,
                            fontSize: 12, fontWeight: 600, color: cor,
                            background: cor + '18', padding: '3px 9px', borderRadius: 6,
                          }}>
                            <span style={{ width: 6, height: 6, borderRadius: '50%', background: cor, display: 'inline-block', flexShrink: 0 }} />
                            {status}
                          </span>
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table></div>
            )}
          </div>
        </>
      )}

      {/* Modal de importação */}
      {showImport && (
        <ImportarHistoricoModal
          onClose={() => setShowImport(false)}
          onImported={() => setRefreshKey(k => k + 1)}
        />
      )}
    </div>
  );
}

window.Screens = window.Screens || {};
window.Screens.InteligenciaScreen = InteligenciaScreen;
