# 📊 Schema de Dados - Busco Rumo

## Campo por Campo

### Identidade
```javascript
{
  id: "sao_paulo_sp",        // string: kebab-case de nome_uf (único)
  nome: "São Paulo",         // string: nome oficial do município
  uf: "SP"                   // string (2): sigla do estado
}
```

| Campo | Tipo | Fonte | Atualização |
|-------|------|-------|------------|
| `id` | string | Manual (gerado de nome_uf) | Raro |
| `nome` | string | IBGE API | Anual (IBGE) |
| `uf` | string(2) | IBGE API | Fixa |

---

### Localização Geográfica
```javascript
{
  regiao: "Sudeste",          // enum: Norte, Nordeste, Centro-Oeste, Sudeste, Sul
  bioma: "mata_atlantica",    // enum: amazonia, cerrado, mata_atlantica, caatinga, pantanal, pampa
  clima: "ameno",             // enum: frio, ameno, quente, semiarido
  lat: -23.5505,              // float: latitude IBGE
  lon: -46.6333,              // float: longitude IBGE
  alt: 760,                   // int: altitude da SEDE urbana, em metros
  alt_max: 1204,              // int: ponto culminante do MUNICÍPIO, em metros
  alt_med: 780                // int: mediana da cota do território, em metros
}
```

| Campo | Tipo | Fonte | Atualização | Nota |
|-------|------|-------|------------|------|
| `regiao` | enum | Mapa UF→Regiao | Fixa | Regra: estado define região |
| `bioma` | enum | Mapa UF→Bioma | Fixa | **Simplificado**: 1 bioma por estado |
| `clima` | enum | Amplitude térmica | Fixa | **frio** se temp_min<10 · **quente** se temp_max>32 · senão **ameno** (semiárido preservado) — ver `scripts/melhorar_clima.py` |
| `lat`/`lon` | float | IBGE API localidades | Fixa | Dados geográficos |
| `alt` | int | SRTM 30m no ponto da sede (IBGE BC250 2025) | Ao rerodar o gerador | Altitude de **onde a cidade está**, não do município |
| `alt_max` | int | SRTM 30m recortado pela malha municipal do IBGE | Ao rerodar o gerador | **Ponto culminante do município** — pode estar longe da sede |
| `alt_med` | int | idem | Ao rerodar o gerador | Mediana do terreno: diz se o município é alto **como um todo** |
| `desnivel` | int | SRTM 30m num raio de 10 km da sede | Ao rerodar o gerador | **O visual**: quanto o terreno sobe acima da cidade (p95 do entorno − cota da sede) |
| `alt_fonte` | string | — | — | `SRTM 1 arco-seg (~30m) + malha municipal IBGE` |

No cache `sources/raw/altitude_srtm.json` há ainda `alt_min`, `alt_p90`, `area_km2` e **`relevo`** (mediana do desnível em blocos de ~900m — quão acidentado é o terreno, independente do tamanho do município). `relevo` é o que define a tag `montanha`; ver *Tipo de Cidade* abaixo.

#### Altimetria: por que dois números (2026-08-20)

Um número só mente nos dois sentidos, e o card mostra os dois por isso:

- **Só a sede engana pra baixo.** Resende/RJ tem sede a ~400 m no vale do Paraíba,
  mas o município sobe até o maciço do Itatiaia — Visconde de Mauá, distrito de
  serra, está a ~1.200 m. Quem procura serra descartaria Resende olhando só `alt`.
- **Só o máximo engana pra cima.** Itatiaia/RJ tem `alt_max` perto de 2.800 m
  (Agulhas Negras) e sede a ~410 m: morar lá é morar no vale, não no pico.

A fonte anterior (Open-Elevation, SRTM reamostrado em grade ~250m) suavizava o
relevo e errava até 570 m justamente em serra — Itatiaia marcava 1007 m, Cabreúva
898 m (era o topo da Serra do Japi, não a cidade), Diamantina 850 m (planalto
subestimado). Trocado por SRTM 1 arco-seg (~30m) amostrado no ponto oficial da
sede, com mediana 5×5 (~150m) pra não depender de um pixel isolado de encosta.
Precisão medida contra referências conhecidas: sede dentro de ±50 m na maioria
(Campos do Jordão 1.606 m aqui × 1.628 m oficial; São Joaquim/SC 1.354 × 1.353).

O `alt_max` sai do mesmo MDE de 30 m recortado por uma malha municipal
simplificada (~295 vértices por município), então **cume em divisa** fica abaixo
do valor topográfico oficial:

| Cume | Oficial | Aqui | Divisa |
|---|---|---|---|
| Pedra da Mina (Queluz/SP) | 2.798 m | 2.792 m | interna |
| Pico da Bandeira (Ibitirama/ES) | 2.892 m | 2.868 m | interna |
| Agulhas Negras (Resende/RJ) | 2.791 m | 2.770 m | interna |
| Pico da Neblina (São Gabriel da Cachoeira/AM) | 2.995 m | **1.244 m** | internacional |
| Monte Roraima (Uiramutã/RR) | 2.734 m | **2.477 m** | internacional |

Em divisa interna o erro fica entre −6 m e −149 m e é aceito como limite do MDE.
Em divisa internacional a malha simplifica a linha em mais de 1 km e deixa o
maciço inteiro fora do polígono — esses dois casos entram como override curado em
`sources/overrides/altitude_max.json` (cota do IBGE, com o valor que o MDE achou
registrado ao lado). Cache versionado: `sources/raw/altitude_srtm.json` · gerador:
`scripts/gerar_altitude_srtm.py`.

**Mapas referência:**
```python
# UF → Região
UF_REGIAO = {
  'AC':'Norte', 'AL':'Nordeste', 'AM':'Norte', 'AP':'Norte',
  'BA':'Nordeste', 'CE':'Nordeste', 'DF':'Centro-Oeste', 'ES':'Sudeste',
  'GO':'Centro-Oeste', 'MA':'Nordeste', 'MG':'Sudeste', 'MS':'Centro-Oeste',
  'MT':'Centro-Oeste', 'PA':'Norte', 'PB':'Nordeste', 'PE':'Nordeste',
  'PI':'Nordeste', 'PR':'Sul', 'RJ':'Sudeste', 'RN':'Nordeste',
  'RO':'Norte', 'RR':'Norte', 'RS':'Sul', 'SC':'Sul',
  'SE':'Nordeste', 'SP':'Sudeste', 'TO':'Norte'
}

# UF → Bioma (simplificado)
UF_BIOMA = {
  'AC':'amazonia', 'BA':'caatinga', 'DF':'cerrado', 'ES':'mata_atlantica',
  'MG':'mata_atlantica', 'MS':'pantanal', 'PA':'amazonia', 'RJ':'mata_atlantica',
  'RS':'pampa', 'SP':'mata_atlantica',
  # ... todos os 27 estados
}
```

---

### Indicadores Socioeconômicos
```javascript
{
  pop: 11451245,                    // integer: população total (Censo 2022)
  idh: 0.805,                       // float: 0.0–1.0 (indicador interno IDH-E, NÃO é o IDHM oficial - ver seção abaixo)
  ideb: 6.2,                        // float: 0–10 (INEP/Ideb 2025, Anos Iniciais, rede pública)
  pib: 45231,                       // integer: PIB per capita R$ (IBGE, PIB dos Municípios 2023)
  temp: 19,                         // integer: temperatura média °C
  temp_min: 14,                     // integer: mínima estimada °C
  temp_max: 28,                     // integer: máxima estimada °C
  crescimento_pct: 0.3,             // float ou null: crescimento populacional 2010–2022 (%)
  crescimento_fonte: "IBGE",        // enum: "IBGE" (real) | null (indisponível)
  crescimento_status: "censitario", // enum: "censitario" | "indisponivel"
  crescimento_classificacao: "baixo", // enum: "retracao"|"baixo"|"moderado"|"elevado"|"desconhecida"
  pop_2010: 11253503,               // int: só quando crescimento_status == "censitario"
  pop_2022: 11451999,               // int: só quando crescimento_status == "censitario"
  pop_2025_estimativa: 11904961,    // int ou ausente: estimativa IBGE 2025 (SIDRA 6579) - campo SEPARADO, nunca combinado com o crescimento censitário
  pop_2025_estimativa_status: "oficial_ibge"
}
```

| Campo | Tipo | Fonte | Última atualização | Nota |
|-------|------|-------|-------------------|------|
| `pop` | int | IBGE Censo 2022 | Censo 2022 | ~5570 municípios |
| `idh` | float | Cálculo interno (educação/longevidade/PIB, todos IBGE) | componentes de anos distintos | **NÃO é o IDHM oficial do PNUD/Atlas Brasil** — ver seção abaixo |
| `ideb` | float | INEP/Ideb 2025 (rede pública) | 2025 | 5.477 oficiais · 93 com fallback regional |
| `pib` | int | IBGE — PIB dos Municípios (ind. 47001) | 2023 | 5.570 oficiais · 0 fallback |
| `temp` | int | Média estadual **corrigida pela altitude medida da sede** (lapse 6,5 °C/km) | Ao rerodar o pipeline | Média anual estimada — ver *Clima* abaixo |
| `temp_uf_base` | int | Âncora estadual (o valor histórico de onde `temp` parte) | — | Existe para o passo ser idempotente |
| `temp_fonte` | string | — | — | `média estadual corrigida pela altitude medida (lapse 6,5°C/km)` |
| `temp_min`/`temp_max` | float | `temp` ± amplitude por bioma/litoral | Ao rerodar | Base da categorização de `clima` — `melhorar_clima.py` |

#### Clima: por que a altitude entrou (2026-08-21)

Até aqui `temp` era uma **constante por estado** (os 645 municípios de SP tinham 19 °C) e a única
coisa que esfriava uma cidade era carregar a tag `montanha` (`min_offset = -8`). Três consequências,
todas medidas:

1. **Campos do Jordão (sede 1.606 m) e Ubatuba (sede 6 m) eram ambas `ameno`**, separadas por 3 °C
   na mínima. As médias anuais reais são 13,7 °C e 22,5 °C — quase 9 °C.
2. **Antonina/PR (8 m) e Morretes/PR (11 m)** carregam a tag `montanha` por causa da escarpa da
   Serra do Mar dentro do município e recebiam o desconto de frio. É o caso Ubatuba: paredão de
   serra no município, cidade quente e úmida ao pé dele.
3. **Circularidade**: com a tag `montanha` saindo do relevo medido (`fix_relevo_v25`), `clima`
   viraria função do relevo. Rodar o passo antigo depois do retag transformaria **94 cidades em
   `frio`**, entre elas Novo Hamburgo/RS, sede a 36 m.

Agora `temp` = média do estado corrigida pelo **lapse rate atmosférico** (6,5 °C/1.000 m) sobre a
altitude real da sede, ancorada em `alt_ref_uf` — a altitude média das sedes do estado **ponderada
por população**, ou seja, a altitude a que a média estadual se aplica. No litoral o aquecimento
previsto é amortecido em 40% (o mar segura a máxima). Erro médio contra 15 médias anuais reais:
**1,4 °C**, contra 2,0 °C do dado anterior — que além disso não variava dentro do estado.

**Limite conhecido:** o modelo não captura continentalidade nem latitude dentro do estado.
Ribeirão Preto/SP (554 m, 300 km a noroeste da capital) sai a 19,7 °C contra 23,5 °C reais.
Enquanto `temp_uf_base` for constante por estado, esse erro fica; a saída definitiva é uma normal
climatológica municipal (INMET).
| `crescimento_pct` | float/null | IBGE (Censo 2010 vs 2022, SIDRA 200/9514) | 2010 e 2022 | Ver [CRESCIMENTO.md](CRESCIMENTO.md) — só municípios comparáveis (join por código IBGE via DTB) |
| `crescimento_fonte` | enum | — | — | `IBGE` (5.565 municípios) ou `null` (5 criados após 2010, indisponível) |
| `pop_2010`/`pop_2022` | int | IBGE (SIDRA 200/9514, oficial) | Censo | Presentes só nos municípios censitariamente comparáveis |
| `pop_2025_estimativa` | int | IBGE (SIDRA 6579, Estimativas de População) | 2025 | Campo distinto do Censo — nunca usado pra calcular crescimento |

### IDH-E: como é calculado (indicador interno, não oficial)
**Fase 2 complementar (2026-07-27): este campo NÃO é o IDHM oficial publicado pelo Atlas Brasil/PNUD.** É um indicador
próprio (chamado de "IDH-E" na interface), calculado a partir de 3 componentes IBGE:

IDH-E = (componente_educacao + componente_longevidade + componente_renda) / 3
- **Educação**: anos de escolaridade (não é a fórmula oficial do IDHM, que combina escolaridade esperada + escolaridade média da população adulta)
- **Longevidade**: expectativa de vida (Censo)
- **Renda**: PIB per capita bruto (não é a fórmula oficial do IDHM, que usa logaritmo da renda per capita com piso/teto normalizados)

**Por que não é o IDHM oficial**: o IDHM (Atlas Brasil/PNUD) só tem edição municipal completa para os anos censitários
(2000, 2010) com metodologia própria de normalização. Não existe uma atualização oficial 2022 publicada pelo PNUD no
momento desta auditoria. Em vez de reaproveitar o IDHM 2010 (desatualizado) ou inventar uma extrapolação, o produto usa
este indicador interno simplificado — sinalizado como estimado, não oficial, na interface e no componente de
proveniência (`proveniencia_shared.js`).

**Cobertura**: ~99% dos municípios (componentes: educação/longevidade IBGE Censo, PIB per capita IBGE 2023)

### IDEB: fonte, safra e fallback (atualizado 2026-08-07)

**Fonte:** planilha de divulgação do INEP, **Anos Iniciais do Ensino Fundamental, rede pública**,
por município — `divulgacao_anos_iniciais_municipios_<EDICAO>.zip`. Baixada, cacheada com
proveniência (SHA-256 + URL + data) em `sources/raw/ideb_inep/` e fundida por
`scripts/baixar_ideb_inep.py`, que é a **fonte única** do campo.

**Por que não vem mais da API do IBGE:** até 2026-08-07 o `ideb` era buscado em
`servicodados.ibge.gov.br/.../indicadores/78188`, pegando o ano mais recente que a API devolvesse.
Essa API **só chega a 2023** — o IDEB 2025 (divulgado em 05/08/2026) só existe no download do INEP.

**Rede pública** (municipais + estaduais + federais somadas), desde 2026-08-07. A planilha do INEP
publica por rede; a rota antiga entregava a **Municipal** — confirmado por casamento exato dos 4.957
valores anteriores (100,0% com Municipal/2023, contra 6,3% com 2021 e 0,9% com Estadual).

A troca foi decisão de produto, motivada pela **checagem visual dos apps**: com a rede Municipal,
251 municípios apareciam na tela com média regional e asterisco, e **158 deles têm nota real na
rede pública** — incluindo **Brasília** (2,8 mi de habitantes; o DF não tem rede municipal, então
nunca teria nota por Municipal) e **Mauá/SP** (418 mil). Brasília saiu de `5,5 estimado` para
`6,0 oficial`; Mauá, de `5,8` para `6,6`. **Ninguém regrediu** de oficial para fallback na troca.

Também é mais fiel à pergunta do produto: quem escolhe onde morar não matricula o filho numa
*rede*, matricula na escola pública que existe na cidade.

> ⚠️ Valores gravados **antes de 2026-08-07** eram da rede Municipal. Não comparar safra nova com
> registro antigo achando que a diferença é evolução do ensino — parte dela é mudança de definição.

**⚠ Bug de proveniência corrigido junto:** `ideb_ano` dizia `2021` em todos os municípios com valor
oficial, mas o dado real era de **2023**. O ano vinha chumbado em
`scripts/enriquecer_provenance_status.py`, que o deduzia em vez de ler a fonte. Esse script não
escreve mais nada de IDEB: só **confere** que o carimbo do INEP existe e **aborta** se não existir.

**Fallback:** município sem valor na rede pública fica com a constante regional `IDEB_REG`
(a mesma de `gerar_cidades.py`) e `ideb_status='fallback_regional'` — nunca a nota da capital
(a versão anterior desta seção descrevia um fallback por capital que o código nunca teve).

| Safra | Municípios com valor oficial | Com fallback regional |
|---|---|---|
| 2023 · rede Municipal (anterior) | 4.957 | 613 |
| 2025 · rede Municipal | 5.319 | 251 |
| **2025 · rede Pública (atual)** | **5.477** | **93** |

Periodicidade: **bienal**, aplicado em anos ímpares, divulgado em agosto do ano seguinte.
Próxima safra: **2027 (~ago/2028)** — ver [AGENDA_DADOS.md](AGENDA_DADOS.md).

### Segurança e Saneamento (Fase de segurança, 2026-07-28)

Reativa o que a Fase 2 complementar (2026-07-27) tinha suspendido — `det_homicidios`/`mtur_seguranca` (planilha MTur sem
ano/licença/fonte documentados) continuam **mortos, nunca lidos** por nenhum dos 2 apps. Os campos abaixo são novos,
com fonte oficial e proveniência completa.

```javascript
{
  seguranca_taxa_homicidios: 6.16,        // float ou null: taxa por 100 mil hab. (Atlas da Violência 2026, Ipea/FBSP)
  seguranca_tendencia: "queda",           // enum: "queda" | "estavel" | "alta" (média dos últimos 3 anos vs. 3 anos anteriores)
  seguranca_status: "oficial",            // enum: "oficial" | "fallback_regional"
  seguranca_fonte: "Atlas da Violência 2026 (Ipea/FBSP), série municipal",
  seguranca_ano: "2019-2024",             // janela usada p/ taxa + tendência
  saneamento_indice_pct: 76.4,            // float ou null: média de água/esgoto/resíduos atendidos (0-100)
  saneamento_status: "oficial",           // enum: "oficial" | "fallback_regional"
  saneamento_fonte: "SINISA 2024 — ano de referência 2023 (Ministério das Cidades), via Instituto Água e Saneamento",
  saneamento_ano: "2023"          // ANO DE REFERÊNCIA do dado, não o nome da edição
}
```

| Campo | Tipo | Fonte | Cobertura | Nota |
|-------|------|-------|-----------|------|
| `seguranca_*` | ver acima | Atlas da Violência 2026 (Ipea/FBSP) — `dados-api/series-values/20/4` (municípios) e `/20/3` (estados) | 5.530/5.570 municípios diretos (99,3%); 40 com fallback estadual | Script: `scripts/baixar_seguranca_atlas_violencia.py`. Segurança é usada no **Busco Rumo e no Dicas Outdoor** (badge "Refúgio Seguro" + ordenação) |
| `saneamento_*` | ver acima | SINISA **edição 2024, ano de referência 2023** (Ministério das Cidades), consolidado por página pública do Instituto Água e Saneamento | 5.272/5.570 municípios diretos (94,6%); 298 com fallback estadual | Script: `scripts/baixar_saneamento_sinisa.py`. Saneamento é usado **só no Busco Rumo** (não aparece no Dicas Outdoor) |

> ⚠️ **SINISA tem dois anos — não confundir.** A edição `AAAA` tem ano de referência `AAAA-1`.
> Até 2026-08-07 o campo `saneamento_ano` guardava `"2024"`, que é o nome da **edição**, não a data
> do dado — mesmo erro encontrado no mesmo dia em `ideb_ano` e `pib_ano`. Corrigido: `saneamento_ano`
> agora é `"2023"` (referência) e o nome da edição vive em `saneamento_fonte`, onde não pode ser lido
> como data. O componente de proveniência (`proveniencia_shared.js`) também passou a rotular a fonte
> como "SINISA — edição 2024", já que os dois números aparecem lado a lado na tela.

**Fallback estadual**: quando o município não tem série própria, o valor vem da média/série oficial da UF (nunca uma
invenção) e `*_status` fica `"fallback_regional"` — sempre marcado como tal na UI (nunca disfarçado de dado
municipal direto), mesmo padrão já usado no IDEB acima.

**Uso no score do Busco Rumo**: ambos entram como fator base sempre aplicado em `recommendCities()` (`bussola.html`)
e como dimensão `violencia_letal` no motor de compatibilidade (`index.html`, `compatScoreDetalhado`) — nunca via
`det_homicidios`/`mtur_seguranca`.

**Exibição no card (2026-07-29):**
- **Busco Rumo**: chips 🛡️ Segurança e 🚰 Saneamento na faixa de sinais do card, coloridos pelos mesmos limiares do
  score de Qualidade (ver seção "Qualidade" abaixo); `*` no rótulo + tooltip quando o valor é fallback estadual.
  Detalhe completo (fonte/ano/confiança) no modal, via `proveniencia_shared.js` (`ProveShared.render('seguranca'|'saneamento', ...)`).
- **Dicas Outdoor**: só o índice de Segurança aparece no card (mesma cor/fallback); saneamento não aparece (fora de
  escopo deste app). O modal interno **não** mostra o detalhe de proveniência de segurança/crescimento/IDH-E — esse
  conteúdo é do Busco Rumo; o card interno do Outdoor prioriza atividades outdoor (trilhas/esportes).

### Qualidade (calcScore): fórmula final com Segurança pública e Saneamento (Fase de Qualidade, 2026-07-28)

`calcScore()`/`scoreComponents()` é o indicador de **"Qualidade"** (0–100) mostrado em todo card de cidade no Busco
Rumo (`index.html`) e no Dicas Outdoor (`outdoor/index.html`) — formula duplicada nos dois arquivos (não há módulo
compartilhado pra esse trecho, mesma convenção do resto do repo). É **diferente** de:
- `compatScoreDetalhado()` (Busco Rumo) — "Compatibilidade" (`card_compat`, "combina com você"), preferência
  PESSOAL que só pontua quando o usuário ativa uma persona. Tem sua própria dimensão `violencia_letal` (segurança),
  independente da Qualidade — os dois scores aparecem em rótulos diferentes na mesma tela, sem se sobrepor.
- `recommendCities()` (Bússola, `bussola.html`) — motor de recomendação do questionário, não tem um número
  "Qualidade" 0-100; segurança/saneamento já entram lá como fator base (ver seção acima). A Bússola não ganhou uma
  segunda pontuação nesta fase — o fator base já era a camada certa.

**Busco Rumo (`index.html`) — 7 componentes, soma 100:**

| Componente | Peso | Campo fonte | Fórmula de normalização (0–1) |
|---|---|---|---|
| IDH-E | 34 | `idh` | `max(0, (idh-0.5)/0.37)` |
| IDEB | 24 | `ideb` | `max(0,(ideb-3)/7)` (neutro 0.4 se ausente) |
| PIB per capita | 15 | `pib` | `min(1, pib/80000)` |
| Natureza | 6 | `tipo` (montanha/praia) | 1 se montanha/praia, senão 0.3 |
| Turismo | 6 | `tipo` (turística) | 1 se turística, senão 0.2 |
| **Segurança pública** | **8** | `seguranca_taxa_homicidios`/`seguranca_status` | `max(0, min(1, 1 - taxa/50))` |
| **Saneamento** | **7** | `saneamento_indice_pct`/`saneamento_status` | `max(0, min(1, indice/100))` |

**Dicas Outdoor (`outdoor/index.html`) — 6 componentes, soma 100 (sem saneamento — fora de escopo deste app):**

| Componente | Peso | Fórmula |
|---|---|---|
| IDH-E | 37 | igual acima |
| IDEB | 26 | igual acima |
| PIB per capita | 16 | igual acima |
| Natureza | 6 | igual acima |
| Turismo | 6 | igual acima |
| **Segurança pública** | **9** | igual acima |

Os pesos dos 5 componentes antigos foram **encolhidos proporcionalmente** (não zerados nem escolhidos ao acaso) pra
abrir espaço aos novos e ainda somar 100: Busco Rumo aplicou fator 0,85 (100 → 85, sobrando 15 pra
segurança+saneamento); Dicas Outdoor aplicou fator 100/93 sobre a versão de 6 componentes (sem saneamento) pra
redistribuir o que sobrou sem o campo de saneamento.

**Fallback estadual dentro da Qualidade**: como a cobertura de `seguranca_status`/`saneamento_status` já é ~100%
(nenhum município fica sem valor, graças ao fallback), o componente sempre entra no cálculo — mas quando
`status==='fallback_regional'` o valor normalizado é **amortecido a 50% da distância até o neutro (0.5)**:
`valor_final = 0.5 + (valor_bruto - 0.5) * 0.5`. Isso reduz o peso prático do fallback sem zerá-lo nem tratá-lo como
dado municipal — e o breakdown (`renderScoreBreakdown`) sempre mostra "(estadual)" ao lado da nota quando isso
acontece, além do aviso completo no modal de proveniência (`proveniencia_shared.js`, chaves `seguranca`/`saneamento`
do `REGISTRY`, confiança cai pra "média" nesse caso).

**`PERSONA_W.seguranca`** (bônus quando o usuário ativa a persona/chip "Segurança" no comparador de perfis) foi
**corrigido** nesta fase: antes bonificava IDH-E/IDEB/PIB/Natureza (usando IDH-E como proxy de criminalidade — o
mesmo erro que a Fase 2 já tinha proibido no texto, só não tinha sido corrigido aqui). Agora bonifica exclusivamente
o componente `s_seguranca` real (`seguranca:{seguranca:2.0}`) — sem dupla contagem com os outros componentes.

### PIB per capita: oficial por município (atualizado 2026-08-07)

**Fonte:** IBGE — *PIB dos Municípios*, pesquisa 38, indicador **47001** (PIB per capita, R$),
por `scripts/baixar_pib_ibge.py`, que é a **fonte única** do campo. Cache com proveniência em
`sources/raw/ibge_pib/`.

**Cobertura: 5.570/5.570 (100%) com valor oficial — nenhum fallback.**

> ⚠️ A versão anterior desta seção descrevia um rateio `(pop_município / pop_estado) × pib_estado`
> com a ressalva "apenas SP, RJ e MG têm PIB granular". **Isso nunca foi verdade no código** — o
> IBGE publica PIB per capita para todos os 5.570 municípios e o pipeline sempre usou o indicador
> 47001. O texto do rateio era descrição de um mecanismo que não existia.

**⚠ Bug de proveniência corrigido:** `pib_ano` dizia `2021`, mas o dado era de **2023** — 5.567 dos
5.570 valores (99,9%) batiam com a série 2023. O `gerar_cidades.py` pegava
`sorted(series.keys())[-1]` (o mais recente que a API devolvesse) sem registrar qual era, e o
`enriquecer_provenance_status.py` chumbava `2021`. Mesmo bug do IDEB, mesmo dia.

**Fallback eliminado:** o status antes era *inferido* comparando o valor com a constante regional
`PIB_REG` — isso marcava **227 municípios** como `fallback_regional` só porque o PIB real deles
coincidia com o número redondo da própria região, descartando dado bom do score. Agora o status é
carimbado na coleta: 0 fallbacks.

**Precisão:** o valor deixou de ser arredondado ao milhar (`round(pib/1000)*1000`), que distorcia
até ~6% nos municípios pobres (8.432 → 8.000). Agora é o inteiro em reais.

Periodicidade: **anual**, divulgação ~dezembro, defasagem de ~3 anos (safra atual: **2023**) —
ver [AGENDA_DADOS.md](AGENDA_DADOS.md).

---

### Mudanças Climáticas
```javascript
{
  mc: "neutro",               // enum: positivo, neutro, risco_calor, risco_seca, risco_inundacao, risco_desmatamento
  desmat_ha_ano: 0            // integer: hectares desmatados/ano (MapBiomas)
}
```

| Campo | Tipo | Fonte | Atualização | Nota |
|-------|------|-------|------------|------|
| `mc` | enum | Mapa UF→MC + dados municipais | Anual | Risco climático por estado |
| `desmat_ha_ano` | int | MapBiomas município | ~2 anos atrás | Apenas alguns municípios |

**Classificação MC por estado:**
```python
UF_MC = {
  'AC':'risco_desmatamento',   # Amazônia
  'AL':'risco_inundacao',      # Litoral
  'BA':'risco_seca',           # Sertão
  'DF':'neutro',               # Planalto
  'MG':'neutro',               # Interior
  'SP':'neutro',               # Centro-sul
  # ... (ver scripts/gerar_cidades.py)
}
```

**Desmatamento (MapBiomas) — travado na Coleção 9, por limitação da fonte:**
- Arquivo: `scripts/mapbiomas_desmat_municipio.xlsx` (Col.9, supressão de veg. primária 2018–2023)
- Processado por: `scripts/enriquecer_desmatamento.py` · Dados: ha/ano · 5.548 municípios com valor

> ⚠️ **A Coleção 10.1 NÃO publica desmatamento por município** — verificado em 2026-08-07 baixando
> `MAPBIOMAS_BRAZIL_COL.10.1_DEFORESTATION.xlsx` e lendo a aba METADADOS: os recortes territoriais
> são `country`, `biome`, `region`, `state`, `watershed_lv1-3`, `uc_name` e `il_name`. **Não existe
> campo de município.** As abas são STATE_REGION_BIOME, WATERSHED, UC e IL. Na Coleção 9 existia um
> arquivo `..._state_municipality.xlsx` (é o que está no repo, 217 MB); na 10/10.1 esse arquivo
> deixou de ser publicado — as três URLs prováveis dão 404, e o único `MUNICIPALITY` que sobrou na
> página de estatísticas é o de *pastagem* (Col.1), outro produto.
>
> **Não substituir pelo dado estadual da 10.1**: seria trocar valor municipal real por um número de
> UF replicado nas 5.570 cidades — degradação, não atualização, e contra a regra de nunca disfarçar
> recorte regional de dado municipal.
>
> Caminho se isso virar prioridade: a plataforma do MapBiomas
> (`plataforma.brasil.mapbiomas.org`) permite consulta por município, mas é engenharia nova, sem
> arquivo estável publicado. Enquanto isso, o campo fica na Col.9 e a defasagem está declarada aqui.

**Cobertura vegetal (`cobertura_florestal_pct`/`cobertura_natural_pct`) — essa sim está em dia:**
Coleção **10.1, ano 2024** (5.564 municípios), a safra mais recente publicada. É um arquivo
diferente do de desmatamento e continua tendo recorte municipal.

---

### Características da Cidade
```javascript
{
  tipo: ["capital", "industrial"],
  esportes: ["futebol", "trekking", "cicloturismo"]
}
```

#### Tipo de Cidade (arrays)
Pode ter múltiplos valores:
```javascript
tipo: string[]  // um ou mais de:
[
  'capital',                   // Capital de estado
  'praia',                     // Litoral (até 50km da costa)
  'montanha',                  // Relevo de serra medido no SRTM — 717 cidades (ver abaixo)
  'interior',                  // Não-litoral, não-capital
  'turistica',                 // Fluxo turístico — 399 cidades (curadoria MTur)
  'universitaria',             // Polo universitário — dado real INEP, 698 cidades (corrigido 2026-07-17)
  'agro',                      // Agricultura/pecuária (PIB agro alto)
  'industrial'                 // Industrial (PIB ind alto)
  // 'comercio_servicos' — DESCONTINUADO como filtro (registros legados podem mantê-lo)
]
```

**Como é determinado?**
1. **Capital**: Verificar se é capital_uf
2. **Praia**: Lista oficial de municípios litorâneos do IBGE 2021 — 286 cidades (`fix_dados_v13.py`)
3. **Montanha**: **relevo local ≥ 120m E mediana do terreno ≥ 500m** (`fix_relevo_v25.py`), medido no SRTM 30m sobre a malha do IBGE. Sai quem tem relevo local < 50m; entre os dois limiares fica uma **faixa cinza** que preserva a curadoria existente (Tiradentes/MG, relevo 95m e mediana 955m, não entra pela regra mas não é removida). O corte de saída é conservador de propósito — 50m é *terreno liso*, não *pouco acidentado*: **São Paulo/SP** (relevo 55m) fica logo acima e mantém a tag, decisão de produto tomada sabendo que o relevo medido não a sustenta sozinha. Nunca entra município marcado como `praia` e nunca sai cidade-portal de Pico do Brasil — invariantes de `fix_mtur_v22.py`.

   > **Até 2026-08-20 a regra era `det_montanhas > 20`** (`fix_mtur_v20.py`) — densidade de **atrativo** de montanha no inventário do MTur, ou seja, oferta turística, não relevo. Isso deixava Cunha/SP de fora (`det_montanhas` = 0,7, apesar de relevo local de 145m e mediana de 1.025m) e mantinha São Paulo/SP dentro (relevo local de 55m). A reconciliação mudou 507 municípios: **489 entraram, 18 saíram**, de 246 para 717 — cada um com ±4,2 pontos no score de Qualidade (a tag vale 6 pontos em `s_nat`), e o filtro duro de paisagem da Bússola passou de 246 para 717 candidatos.

   > **Por que `relevo` e não altitude ou amplitude:** mediana da cota diz que o município é *alto*, não que é acidentado (Brasília: mediana 1.025m, relevo local 55m); amplitude (máx−mín) cresce com a **área** (Brasília tem 621m de amplitude por ter 5.755 km², enquanto Tiradentes, serrana, tem 478m em 83 km²) e normalizar por área derruba serrano grande (Diamantina, 3.886 km²) e aceita chapada plana pequena (Cidade Ocidental/GO). `relevo` é a mediana do desnível em blocos de ~900m — mede inclinação e independe do tamanho do município. Calibragem: Petrópolis 285 · Nova Petrópolis 235 · Campos do Jordão 175 · Cunha 145 · Diamantina 125 · **corte em 120** · Volta Redonda 115 · Belo Horizonte 75 · Brasília 55 · Curitiba 45. A exigência de mediana ≥500m separa serra de **vale cercado de morro** (Governador Valadares, mediana 275m; Ipatinga, 435m).
4. **Turística**: praia c/ pop>3k + montanha + curadoria MTur — 399 cidades (`fix_dados_v13.py`)
5. **Agro/Industrial**: PIB setorial IBGE
6. **Universitária (tag em `tipo`)**: dado real do INEP (`tem_universidade`, ver campos abaixo), corrigido via `scripts/corrigir_tag_universitaria.py` em 2026-07-17 — 698 municípios (570 ganharam a tag, 5 perderam por não terem IES real, 128 já estavam corretos). **`gerar_cidades.py` ainda tem a heurística antiga** (lista hardcoded + população ≥ 300 mil, 133 cidades, com falsos-positivos) — se o pipeline completo for rodado do zero nesse gerador, `patch_universidade_cities_data.py` (grava `tem_universidade`) entra logo após a base (passo 6d), e `corrigir_tag_universitaria.py` (corrige a tag `tipo` a partir do campo real) roda **por último** (passo 21b), **depois** de todos os `fix_*` que reescrevem `tipo[]` — caso contrário a base regride para o proxy antigo. Ordem canônica completa: ver a tabela `STAGES` em `build.py` / [MANUTENCAO.md](MANUTENCAO.md) (ou simplesmente `python build.py --run`).

#### Universidade (campos próprios, INEP — 2026-07-17)
```javascript
{
  tem_universidade: true,           // bool — pelo menos 1 IES ativa no município
  universidade_publica: true,       // bool — Federal/Estadual/Municipal
  universidade_privada: true,       // bool — com ou sem fins lucrativos
  qtd_ies: 21,                      // int — total de instituições
  qtd_ies_forte: 5                  // int — só Universidade/Centro Universitário/IF (exclui faculdade isolada)
}
```
**Fonte:** Censo da Educação Superior 2024 (INEP), `MICRODADOS_ED_SUP_IES_2024.CSV` — 2.561 IES em 698 municípios, cruzado por código IBGE (campo `ibge`) via `scripts/patch_universidade_cities_data.py`. Agregação intermediária em `scripts/ies_por_municipio.json`. Usado no chip do card de avaliação (`index.html`, `renderCityProfile()`) e no campo `"uni"` de `bussola_cities.json` (Bússola de Mudança).

#### Esportes Disponíveis (arrays)

> **⚠️ Atualização 2026-07-15:** `mountain_bike` e `ciclismo_estrada` saíram
> **permanentemente** do produto (pedido do usuário). `trekking`/`hiking`/
> `cicloturismo`/`bikepacking` saíram do Wikiloc (que continua os alimentando
> na tabela abaixo como histórico de origem, v16/v17) e **hoje vêm de fontes
> oficiais curadas** (Trilhas SP, Ciclorrotas SP, Peregrinação, Rede de
> Trilhas) via `scripts/sync_atividades_oficiais.py` — ver
> [RUNBOOK_ATIVIDADES_OFICIAIS.md](RUNBOOK_ATIVIDADES_OFICIAIS.md). A tabela
> e a narrativa v13-v16 abaixo são mantidas como **histórico** de como os
> dados chegaram até aqui; para o estado atual dessas 4 atividades, ver o
> runbook.

As 21 atividades outdoor vêm de **dados reais de uso do Wikiloc** (`fix_atividades_wikiloc_v16.py`); as demais têm **critério geográfico discriminante** (`fix_esportes_v14.py`/`v15`).
Removidos por serem universais: ~~natação~~ (100%) e ~~hipismo~~ (95%). Retirados do dashboard na v16: ~~esqui~~, ~~tênis~~, ~~sandboard~~, ~~stand-up paddle~~, ~~surf~~, ~~buggy~~, ~~rappel~~.
```javascript
esportes: string[]  // um ou mais de:
[
  // ── 20 códigos vindos do Wikiloc (uso registrado, filtro de qualidade — v16) ──
  // 21 slugs do CSV → 20 códigos (parapente funde em asa_delta_parapente).
  'via_ferrata',               // Wikiloc (16)
  'canionismo',                // Wikiloc (49)
  'escalada',                   // Wikiloc (28)
  'espeleologia',              // Wikiloc (68)
  'mergulho',                   // Wikiloc (9)
  'canoagem',                   // Wikiloc — caiaque/canoa (37)
  'birdwatching',              // Wikiloc — observação de aves (44)
  'observacao_fauna',          // Wikiloc — observação de fauna (30)
  'balonismo',                 // Wikiloc (22)
  'asa_delta_parapente',       // Wikiloc — asa delta + parapente (44)
  'vela',                       // Wikiloc — veleiro (37)
  'cavalgada',                 // Wikiloc (31)
  'jet_ski',                    // Wikiloc (20)
  'kite_windsurf',             // Wikiloc — kitesurf (22)
  'kite_ski',                   // Wikiloc (8)
  'alpinismo',                 // Wikiloc — montanhismo (42)
  'bikepacking',               // Wikiloc (40)
  'remo',                       // Wikiloc (32)
  'trekking',                   // Wikiloc — trilha ≥20 km, TrailRank≥50 (96)
  'hiking',                     // Wikiloc — trilha 2–19,9 km, TrailRank≥50 (186)
  // ── atividades com critério geográfico próprio (não-Wikiloc) ──
  'futebol',                    // Estádio profissional + capitais (255)
  'mountain_bike',             // Serra/turística/praia (256)
  'pesca_esportiva',           // Litoral + grandes rios/reservatórios (356)
  'rafting',                    // Rio de corredeira + operação comercial (32, lista curada)
  'bodyboard',                 // Litoral (ondas pequenas) (115)
  'ciclismo_estrada',          // Estradas bem sinalizadas (1327)
  'pico_brasil'                 // 36 cidades-portal dos pontos mais altos
]
// SAÍRAM do dashboard (v16): esqui_snowboard, tenis, sandboard, stand_up_paddle,
// surf, buggy, rappel_tirolesa. Universais já removidos: natacao, hipismo.
```

**Como é determinado?**
1. **Base inicial** por inferências de tipo/bioma em `gerar_cidades.py`:
   - Montanha → escalada, rappel, asa delta (trekking saiu daqui em 2026-07-15, ver aviso acima)
   - Praia → surf, mergulho, kite, stand up paddle
   - Rio/lago → canoagem, rafting, pesca
2. **Curadoria geográfica** aplicada por `fix_esportes_v14.py`:
   - Futebol → lista de clubes profissionais (CBF Séries A/B/C/D) + 27 capitais
   - Pesca → litoral + grandes bacias/represas (São Francisco, Amazonas, Paraná, Tocantins-Araguaia, Pantanal)
   - Kite/Sandboard/Buggy → confinados a `tipo:praia`
   - ~~Trekking/MTB → restritos a `montanha`/`turistica`/`praia`~~ (histórico — desde 2026-07-15 `trekking` não é mais tocado aqui e `mountain_bike` saiu do produto)
   - Natação e Hipismo → removidos (universais)
3. **Curadoria v15** (`fix_rafting_praia_v15.py`):
   - Rafting → lista factual de ~32 destinos com rio de corredeira e operação comercial (Brotas-SP, Três Coroas-RS, Apiúna-SC, Jaciara-MT, Itacaré-BA, Presidente Figueiredo-AM…). Antes em ~700 cidades (quase todo PR+SC) por erro de heurística regional.
   - Segmento MTur `sol_praia` → removido de 666 cidades sem litoral; agora só em `tipo:praia`.
4. **Curadoria v16 — atividades Wikiloc** (`fix_atividades_wikiloc_v16.py`, fonte `wikiloc_cidades.csv`):
   - **20 códigos** outdoor vêm de **dados reais de uso do Wikiloc** (plataforma de trilhas alimentada por usuários — evidência de uso, não cadastro oficial), agora com **filtro de qualidade** (`MIN_TRILHAS` por atividade + `min_km`/`max_km`/`min_trailrank` para trekking/hiking — ver [WIKILOC_COMO_RASPAR.md](WIKILOC_COMO_RASPAR.md)). É a **fonte única de verdade**: limpa os códigos-alvo e reaplica do CSV.
   - 21 slugs do CSV → 20 códigos (parapente funde em asa_delta_parapente). **Trekking** (≥20 km) e **hiking** (2–19,9 km) saem do mesmo slug `trekking`, separados por distância.
   - O filtro removeu falsos positivos (ex.: birdwatching 237→44, cavalgada 255→31, balonismo 68→22). 5 atividades novas: alpinismo, bikepacking, remo, kite_ski, observacao_fauna.
   - Saem do dashboard: esqui_snowboard, tenis, sandboard, stand_up_paddle, surf, buggy, rappel_tirolesa.

---

### Notas
```javascript
{
  nota: "Município serrano no sul da Mantiqueira, ~1.100m de altitude...",
  nota_en: "Mountain town in the southern Mantiqueira range...",
  nota_es: "Municipio serrano en el sur de la Mantiqueira...",
  nota_zh: "位于南马蒂克拉山脉的山区市镇...",
}
```

| Campo | Tipo | Fonte | Atualização |
|-------|------|-------|------------|
| `nota` | string | `scripts/gen_notas.py` (5.558 cidades, orientado a dados) + curadoria manual em `NOTAS_I18N` no `index.html` (12 cidades) | Ao rodar `gen_notas.py` |

Nota = Descrição para usuários entenderem a vocação da cidade (turismo, natureza, isolamento
etc.), **sempre com um ponto positivo e um "Ponto de atenção" negativo**. Para as 5.558 cidades
sem curadoria manual, o texto é 100% derivado dos indicadores do próprio dataset (IDH, IDEB,
PIB, crescimento, renda, saúde, segurança, conectividade, turismo, desmatamento) — sem fatos
inventados. As 12 cidades curadas (fatos reais conhecidos) ficam em `NOTAS_I18N` dentro do
`index.html`, não em `cities_data.js`. Ver [RUNBOOK_IDIOMAS.md](RUNBOOK_IDIOMAS.md) seção
"Notas de cidade" para como editar/traduzir.

---

### `araucaria*` — o município ainda tem araucária?

| Campo | Tipo | Fonte | Atualização |
|-------|------|-------|------------|
| `araucaria` | bool | evidência em pelo menos uma das fontes abaixo | anual (PEVS, ~set) |
| `araucaria_classe` | `produtiva` \| `confirmada` \| `historica` \| `null` | ver regra abaixo | idem |
| `araucaria_ano` | int \| null | **ano lido da fonte**, nunca deduzido | idem |
| `araucaria_fonte` | string | IBGE/PEVS 289 (pinhão) e/ou GBIF | idem |
| `araucaria_status` | `oficial` \| `sem_evidencia` | idem | idem |
| `araucaria_pinhao_t` | float | IBGE/PEVS 289, quantidade produzida na última safra | idem |
| `araucaria_registros` | int | nº de ocorrências no GBIF atribuídas ao município | idem |

Escrito por `scripts/mapear_araucaria_municipios.py --fundir`. Duas evidências, ambas municipais e
oficiais: **produção de pinhão** (IBGE/PEVS, tabela SIDRA 289 — prova araucária adulta produzindo
semente) e **ocorrência registrada** (GBIF, herbários brasileiros — prova indivíduo identificado em
campo, com o município resolvido por ponto-em-polígono na malha do IBGE, não pelo texto do
registro). `produtiva` = pinhão nas últimas 5 safras; `confirmada` = registro de 1995 em diante;
`historica` = só evidência anterior a isso.

⚠️ **`araucaria: false` significa `sem_evidencia`, não "não tem".** Coleta de herbário é esforço
amostral enviesado e nem toda araucária vira pinhão declarado. E nem `mtur_regiao` ("Rota das
Araucárias", "Rotas do Pinhão") nem o nome do município (Araucária/PR) são evidência botânica —
são marketing regional e toponímia. Regras completas, limites e fontes descartadas em
[RUNBOOK_ARAUCARIA.md](RUNBOOK_ARAUCARIA.md).

---

## Exemplo Completo

```javascript
{
  id: "goncalves_mg",
  nome: "Gonçalves",
  uf: "MG",
  regiao: "Sudeste",
  bioma: "mata_atlantica",
  clima: "frio",
  lat: -22.3,
  lon: -45.4,
  
  pop: 1100,
  idh: 0.695,
  ideb: 5.2,
  pib: 15000,
  temp: 14,
  
  mc: "neutro",
  desmat_ha_ano: 0,
  
  tipo: ["montanha", "turistica", "interior"],
  esportes: ["trekking", "escalada", "mountain_bike", "camping"],
  
  nota: "Município serrano no sul da Mantiqueira, ~1.100m de altitude. Neblina frequente no inverno, frio real de maio a agosto. Turismo rural e de aventura consolidado."
}
```

---

## Atualização de Dados

### Workflow de Atualização

```
[1] Check IBGE
    ↓
[2] python scripts/gerar_cidades.py
    ├─ Busca API IBGE localidades
    ├─ Busca IDEB (INEP)
    ├─ Busca PIB por estado
    └─ Gera cities_data.js
    ↓
[3] python scripts/enriquecer_desmatamento.py (se houver dados novos)
    ├─ Read: mapbiomas_desmat_municipio.xlsx
    └─ Merge em cities_data.js
    ↓
[4] python scripts/enriquecer_mc_munic.py (se houver mudanças)
    ├─ Recalcula risco climático
    └─ Merge em cities_data.js
    ↓
[5] Validação
    ├─ Conferir ~10 cidades aleatórias
    ├─ Testar scoring
    └─ Testar filtros
    ↓
[6] Commit e reload
```

### Passo 1: Regenerar com dados novos

```bash
# Terminal
cd scripts/
python gerar_cidades.py

# Verifica se rodou bem:
# - Output: ../cities_data.js (~7MB)
# - Mensagens: "Processando X cidades..." "Salvo com sucesso"
```

**Tempo estimado**: 2–5 minutos (depende da API IBGE)

### Passo 2: Enriquecer desmatamento

```bash
# Se há novo arquivo MapBiomas
python enriquecer_desmatamento.py

# Output:
# - desmat_por_municipio.json
# - cities_data.js atualizado
```

### Passo 3: Validação

```javascript
// No console do navegador

// Conferir total de cidades
console.log(window.CITIES_DB.length)  // deve ser ~5500–5570

// Conferir índices mínimos
const idhs = CITIES_DB.map(c => c.idh).sort((a,b) => a-b)
console.log('Min IDH:', idhs[0], 'Max:', idhs[idhs.length-1])
// Esperado: Min: ~0.50, Max: ~0.87

// Score de uma capital (ex: São Paulo)
const sp = CITIES_DB.find(c => c.id === 'sao_paulo_sp')
console.log('SP Score:', calcScore(sp))
// Esperado: ~70–80 pontos

// Conferir desmatamento (algumas cidades)
const comDesmat = CITIES_DB.filter(c => c.desmat_ha_ano > 0)
console.log('Cidades com desmatamento:', comDesmat.length)
// Esperado: 20–200 (depende do MapBiomas)
```

---

## Versionamento de Dados

Manter um histórico:

```
cities_data.js (current)
├─ cities_data_2026-06-23_backup.js
├─ cities_data_2026-06-16_backup.js
├─ cities_data_2026-06-09_backup.js
└─ ...
```

**Razão**: Fácil reverter se houver erro de atualização.

---

## FAQ sobre Dados

### P: Onde vem cada dado?

| Dado | Fonte | Frequência |
|------|-------|-----------|
| Municípios (ID, UF) | IBGE API | Fixa |
| População | IBGE Censo 2022 | Anual (próximo 2030) |
| IDH | PNUD + IBGE Censo 2022 | ~2 anos |
| IDEB | INEP/Ideb (Anos Iniciais, rede pública) | **Bienal** (~ago do ano seguinte ao Saeb) |
| PIB per capita | IBGE — PIB dos Municípios (indicador 47001) | Anual (~dez, defasagem ~3 anos) |
| Temperatura | Histórico climático INMET | Fixa (média) |
| Desmatamento | MapBiomas | Anual (2–3 anos depois) |
| Renda per capita (`renda_pc`) | IBGE Censo 2022 (SIDRA tabela 10295) | ~10 anos (próximo censo) |
| Desemprego (`desemprego_pct`) | IBGE Censo 2022 (SIDRA tabela 6580) | ~10 anos (próximo censo) |

### P: Por que nem todas cidades têm IDEB?

O IDEB só existe onde há escola pública avaliada pelo Saeb com participação suficiente. Na safra
2025, rede pública, são **93 municípios sem valor** (eram 251 pela rede Municipal, e 613 na safra 2023).

**Fallback**: constante regional `IDEB_REG` (Norte 4,5 · Nordeste 4,8 · Centro-Oeste 5,5 ·
Sudeste 5,8 · Sul 6,2), com `ideb_status='fallback_regional'` — sempre marcado como tal na UI, e
com peso amortecido no score. **Não** é a nota da capital.

### P: O PIB é real ou estimado?

**Real, para os 5.570 municípios.** O IBGE publica PIB per capita municipal (pesquisa 38,
indicador 47001) e é isso que está no campo — safra **2023**, com `pib_ano` carimbado da fonte.

A resposta anterior aqui dizia "estimado para ~5.200 municípios, por rateio populacional, com
imprecisão de 200–300%". Era descrição de um método que o código **nunca** usou.

### P: Como é o risco climático?

Por estado (simplificado):

1. **Amazônia** (AC, AM, AP, PA, RO, RR) → risco_desmatamento
2. **Cerrado** (DF, GO, MS, MT, TO) → risco_seca
3. **Caatinga** (AL, BA, CE, PB, PE, PI, RN, SE) → risco_seca
4. **Litoral** (cidades praia) → risco_inundacao
5. **Sul** (PR, RS, SC) → risco_inundacao
6. **Sudeste** (ES, MG, RJ, SP) → neutro

**Refinamento futuro**: Usar dados municipais de risco climático (IPCC, SEEG).

### P: Posso fazer download dos dados?

Sim! `cities_data.js` é um arquivo JSON (após remover `window.CITIES_DB = `).

```bash
# Extract pure JSON from cities_data.js
cat cities_data.js | sed 's/^window\.CITIES_DB = //; s/;$//' > cities.json

# Agora é um JSON válido para importar em qualquer ferramenta
```

---

**Last updated**: 2026-06-23
