# SIRENE — Plataforma Integrada de Emergências Multiagência

Frontend do sistema de **regulação de chamados de emergência multiagência**
(Polícia 190 · SAMU 192 · Bombeiros 193 · Defesa Civil 199), construído sobre o
**template padrão iPadOS ("IPAD")** — design system Apple/iPadOS com tema claro/escuro.

## Manual do sistema

O manual operacional e de instalação está em [`manual/`](manual/) e dentro do sistema (menu **Sistema → Manual do
sistema**), em três blocos: **implantação** (instalação, servidor da nuvem, servidor local espelho, bancos de dados,
status, ajustes), **operação** (ciclo da ocorrência etapa por etapa, comunicação com o solicitante, aplicativos,
recursos e frota, rede de destinos, estoque e farmácia, painel do plantão, videowall) e **administração**
(segurança, perfis, auditoria, backup), além do **histórico de alterações** (`manual/99-historico.md`).
O índice fica em `manual/manual.json`. Regra do projeto: **toda mudança no sistema atualiza o capítulo afetado e o
histórico.**

## Requisitos e arquitetura-alvo

A pasta [`Requisitos/`](Requisitos/README.md) consolida a especificação: funcionalidades (RF, MoSCoW, Gherkin),
integrações, fluxos do usuário (Mermaid), arquitetura de banco de dados, arquitetura do código (C4, hexagonal,
eventos) e requisitos não funcionais (segurança OWASP ASVS, LGPD, DR, regulatório).

## Como executar (sistema completo)

Requisitos: **Node.js ≥ 22.5**, conexão para as bibliotecas de CDN (Font Awesome, Leaflet, Chart.js, CesiumJS)
e tiles de mapa. Persistência em **PostgreSQL 16** (produção, três clusters via Docker) ou **SQLite embutido**
(`node:sqlite`, sem serviços externos, ideal para desenvolvimento).

```bash
cd /Users/thiagobissoli/Sirene
npm install          # instala o driver `pg` (única dependência)
./start.sh           # bancos (Docker) + servidor da nuvem; imprime os endereços de acesso
./start.sh status    # o que está no ar · ./stop.sh para tudo
```

Sem Docker: `./start.sh sqlite` (ou `npm start`). Servidor local espelho: `./start.sh local` (ver manual, cap. 3).

Com PostgreSQL (recomendado — é a arquitetura-alvo do documento 04):

```bash
npm run db:up        # docker compose: pg-geral :5432 · pg-geo :5433 (TimescaleDB + PostGIS) · pg-logs :5434
npm run db:migrate   # opcional: copia os dados já existentes do SQLite para os três clusters
npm run start:pg     # servidor com SIRENE_DB=postgres (URLs padrão sirene/sirene@localhost)
npm run loadtest     # teste de carga (50 k entradas/dia, pico 10×, burst 50/s) — ver Requisitos/07
```

Abrir <http://localhost:8080> → tela de login. **Primeiro acesso:** `admin` / `Sirene@2026` (troca de senha
obrigatória em Ajustes → Conta). Depois que o sistema salva os dados de demonstração, qualquer usuário de
**Administração → Usuários** entra com a senha padrão `Sirene@2026` (ex.: `thiago.bissoli`, `ana.souza`).

O que o servidor ([`server/server.js`](server/server.js)) faz:

- **Persistência** (`server/db/`, dois drivers com o mesmo contrato): **PostgreSQL em 3 clusters por perfil
  de carga** — *geral* (registros versionados em JSONB, config, senhas scrypt, sessões, tickets), *geo*
  (telemetria em hypertable TimescaleDB com PostGIS + última posição por viatura) e *logs* (auditoria
  append-only particionada por mês com **cadeia de hash SHA-256** e trava consultiva) — ou **SQLite** em
  `server/data/sirene.db` (WAL) com as mesmas tabelas. Variáveis: `SIRENE_DB`, `SIRENE_PG_URL`,
  `SIRENE_PG_GEO_URL`, `SIRENE_PG_LOGS_URL`, `SIRENE_PG_POOL`, `SIRENE_HISTORICO_HORAS`.
- **Autenticação JWT** (HS256, segredo gerado em `server/data/secret.key`), expiração configurável, bloqueio
  progressivo após tentativas (rate limit), revogação de sessões, troca obrigatória de senha no 1º acesso.
- **Autorização por perfil** (matriz módulo × nível salva em Administração → Perfis), também aplicada no
  servidor: escrita em `admin`/`config` exige nível de administração; backup/restauração exigem total.
  Cada coleção aceita escrita de um conjunto de módulos (ex.: `ocorrencias` ← entrada, regulação,
  despacho, contra-regulação, transporte; `fleet` ← recursos, despacho e a equipe embarcada). Quem não
  tem edição geral em Ocorrências só grava registros cuja **etapa** pertence ao seu módulo (a equipe
  grava cena/transporte/encerramento, nunca a triagem); registros negados voltam em `negados` e o
  cliente avisa "Alteração não gravada".
- **API**: `/api/auth/*`, `/api/state[/:colecao]`, `POST /api/state/:colecao/batch` (ops por registro),
  `GET /api/events` (SSE), `/api/audit`, `/api/audit/verify`, `/api/backup`,
  `/api/backup/restore`, `/api/config` (público, sem segredos), `/api/health` (estado dos três clusters,
  integridade da auditoria), `/api/metrics` (latência p50/p95/p99 por rota), **telemetria**
  `POST /api/telemetria` · `GET /api/telemetria/ultimas` · `GET /api/telemetria/:viatura?desde=`, e a
  **API pública do cidadão** `/api/public/*` (sem login, protegida por *ticket* do chamado e limite por IP).
- **Sincronização em tempo real entre operadores** ([`js/realtime.js`](js/realtime.js)): cada registro
  (ocorrência, viatura, local, equipamento, lote, requisição, usuário…) é guardado **individualmente e
  versionado** no servidor (tabela `records`, `_v`); o frontend envia a cada 2 s apenas os registros que
  mudaram (upsert/delete) com a versão-base — **bloqueio otimista**: se outro operador salvou antes, o
  servidor devolve conflito, a versão do servidor é aplicada e o operador é avisado. O servidor difunde as
  mudanças por **Server-Sent Events** (`/api/events`) e cada tela aplica os registros em memória e se
  re-renderiza; reconexão faz *resync* completo; a barra de status mostra os **operadores online**.
  Configurações e políticas (blobs) também são versionadas e difundidas.
- Sem servidor (ex.: abrindo por outro HTTP estático) o app entra em **modo demonstração** em memória.

Outros comandos: `npm run dev` (reinicia ao alterar o servidor), `npm run backup` (SQLite: cópia em
`server/backups/`; PostgreSQL: `pg_dump` dos três clusters), `npm run check` (verificação de sintaxe),
`npm run rotas` (regera rotas embarcadas), `npm run db:down` (para os clusters; os volumes permanecem),
`node scripts/verify-audit-pg.js` (verificação independente da cadeia de auditoria no cluster de logs),
`npm run cert` (certificado autoassinado → HTTPS em :8443 para os apps no celular).
Variáveis: `PORT`, `HOST`, `SIRENE_DATA_DIR`, `SIRENE_JWT_SECRET`, `SIRENE_DEFAULT_PASSWORD`, `SIRENE_PUBLIC_RPM`,
`SIRENE_HTTPS_PORT`, `SIRENE_TLS_CERT`, `SIRENE_TLS_KEY`, `SIRENE_UPSTREAM_URL`, `SIRENE_UPSTREAM_TOKEN`,
`SIRENE_SYNC_SEG`, `SIRENE_CORS_ORIGINS`. Servidor local espelho: `npm run start:local` (porta 8090, dados em
`server/data-local`); token de serviço: `npm run token:servico`.

**Capacidade** (Requisitos/07): 50 entradas/s em burst e 6/s sustentadas com p95 de 13 ms, 20 operadores
gravando em paralelo e 50 viaturas a 1 Hz, 0 erros, auditoria íntegra — muito acima dos 50 mil/dia exigidos.

## Ajustes (todas as configurações)

Menu **Sistema → Ajustes** (`#ajustes`, [`js/ajustes.js`](js/ajustes.js), padrões em [`js/config.js`](js/config.js)),
salvo na coleção `config`: **Conta** (senha, sessão, sair) · **Geral** (nome da central, subtítulo, barra de
status, município/UF/fuso, prefixo do nº) · **Agências** (nome, telefone, cor, ativa) · **Turnos e vínculos**
(início diurno/noturno, escala, checklist obrigatório, bloqueio de despacho, política de vínculo por agência) ·
**Catálogos** (naturezas com ícone/agências/prioridade — geram os chips da entrada; hipóteses × risco por
agência — usadas na regulação; prioridades) · **Operação** (censo atrasado, retenção, tempo em cena, IMV,
transferência obrigatória, classificações de chamada) · **Estoque** (FEFO, requisição automática, lacre,
dupla conferência, validade) · **Mapas e videowall** (OSRM/Valhalla/tiles/satélite, centro, escala de tempo,
drone, sensor e câmera padrão, paginação do censo) · **Integrações** (telefonia, AML, Sinesp, FHIR, MQTT, SMS,
push, STT, CAP — endpoints, chaves mascaradas, ativa) · **Segurança** (MFA, expiração, tentativas, bloqueio,
senha mínima/expiração, IPs, retenção da auditoria — aplicado pelo servidor) · **Notificações** ·
**Aparência** (tema, densidade) · **Dados e backup** (exportar/restaurar backup completo, restaurar dados de
demonstração) · **Sobre**.

## Estrutura (template iPadOS)

```
Sirene/
├── index.html            App shell + views (Painel, Ocorrências, Recursos, Hospitais, Estoque, Relatórios, UI Kit)
├── graficos/formularios/tabelas/botoes/cards/calendarios/mapas/ajustes.html   Páginas do design system
├── login.html / cadastro.html / recuperar.html                                Autenticação
├── css/                  Design system modular (variables, ipad, sidebar, cards, tables, maps, ...)
├── js/                   ES modules (app → store → boot; um módulo por contexto)
├── server/               server.js (API + SQLite + JWT + auditoria + backup) · data/ (banco, segredo)
├── scripts/              gen_rotas.py (rotas embarcadas) · backup.js (cópia do banco)
└── Requisitos/           especificação (RF, integrações, fluxos, banco, código, RNF)
```

O shell é montado por `js/shell.js` a partir de `js/nav-config.js` (navegação e títulos).
As telas do `index.html` são `<section data-view-panel="…">` alternadas pela sidebar.

## Adaptações para o SIRENE (multiagência)

Sobre o template original (SAMU, agência única) foram aplicados:

- **Marca:** `SIRENE · Emergências Multiagência` (sidebar), status `Central Integrada · Vitória/ES`.
- **Navegação/títulos** (`js/nav-config.js`): Painel = *Central Integrada · 190·192·193·199*; "Ambulâncias" → **Recursos**; seção "Páginas" do template removida do menu (arquivos mantidos).
- **Painel** (`index.html`): alerta de P0 multiagência, KPIs (chamados 190/192/193/199,
  recursos empenhados, TR P90, fila) e feed **Atividade** com as quatro agências.

## Elementos específicos por agência (Polícia · Bombeiros/Salvamento · Defesa Civil)

Além da regulação já ser *agency-aware*, os três momentos ganham campos próprios de cada agência:

- **Entrada** (passo Natureza) — **determinantes específicos** por agência acionada:
  Polícia (arma, agressor no local, reféns, medida protetiva…), Bombeiros (pessoas presas,
  vítima em altura/água, produto perigoso, risco de explosão…), Defesa Civil (desabrigados,
  risco iminente, encosta instável, evacuação…).
- **Regulação** — card **Detalhes operacionais**: Polícia (consultas a bases, nível de risco,
  apoio Força Tática/ROTAM/GATE, Sinesp), Bombeiros (composição do **trem de socorro**, nº ONU
  de produto perigoso, apoio de concessionárias), Defesa Civil (nível de alerta **CAP**, canais
  de alerta — cell broadcast/sirenes/SMS 40199/app, abrigos/S2iD).
- **Contra-regulação** — card **Desfecho operacional**: Polícia (detidos, armas apreendidas,
  BO/TCO), Bombeiros (situação — controlado/extinto/rescaldo, vítimas resgatadas, perícia),
  Defesa Civil (desabrigados/desalojados, abrigos, interdição, vistoria estrutural).

## Protocolos e referências de destino

- **Protocolos** (`#protocolos`, [`js/protocolos.js`](js/protocolos.js)): registro versionado dos protocolos de
  **entrada (TARM)**, **regulação** e **contra-regulação/cena**, por agência e gatilho (natureza / hipótese),
  com passos tipados (pergunta-chave, instrução, regra de decisão, ponto crítico obrigatório), instruções
  pré-chegada/à equipe e critérios de destino; CRUD, nova versão (arquiva a anterior), arquivar, histórico e
  auditoria. **Aplicação nas etapas:** card "Protocolo de triagem" na entrada (ao escolher a natureza),
  "Protocolo de regulação" (agora vindo do registro, também por hipótese) e "Protocolo de cena" na
  contra-regulação — a aderência (passos cumpridos, críticos pendentes, versão) fica em `caso.protocolos[etapa]`.
- **Referências de destino** (`#referencias`, [`js/referencias.js`](js/referencias.js)): rede pactuada por
  **linha de cuidado** (derivada da hipótese diagnóstica/natureza: trauma grave, SCA/IAM, AVC, PCR, pediatria,
  obstetrícia, queimados, saúde mental, clínica, intoxicação; PM: flagrante, violência contra a mulher, custódia;
  CB: resgate; DC: desabrigados) e **município/região** (regra do município ou regional padrão), com destinos
  ordenados (1ª referência, retaguarda, contra-referência), condições, pactuação e vigência; CRUD, simulador e
  alertas (linhas sem referência, destinos não cadastrados, pactuações vencendo). **Uso:** a regulação destaca
  e ordena as unidades próximas pela referência; a contra-regulação ordena/rotula o destino de cada vítima
  (★ 1ª referência · condição · situação do censo) e mostra a linha e o município da regra aplicada.

## Administração — usuários, perfis de acesso, auditoria e backup

Seção **Administração** do menu ([`js/admin.js`](js/admin.js)), registro `window.__SIRENE_ADMIN`:

- **Usuários** (`#usuarios`): cadastro (nome, login, matrícula, e-mail, telefone, agência, **perfil**,
  unidade/lotação), status (ativo, bloqueado, inativo, aguardando ativação), **MFA**, KPIs, filtros por
  agência/perfil/status/sem MFA, busca; ações **editar, bloquear/desbloquear, resetar senha**
  (temporária com troca obrigatória), **encerrar sessões**, **desativar** (soft delete) e histórico das
  ações do usuário; painel de **sessões ativas** (dispositivo, IP, início) com encerramento.
- **Perfis de acesso** (`#perfis`, RBAC): 9 perfis prontos (Administrador, Supervisor de sala, Atendente
  TARM, Especialista/médico regulador, Controlador de frota, Equipe de intervenção, Farmácia, Gestor
  hospitalar, Auditor) com **matriz módulo × nível** (nenhum · leitura · edição · total → ver / criar-
  editar / excluir-aprovar) e **escopo** (própria agência / todas); clonar, editar, excluir perfil
  (bloqueado se em uso) e **"Testar este perfil no menu"** — o sidebar passa a mostrar só os módulos
  permitidos. Toda alteração vai para a auditoria.
- **Auditoria e log** (`#auditoria`): trilha **imutável com cadeia de hash** (cada evento guarda o hash
  do anterior; **Verificar integridade** recomputa a cadeia e acusa adulteração — testado), eventos de
  login/falha/logout, criação/edição/exclusão, despacho, exportação, configuração, backup/restauração e
  **acessos negados**; filtros por usuário, módulo, ação, resultado e busca; **exportação CSV**.
  API global `window.__SIRENE_AUDIT.log({acao, modulo, alvo, detalhes})` para os demais módulos.
- **Backup** (`#backup`): **política** (completo diário, incremental a cada N h, retenção, destino S3,
  réplica secundária, cifra AES-256), lista de **execuções** com tamanho, duração, status e checksum
  (com falha reagendada de exemplo), **backup agora** (com progresso), **restauração** com escopo e
  confirmação "RESTAURAR" (cria backup de segurança e encerra sessões), e **snapshot JSON** dos dados em
  memória — exportar (download) e importar/restaurar (usuários, perfis, locais, equipamentos, ocorrências).

## Estoque e farmácia — gestão completa

View **Estoque e farmácia** (`#estoque`, [`js/estoque.js`](js/estoque.js)), registro `window.__SIRENE_ESTOQUE`
(`itens`, `lotes`, `estoques`, `mov`, `reqs`, `pedidos`, `cc`). Modelo:

- **Estoques hierárquicos:** **Farmácia Central** e **farmácia satélite**; em cada ambulância, a
  **prateleira** e as **bolsas** (vias aéreas, medicamentos, controlados, trauma; mochila APH na
  motolância; bolsa aeromédica). **Cada bolsa é um estoque individual**, com **lacre**, cuja
  **localização é a própria viatura ou a farmácia** (retirada para reposição/conferência — ex.: USB-07
  em manutenção tem as bolsas na Farmácia Central). Todo estoque tem **centro de custo** e **farmácia
  vinculada** (USB-12 → satélite Carapina).
- **Itens e lotes:** catálogo de medicamentos (incl. **controlados** — Portaria 344), materiais e
  insumos com unidade, valor e níveis mín/máx por tipo de estoque; cada **lote** nasce na entrada por
  NF (fornecedor, NF, validade) e é a unidade de rastreio. Saídas sempre por **FEFO**.
- **Consumo na ambulância** ("Registrar consumo"): baixa a bolsa/prateleira por lote, vincula ao
  **paciente e à ocorrência** e **gera automaticamente a ficha de requisição** na farmácia vinculada
  àquela viatura (uma ficha aberta por viatura agrupa os consumos).
- **Fluxo de reposição:** requisição **aberta** → **separada** na farmácia (lotes FEFO, parcial se
  faltar saldo) → **dispensada** (transfere os lotes para a bolsa/prateleira) → **conferida** na
  viatura com **novo lacre** das bolsas. "Repor ao mínimo" gera requisições por nível mínimo.
- **Compra:** farmácia abaixo do mínimo → **pedidos de compra** por fornecedor → **Entrada por NF**
  (cria o lote, dá entrada e baixa o pedido). Perda/descarte (vencido, avaria, contaminação, extravio).
- **Rastreio ponta a ponta:** por lote, item, NF ou fornecedor — linha do tempo fornecedor → farmácia →
  requisição → bolsa/prateleira da viatura → consumo (ocorrência · paciente) e **onde o lote está agora**.
- **Centros de custo:** estoques vinculados, valor em estoque, consumo do mês × orçamento, requisições.
- **Painel:** KPIs (estoques, bolsas/prateleiras, abaixo do mínimo, lotes vencendo ≤ 30 d, requisições
  em aberto, valor em estoque), abas Estoques · Itens e lotes · Requisições e pedidos · Movimentações ·
  Rastreio · Centros de custo; ficha de cada estoque com itens/níveis/lotes e ações (consumo,
  requisitar, retirar/devolver bolsa, conferir e lacrar, perda).

## Equipamentos — gestão patrimonial e operacional

View **Equipamentos** (`#equipamentos`, [`js/equipamentos.js`](js/equipamentos.js)), registro
`window.__SIRENE_EQUIP`: desfibriladores, monitores, ventiladores, bombas, cilindros, EPR, kit de
desencarceramento, rádios, coletes, etilômetro, câmeras corporais, tablets, drone de vistoria…

- **Cadastro:** nome, categoria (médico-hospitalar, comunicação, proteção/EPI, resgate, armamento,
  medição/vistoria, TI), agência, modelo, fabricante, responsável, **patrimônio e nº de série**,
  aquisição, valor, **calibração**, **validade**, **próxima preventiva**, status (Operacional, Em uso,
  Manutenção, Calibração/teste, Emprestado, Extraviado, Baixado) com motivo, observações, baixa.
- **Localização e movimentação:** onde o equipamento está — **viatura**, **base/almoxarifado**,
  **oficina/assistência**, **unidade de saúde (empréstimo)** ou **com profissional** — com transferência
  rastreada (de → para, hora, responsável). **Integração com a frota:** retirar um equipamento da
  viatura marca o item como **em falta no checklist** da viatura; devolver restaura.
- **Manutenção e calibração:** ordens de serviço (preventiva, corretiva, calibração, teste
  funcional, bateria, firmware) com oficina/laboratório, previsão e custo; "enviar à oficina agora"
  move o equipamento e muda o status; **concluir OS** devolve ao local de origem, renova calibração
  (+1 ano) ou preventiva (+6 meses) e libera o equipamento.
- **Painel:** KPIs (total, operacionais, manutenção/calibração, extraviados/emprestados, valor
  patrimonial), alerta de pendências (calibração/validade/preventiva vencendo, OS aberta, extraviado),
  filtros por agência, categoria e status (inclui "Em viatura" e "Com pendências"), busca, tabela com
  datas destacadas, **Inventário do turno** e histórico por item.

## Locais (gestão completa de destinos e apoios)

`#locais` ([`js/locais.js`](js/locais.js)) — catálogo gerenciado de **destinos e apoios operacionais**
(**Hospitais, UPA, UBS, Delegacias, Presídios, Abrigos, Bases**), registro `window.__SIRENE_LOCAIS`:

- **Painel:** KPIs (locais, hospitais/UPA com vaga, superlotados/fechados, abrigos ativados, vagas em
  abrigos), alerta de locais com restrição, filtros por tipo, **município** e situação (Recebendo,
  Superlotados, Fechados, Abrigos ativados, Com restrição), busca por nome/endereço/bairro/serviço.
- **Card:** tipo, endereço/bairro/município, distância/tempo, situação, horário, telefone,
  **medidor de capacidade** (leitos, vagas, custódia, viaturas… conforme o tipo; hospitais também
  sala vermelha e UTI) e serviços/especialidades. Ações: Editar · Capacidade/situação · Contatos.
- **Ficha (modal com abas):** **Dados** (nome, tipo, rede, município, horário, endereço, bairro,
  lat/lng com **mapa Leaflet e marcador arrastável**, capacidade total, observações, remoção com
  confirmação); **Capacidade e situação** (ocupados/total com +1/−1, sala vermelha/UTI, e **situação
  operacional**: Aberto, Fluxo alto, Superlotado, Fechado, Interditado, Ativado/Desativado para abrigos —
  motivo obrigatório para superlotação/fechamento/interdição); **Contatos e serviços** (telefones,
  responsável, e-mail, agências que o usam como destino, checklist de especialidades por tipo);
  **Histórico** (linha do tempo de ocupação, situação, contatos e cadastro).
- **Fonte única de destinos:** as unidades próximas da **regulação**, os destinos por vítima da
  **contra-regulação**, o redirecionamento no **transporte** e a distribuição do **IMV** consomem
  `destinosPorAgencia()` / `unidadesSaude()` (SAMU → hospital/UPA/UBS · Polícia → delegacia/presídio ·
  Bombeiros → hospital/UPA/abrigo · Defesa Civil → abrigo). Um local **fechado, interditado ou
  desativado deixa de ser oferecido** como destino; um local novo entra imediatamente (verificado).

## Censo hospitalar — painel estilo aeroporto (split-flap)

View **Censo hospitalar** (`#censo`, [`js/censo.js`](js/censo.js), [`css/censo.css`](css/censo.css)) e
página em tela cheia para videowall [`censo.html`](censo.html) (`F` = tela cheia, paginação
automática a cada 12 s, relógio). Uma linha por **hospital, UPA e UBS** com hora do último censo,
unidade/bairro/município, tipo, **leitos ocupados/total** (barra), **vagas**, **sala vermelha**, **UTI**,
**tempo de espera**, **fila**, **situação** (RECEBENDO · FLUXO ALTO · SUPERLOTADO · FECHADO ·
INTERDITADO · CENSO ATRASADO) e tendência. **As letras giram (split-flap) quando um valor muda** —
como nos painéis de aeroporto. KPIs: leitos livres, recebendo, superlotados, fechados, sala vermelha,
UTI livres, censo atrasado (> 2 h). **Tema claro/escuro:** o painel usa tokens `--cs-*` que
seguem o `data-theme` do template; `censo.html` respeita o tema salvo e tem botão/tecla `T` para alternar.

- **Gestão do censo:** clique na linha → modal com leitos totais/ocupados, sala vermelha, UTI, espera,
  fila, responsável, situação declarada (observação obrigatória para superlotação/fechamento/
  interdição), **Confirmar censo** (atualiza o painel, o histórico e a oferta como destino) e
  **Solicitar atualização à unidade**.
- **Ao vivo:** simulação de atualizações a cada 7 s (pausável); filtros por tipo e município,
  ordenação por mais vagas / menor espera / situação / A–Z, busca.
- Mesmos dados de **Locais** (`LOCAIS`): o censo altera capacidade/situação usadas pela regulação,
  contra-regulação, transporte e IMV.

## Entrada de chamado — fila de chamados (`#atendimento`) e registro (`#entrada`)

O item **Entrada de chamado** do menu abre a **fila de chamados** ([`js/atendimento.js`](js/atendimento.js),
[`css/atendimento.css`](css/atendimento.css)); o formulário de registro (`#entrada`) é aberto ao atender um chamado
da fila ou por "Registrar sem chamada", e tem um *breadcrumb* de volta à fila.
Todo chamado que chega na etapa de **entrada** sem atendente — pelo **app do cidadão** (`POST /api/public/chamado`)
ou pela telefonia (há um botão "Simular chamada telefônica" enquanto não existe CTI) — entra na fila e **toca em
qualquer tela do sistema**: campainha (Web Audio, liberada no primeiro clique da sessão), aviso, botão flutuante
"N chamados aguardando · atender", selo piscante no menu e contador no título da aba.

- Cartão por chamado: canal, prioridade, natureza, endereço/GPS, solicitante e telefone, descrição ou última
  mensagem do chat, **tempo de espera** ao vivo, P0 em destaque; status do atendente (Disponível / Em pausa) e som
  ligado/desligado; KPIs (na fila, maior espera, em atendimento, atendidos por mim hoje); atendimentos de outros
  atendentes e o histórico do dia.
- **Atender** registra quem atendeu e quando (`atendimento`, `atores.entrada`, `tempos.atendimento`), envia ao
  cidadão a mensagem "assumiu seu chamado" e abre a **Entrada de chamado já preenchida** (canal, endereço, bairro,
  cidade, solicitante, natureza, prioridade, GPS, telefone e descrição no cabeçalho da chamada). Ao concluir a
  entrada a ocorrência **mantém o mesmo número** e segue para a regulação com chat, dados do app e carimbos
  preservados; "Descartar" devolve o chamado à fila; um desfecho sem ocorrência (trote, engano…) encerra o chamado.

## Filas por etapa — cada etapa é de um usuário diferente

A entrada, a regulação, o despacho, a contra-regulação e o transporte são feitos por usuários diferentes. Por
isso **cada item do menu abre a fila da sua etapa** e o formulário só aparece depois que o usuário seleciona
(assume) uma ocorrência ([`js/fila-etapa.js`](js/fila-etapa.js); o despacho usa a fila nativa da própria
tela, [`js/despacho.js`](js/despacho.js), agora derivada das ocorrências na etapa `despacho` mais os pedidos
de apoio da cena):

| Menu | Fila | Ação | Formulário |
|---|---|---|---|
| Entrada de chamado | chamados sem atendente (app, telefonia) | Atender | registro da entrada |
| Regulação | ocorrências na etapa `regulacao` | Regular | regulação do caso |
| Despacho | ocorrências na etapa `despacho` + apoios | selecionar na fila | empenho do recurso |
| Contra-regulação | ocorrências na etapa `contrarregulacao` | Assumir a cena | avaliação e destino |
| Transporte | ocorrências na etapa `transporte` | Acompanhar transporte | transporte e encerramento |

Em todas: P0 primeiro e depois ordem de chegada; canal, local, solicitante, recurso, de quem veio o handoff,
espera ao vivo e **quem está com o caso** (`responsaveis[etapa]`, auditado ao assumir; assumir o caso de outro
operador pede confirmação); seções "Comigo", "Aguardando" e "Com outros operadores"; selo no menu com a
contagem; breadcrumb e botão para voltar à fila. Chegar por handoff, hub de ocorrências ou painel abre o caso
diretamente.

Na **Contra-regulação**, a fila coloca no topo (piscando, com selo no menu) as ocorrências cuja equipe pediu
contra-regulação pelo app; ao abrir o caso, o painel **Equipe no local** mostra a linha do tempo da equipe
(despacho → ciente → deslocamento → no local → contra-regulação), os registros por vítima (estado, sinais
vitais, procedimentos, evolução) e o formulário **Responder à equipe** (decisão, destino pela referência da
região, orientações). A resposta vai ao app e ao chat, aplica o destino às vítimas e fica auditada.

**Vídeo, chat e áudio acompanham a ocorrência selecionada**: o dock de comunicação (áudio · vídeo · chat com o
cidadão, WebRTC) abre em qualquer etapa assim que o usuário seleciona uma ocorrência em andamento, e fica
oculto enquanto a fila está na tela.

## Redundância — nuvem, servidor local e operação sem conexão

O SIRENE roda na **nuvem** e continua operando se a internet da central cair, em três camadas:

1. **Servidor local (espelho)** na central: `npm run start:local` com `SIRENE_UPSTREAM_URL` apontando para a
   nuvem, um **token de serviço** (`npm run token:servico`, assinado com o mesmo `SIRENE_JWT_SECRET` da nuvem)
   e, opcionalmente, o mesmo PostgreSQL ou o SQLite embutido. Ele **puxa** da nuvem tudo o que muda (registros,
   configurações, sessões e tickets) e **grava toda escrita local numa fila durável (outbox)** que é enviada à
   nuvem assim que houver conexão. As sessões replicadas fazem o login feito na nuvem valer no servidor local.
   Conflitos: **última escrita vence** pelo instante de origem; registros idênticos são ignorados; a auditoria
   não é replicada (cada servidor mantém a sua própria cadeia de hash). Implementação em
   [`server/replica.js`](server/replica.js) e nas rotas `POST /api/sync/push`, `GET /api/sync/pull`,
   `GET /api/sync/estado`.
2. **Troca automática de servidor no navegador**: em Ajustes → Redundância informe os servidores
   alternativos (ex.: a nuvem e `http://192.168.0.15:8090`). Se o servidor atual parar de responder (duas
   verificações de saúde seguidas ou um erro de rede ao gravar), o app procura um servidor que responda, valida a
   sessão e **passa a operar por ele** sem recarregar a tela, enviando o que ficou pendente sem exigir a
   versão-base (as versões diferem entre servidores). O servidor libera CORS para as origens configuradas
   (`SIRENE_CORS_ORIGINS` ou a própria lista de Ajustes).
3. **Cache local no navegador (IndexedDB)**: a cada 5 s o app guarda o estado atual e o snapshot da última
   sincronização. Sem nenhum servidor, o app **abre a partir do cache** (com o usuário já autenticado), continua
   operando, conta as **alterações pendentes** e tenta reconectar a cada 6 s; ao reconectar, envia as pendências,
   traz o que mudou no servidor e grava o cache de novo.

A barra de status deixou de ser um rótulo fixo: [`js/saude.js`](js/saude.js) mostra o estado **medido** —
servidor (nuvem ou local), latência, bancos, tempo real (SSE), fila da replicação, alterações pendentes e a
**disponibilidade medida na sessão** (tempo conectado sobre tempo total). Verde = tudo ok; amarelo = degradado
(SSE reconectando, banco com falha, latência alta, nuvem indisponível a partir do servidor local); vermelho =
sem servidor, operando localmente. O tooltip detalha cada item e um clique mede na hora.

Teste realizado: réplica local sincronizada com a nuvem (573 registros nos dois lados); chamado criado no
servidor local apareceu na nuvem em segundos; com a nuvem derrubada, o servidor local seguiu atendendo e
enfileirou as escritas; com a nuvem de volta, a fila foi drenada e uma alteração feita na nuvem chegou ao
servidor local; no navegador, a queda da nuvem provocou a troca automática para o servidor local, a queda dos
dois levou ao modo sem servidor com 1 alteração pendente, e a volta reconectou à nuvem com as duas mensagens
gravadas e a fila zerada.

## Painel do plantão (`#dashboard`)

Página inicial reformulada como **painel operacional do plantão** ([`js/painel.js`](js/painel.js),
[`css/painel.css`](css/painel.css)), calculada em tempo real a partir do estado sincronizado e re-renderizada
a cada mudança remota (SSE) e a cada 30 s. Escopo selecionável: **Plantão** (desde o início do turno
configurado em Ajustes → Turnos), **24 h** ou **7 dias**.

- **Alertas acionáveis** (clique abre o módulo): P0 ativas, IMV em andamento, ocorrências sem recurso há
  mais de 10 min, chamados do app sem resposta no chat, unidades superlotadas/fechadas, viaturas sem
  checklist do turno, disponíveis sem tripulação, fora de operação, censo desatualizado, lotes vencidos e
  vencendo em 30 dias, itens abaixo do mínimo, requisições em aberto, equipamentos com manutenção/calibração
  vencida ou fora de operação, cadeia de auditoria inconsistente, servidor desconectado.
- **Indicadores do plantão**: ocorrências ativas (P0/P1/IMV), entradas e encerramentos no escopo, **tempo de
  resposta despacho → cena** (média, p90 e % dentro da meta), ocupação da frota; **fluxo por etapa** (funil
  entrada → regulação → despacho → cena → transporte) e **tempos médios por transição** (triagem, regulação,
  resposta, cena, ciclo total).
- **Indicadores operacionais** (manual, cap. Painel do plantão): tempos por etapa com média/p90, tempo-resposta por
  prioridade e % na meta, assertividade (prioridade mantida, recurso adequado, sem retrabalho, destino mantido,
  protocolos, apoio, cancelamentos, avaliação do cidadão), volume (chamados, conversão, encerradas, canal, agência)
  e tipo de atendimento (naturezas, desfechos, recursos).
- **Gráficos**: entradas por hora/dia, ativas por agência, naturezas, desfechos, frota por status (Chart.js).
- **Listas**: ocorrências ativas por prioridade (abre a etapa atual), frota agora (chip por viatura com
  ocorrência e checklist pendente), rede de urgência (leitos, situação, espera, idade do censo),
  prioridades × agências, últimas ações da auditoria, operadores online.
- **Meu plantão (indicadores individuais)**: em serviço desde / expiração da sessão, pendências nas etapas do
  meu perfil (derivadas da matriz de permissões), ocorrências ativas sob minha responsabilidade, ocorrências
  que passaram por mim, ações registradas na auditoria, atalhos conforme o perfil (requisições da farmácia,
  checklists da frota, censos a atualizar).

Base dos tempos: o servidor **carimba em cada ocorrência o instante e o operador de cada mudança de etapa**
(`tempos.{etapa}` e `atores.{etapa}`, além de `criadoEm`) em toda gravação — inclusive pelo app do cidadão e
pelo app da equipe — o que alimenta os indicadores de resposta e a atribuição individual.

## Painel de Ocorrências (hub do fluxo)

`#ocorrencias` ([`js/ocorrencias.js`](js/ocorrencias.js)) é o **hub**: lista as ocorrências
(registro `window.__SIRENE_OCCURRENCES`) e, em cada card, dá **acesso direto a todas as etapas**
do atendimento — **Entrada** (reabre em modo edição), **Regulação**, **Despacho**,
**Contra-regulação**, **Transporte** e **IMV** (quando aplicável). Selecionar uma etapa define a
ocorrência ativa (`window.__SIRENE_CASE`) e abre a tela correspondente. A etapa atual é destacada,
as concluídas ficam marcadas, e há filtros (Todas / Ativas / Em regulação / Em transporte / IMV /
Encerradas). A `etapa` de cada ocorrência avança automaticamente ao longo do fluxo
(entrada → regulação → despacho → contra-regulação → transporte → encerrada).

## Responsáveis e handoff entre profissionais

Cada etapa é conduzida por um profissional diferente e **transferida de forma fluida**
([`js/flow.js`](js/flow.js)): **Entrada = Atendente → Regulação = Especialista da agência →
Despacho = Controlador de frota → Contra-regulação = Equipe de intervenção + especialista →
Transporte = Equipe de intervenção**. Cada tela mostra o **Responsável** e um **"Recebido de:
[papel] · hora"**; cada transição gera um encaminhamento nomeado ao próximo profissional, e o
encerramento **libera a equipe** para nova ocorrência. A `etapa` da ocorrência avança
automaticamente (verificado: despacho empenha → contra-regulação recebe do "Controlador de frota").

## Triagem da entrada — Quem / O quê / Onde / Quando / Risco

- **O quê (natureza):** lista ampliada incluindo ocorrências policiais (roubo, furto, agressão,
  ameaça, pessoa desaparecida, perturbação, invasão, apoio a outra instituição) além das de
  saúde/bombeiros/defesa civil.
- **Onde:** endereço, número, complemento/referência, bairro, município, **estabelecimento /
  ponto de interesse** e **coordenadas GPS (AML)**.
- **Quando:** acontecendo agora / acabou de acontecer / ocorreu anteriormente.
- **Quem:** solicitante (nome, relação), vítimas (ficha individual) e **suspeito/agressor**.
- **Risco:** há arma, vítima ferida, agressor, **risco de morte**, suspeito no local, crianças,
  várias pessoas — os fatores graves **elevam a prioridade** (risco de morte → P0).

**Classificação de chamada sem ocorrência** (passo Canal): **Abandonada, Falsa/engano, Trote,
Silenciosa, Interrompida, Teste, Orientação** — registra o atendimento em `__SIRENE_CALL_LOG` e
encerra sem gerar ocorrência.

## Fluxo de Entrada de Chamado (implementado)

Tela **Entrada de chamado** (sidebar · botão "Nova ocorrência") — assistente de 6 passos,
tudo no padrão iPadOS, com ficha do chamado montada ao vivo e cronômetro T0:

1. **Canal** — telefone (chamada entrante, AML, gravação) ou **aplicativo** com
   **videoconferência** (câmera do cidadão, controles de mic/câmera/lanterna/Libras/captura)
   e **chat** bidirecional em tempo real.
2. **Localização** — *objetivo: identificar o local* — endereço + AML + mapa Leaflet com marcador arrastável.
3. **Natureza** — *objetivo: classificar* — natureza/queixa, triagem (sinais de alerta),
   **acionamento cruzado automático** de agências (ex.: acidente → SAMU + Bombeiros + Polícia)
   e **prioridade P0–P4 sugerida** pelo protocolo.
4. **Vítimas** — *objetivo: identificar vítimas* — contador + **ficha individual por vítima**
   (nome, idade, sexo, estado, queixa), preservada ao ajustar a quantidade. Esses dados são
   levados à ocorrência (`case.victims`) e alimentam o **IMV** e a **avaliação por vítima da
   contra-regulação**.
5. **Solicitante** — *objetivo: identificar o solicitante* — nome, CLI, relação, no local?, callback.
6. **Revisão** — resumo + cronologia T0–T3 e **criar ocorrência / encaminhar à regulação**.

**Edição:** na tela de uma ocorrência (`#ocorrencia`), o botão **"Editar etapas"** reabre esse
mesmo fluxo em **modo edição** — pré-preenchido com os dados do chamado, cabeçalho "Editando #id",
botão "Salvar alterações" e retorno à ocorrência ao concluir.

Arquivos: [`js/intake.js`](js/intake.js) (lógica), [`css/intake.css`](css/intake.css) (estilos),
seção `data-view-panel="entrada"` em [`index.html`](index.html), registro em [`js/nav-config.js`](js/nav-config.js).

## Regulação do chamado (implementado)

Ao concluir a entrada, o caso é **encaminhado à Regulação** (`#regulacao`) — a ficha é entregue
automaticamente (via `window.__SIRENE_CASE`). O regulador vê a triagem recebida e a prioridade,
escolhe a **conduta** (orientar sem envio · enviar USA/USB/motolância/aeromédico · encaminhar UPA ·
acionar outra agência), registra CID, destino/leito e justificativa, e o **motor recomenda o
recurso** (unidade + ETA). "Despachar" empenha o recurso e notifica o hospital.

A regulação usa **protocolos** e mantém o **registro da conversa** com o solicitante:
- **Protocolo de regulação** — selecionado automaticamente pela natureza (PCR, dor torácica,
  trauma, afogamento…): perguntas estruturadas (checklist com barra de progresso), instruções
  pré-chegada e referência versionada.
- **Registro da conversa** — **transcrição unificada** que reúne e marca a origem de cada
  linha: **Áudio** (voz), **Vídeo** (videoconferência) e **Chat**. O áudio da videoconferência
  é **transcrito ao vivo** (legendas CC sobre o vídeo) e cada fala é **registrada na transcrição**
  da ocorrência, com destaque de palavras de risco e hash SHA-256 (cadeia de custódia).

**Encaminhamento à agência responsável:** ao final da entrada, o caso é roteado à regulação
da **agência responsável** (SAMU / Polícia / Bombeiros / Defesa Civil), definida pela natureza
e agências acionadas. A regulação é *agency-aware* — cabeçalho com a agência (cor própria),
condutas e recursos específicos, e **nota de compartilhamento** com as demais agências
(lâminas) quando multiagência.

As **hipóteses diagnósticas** usam uma **lista dinâmica buscável (estilo Select2)** de
diagnósticos configurados por agência — **sem CID** ([`js/tagpicker.js`](js/tagpicker.js)).
O **Recurso / conduta** é um select (SAMU: Orientação / USA / USB / Motolância / Helicóptero /
UPA) e o **Risco presumido** usa cores (Vermelho / Amarelo / Verde / Azul) — **preenchido
automaticamente pela hipótese diagnóstica** selecionada (a maior gravidade entre os diagnósticos
escolhidos define a cor). O **destino NÃO é decidido na regulação** — é definido depois, na
contra-regulação.

**Ações da regulação:** **Despachar recurso** (o regulador seleciona o recurso em "Recurso /
conduta" e despacha — exige um recurso selecionado), **Enviar para fila de despacho** (a
solicitação vai ao controlador/despachante, que escolhe o recurso), **Encerrar com orientação**
e **Voltar à entrada**.

**Despacho** (`#despacho` · [`js/despacho.js`](js/despacho.js)) — a **fila do controlador**:
recebe as solicitações "Enviar para fila de despacho" (via `window.__SIRENE_DISPATCH_QUEUE`).
O rádio-operador seleciona uma solicitação e escolhe o recurso numa **lista ordenada por ETA
até o local** (distância + tempo), filtrada pela agência.

- **Múltiplos recursos:** é possível **selecionar e empenhar vários recursos** para a mesma
  ocorrência (ex.: uma **USB mais próxima** + uma **USA mais adequada**). Há uma **sugestão de
  combinação** (recurso de menor ETA marcado "mais próxima" + o tipo solicitado "mais adequada")
  com botão *Aplicar*, e o botão **Empenhar (N)**.
- **Pedido de apoio da equipe:** na **contra-regulação**, a equipe em campo pode **Solicitar
  apoio** (outra ambulância USB/USA, aeromédico, **viatura policial 190** ou **bombeiros 193**);
  o pedido entra na fila de despacho **da agência correspondente**, marcado **APOIO** e vinculado
  à ocorrência de origem.

**IMV — Incidente com Múltiplas Vítimas** (`#imv` · [`js/imv.js`](js/imv.js)):
a ocorrência é **marcada como IMV** na **entrada** (switch no passo Vítimas, sugerido
automaticamente a partir de 3 vítimas), na **regulação** ou na **contra-regulação** (botão
"Marcar como IMV" — a equipe confirma múltiplas vítimas ao chegar na cena).
As ocorrências IMV ficam em `window.__SIRENE_IMV`; no menu **Múltiplas vítimas** o usuário
**seleciona a ocorrência** num dropdown para abrir o **dashboard do incidente**: comando de
incidente com **triagem START por cor** (Vermelho/Amarelo/Verde/Preto) e contagem por cor,
**lista de vítimas** editável (triagem, idade, queixa, **destino hospitalar**, rastreio),
**recursos multiagência no incidente** (+ solicitar mais) e **distribuição hospitalar** com
vagas em tempo real e **notificação de recepção em massa**.

**Apoio à decisão (regulação médica):** a tela mostra as **ambulâncias disponíveis na região**
(distância e ETA até o local) e as **unidades de saúde próximas do chamado** — UPA, UBS e
hospitais com **endereço, distância e tempo de deslocamento por carro** (e vagas em tempo real).
Esses painéis aparecem quando a agência responsável é o SAMU.

Na **contra-regulação**, a equipe em campo usa um **app operacional** com **Registro operacional**
(abas Transcrição · Áudio · Vídeo · Chat): grava por **vídeo, áudio ou chat** e **tudo é
transcrito** numa transcrição unificada, marcada pela origem (Áudio/Vídeo/Chat) e com hash de
cadeia de custódia — o áudio da videoconferência da cena vira legenda ao vivo e é registrado.

**Contra-regulação** (`#contrarregulacao` · [`js/contrarregulacao.js`](js/contrarregulacao.js)):
acontece **após a chegada da equipe à cena** (confirmada por geofence). A equipe reporta a
**descrição da cena com campos específicos por natureza** (PCR: ritmo/RCP/via aérea/RCE;
trauma: mecanismo/Glasgow/encarceramento; dor torácica: ECG/dor/nitrato; etc.) e faz a
**avaliação individual por vítima**: um card com **abas V1…Vn** (+ adicionar) onde cada vítima
tem nome, triagem START (cor), idade, sexo, queixa, **sinais vitais** e **destino** próprios. É
**aqui que se define o destino** de cada vítima (regulação de leito), com **conclusão** que valida
e notifica os hospitais. Se a equipe confirmar **menos vítimas** que o registrado, é possível
**ignorar / excluir vítima** (mantém no mínimo uma; a contagem e o incidente IMV são atualizados). Quando a ocorrência é IMV, essa avaliação **compartilha as vítimas com o
painel Múltiplas vítimas** (edições sincronizam). Para agências não-médicas, o card vira
desfecho/encaminhamento único. Acesso: botão **"Contra-regulação"** na ocorrência ou pela sidebar.

**A contra-regulação NÃO encerra a ocorrência** — ao definir o destino, o botão **"Encaminhar ao
destino"** leva à etapa de **Transporte**.

**Transporte / Encaminhamento** (`#transporte` · [`js/transporte.js`](js/transporte.js)):
o recurso leva o paciente / cidadão / conduzido / desabrigado ao destino (hospital, UPA, UBS,
delegacia, abrigo). Lista os **encaminhamentos** (um por vítima com destino, ou um por
pessoa/agência), cada um avançando **Em transporte → No destino → Cuidado transferido**.
- No destino é obrigatória a **transferência de cuidados** (passagem com receptor + assinatura
  eletrônica) — só então a ocorrência pode encerrar.
- No destino a pessoa pode ser **redirecionada** a outro destino (motivos: **superlotação**,
  destino inadequado, falta de vaga, recusa, maior complexidade), voltando a "Em transporte".
- A **ocorrência só encerra (T7)** quando **todos** tiverem o cuidado transferido.
- Na contra-regulação, **"Encerrar sem encaminhamento"** finaliza direto quando a ocorrência
  **não caracteriza** transporte (orientação, engano, recusa, liberação no local).

**Comunicação persistente (áudio · vídeo · chat):** um **dock fixo no canto superior direito**
([`js/comms.js`](js/comms.js)) mantém áudio, vídeo e chat com o solicitante disponíveis durante
**toda a entrada e a regulação** (vídeo apenas no canal aplicativo). Recolhível, com abas
Áudio/Vídeo/Chat, cronômetro contínuo e gravação vinculada à ocorrência.

Arquivo: [`js/regulacao.js`](js/regulacao.js) · seção `data-view-panel="regulacao"`.
**Fluxo completo: Entrada → Regulação (agência responsável) → Despacho/Ocorrência.**

## Recursos — gestão de frota (criar, editar, manutenção)

View **Recursos** (`data-view-panel="ambulances"`, [`js/recursos.js`](js/recursos.js)): gestão completa
da frota multiagência — ambulâncias (USA/USB/Motolância/Aeromédico), viaturas CB (ABT/ABS/UR),
viaturas PM (VTR/Moto) e equipes DC (Vistoria). Fonte única: `window.__SIRENE_FLEET`
(semente em [`js/geo.js`](js/geo.js), a mesma do videowall).

- **Painel:** KPIs (frota, disponíveis, empenhados, manutenção, indisponíveis/reserva), alerta de
  pendências (OS aberta, revisão vencendo, tripulação incompleta, item em falta, combustível baixo,
  CRLV/seguro vencendo), filtros por agência e status (+ "Com pendências"), busca por prefixo/placa/base/modelo.
- **Card do recurso:** prefixo, tipo·agência, placa, modelo, base, situação (ocorrência empenhada / OS
  aberta / motivo), medidores de combustível e revisão, tripulação e equipamentos (x/y), pendências,
  ações rápidas Editar · Manutenção · Checklist · Tripulação.
- **Ficha (modal com abas):**
  - **Dados** — cadastro/edição (agência → tipos filtrados, prefixo único, placa, modelo, ano, base,
    odômetro, combustível, próxima revisão, CRLV, seguro), **status operacional** (Disponível,
    Empenhado, Reserva, Manutenção, Indisponível) com motivo obrigatório para os não operacionais,
    observações e **baixa** (com confirmação). Toda mudança de status entra no histórico.
  - **Tripulação — vínculo viatura ↔ equipe** ([`js/equipes.js`](js/equipes.js)): três políticas,
    padrão por agência e sobrescrevível por viatura — **Fixo por turno** (SAMU: a viatura tem
    Equipe A no diurno e Equipe B no noturno, ex.: USA 01 · Equipe A/B; escala por turno com equipes
    já em serviço bloqueadas), **Livre** (Bombeiros/Defesa Civil: qualquer guarnição **assume** a
    viatura ao entrar de serviço e a **libera** ao sair) e **Tripulação por turno** (Polícia: sem equipe
    vinculada; a cada turno seleciona-se cada profissional do efetivo, com "sugerir composição" e
    "repetir turno anterior"). Composição mínima por tipo; a tripulação efetiva do turno alimenta
    alertas e o card. Barra de **turno atual** (Diurno 07h–19h / Noturno 19h–07h, automático pelo
    relógio, com simulação) e modal **Equipes e vínculos** (política por agência, CRUD de equipes com
    membros do efetivo, onde cada equipe está escalada/em serviço).
  - **Checklist do turno — por viatura:** feito individualmente em cada viatura, com **conferência
    nominal da tripulação em serviço** (presente/ausente) e dos equipamentos embarcados; registra
    turno/data/hora, ausentes e itens em falta no histórico; "Checklist pendente" vira pendência do
    recurso. O botão global apenas lista as viaturas com checklist pendente no turno.
  - **Manutenção** — ordens de serviço: tipo (preventiva, corretiva, revisão, pneus, funilaria,
    equipamento), descrição, oficina, previsão de retorno, custo, "retirar de operação agora"
    (→ status Manutenção). **Concluir OS** libera o recurso (volta a Disponível) e, se preventiva/
    revisão, reprograma a próxima revisão (+10.000 km). Histórico de OS concluídas.
  - **Histórico** — linha do tempo de tudo (status, OS, checklists, escalas, cadastro).

## Videowall — drone de monitoramento (tempo real)

Página dedicada ao videowall do CIODES: [`videowall.html`](videowall.html) (menu **Videowall**,
ou `F` para tela cheia). Usa a **mesma base técnica do
[God's Eye View](https://github.com/bilawalsidhu/gods-eye-view)** (simulador open source de
satélite espião): globo **CesiumJS** + imagens **Esri World Imagery** (sem chave) + **shaders GLSL
de pós-processamento** que "reskinam" o sensor + HUD tático com **detecções em screen-space**.
Aqui o satélite vira o **DRONE-01**, que patrulha a Grande Vitória e é re-tarefado.

- **Câmeras:** *Drone* (a câmera é o sensor: heading e inclinação do drone), *Órbita* (gira em torno
  do alvo/drone), *Geral* (Grande Vitória inteira; navegação livre). Tecla `C` alterna.
- **Sensores (teclas 1–5):** Óptico · **NVG** (fósforo verde, grão, scanlines) · **Térmico** (paleta
  ironbow por luminância) · **CRT** (aberração cromática + scanlines) · **Noir**. Implementados como
  `Cesium.PostProcessStage` com um único fragment shader (`u_mode`).
- **Ocorrências ao vivo:** marcadores pulsantes coloridos pela prioridade (P0 vermelho…), caixa de
  detecção com `P0 · #id · natureza · etapa`, lista de contatos com distância ao drone e tempo aberto.
  Novas ocorrências entram automaticamente (ou `N` / "Nova ocorrência"): alerta no topo, evento no
  ticker, **despacho automático** da unidade disponível mais próxima da agência responsável e, se P0,
  o **drone é re-tarefado** (missão `RESPOSTA P0`) e passa a orbitar a cena.
- **Unidades pela malha viária (sem linha reta):** cada deslocamento é roteado pelas ruas,
  avenidas, pontes e rodovias reais (dados OpenStreetMap). Ordem de obtenção da rota:
  1) **rotas pré-calculadas embarcadas** em [`js/rotas-cache.js`](js/rotas-cache.js) — ~300 trajetos
  entre bases, locais de ocorrência, hospitais/UPAs e pontos de patrulha, gerados por
  [`scripts/gen_rotas.py`](scripts/gen_rotas.py) (funciona offline e sem depender de serviço externo);
  2) **OSRM** ao vivo (`router.project-osrm.org`); 3) **Valhalla** ao vivo (`valhalla1.openstreetmap.de`).
  Se nenhuma rota for obtida, a viatura **fica parada aguardando** e tenta novamente a cada 10 s —
  veículo terrestre nunca cruza mar, rio ou barreira em linha reta (só aeronaves voam direto).
  A viatura segue a polilinha da rota com heading real e o **nome da via atual** aparece na caixa de
  detecção, na lista de contatos (com km restantes) e no ticker a cada mudança de rua. Ícones por agência
  (SAMU vermelho, CB laranja, PM azul, DC ciano), **rota luminosa** restante até o alvo, ciclo completo simulado: deslocamento → na cena (contra-regulação) →
  transporte ao hospital/UPA mais próximo (transferência de cuidados) → retorno → disponível. A etapa
  da ocorrência acompanha (`contrarregulacao` → `transporte` → `encerrada`, some após 12 s).
- **Drone:** telemetria (ALT/VEL/HDG/LAT/LON/LINK/BAT/MISSÃO), campo de visão (trapézio no solo) e
  linha ao solo; clicar num contato = **cobertura** (ocorrência) ou **rastreio** (unidade); `P` volta
  à patrulha. Camadas: rotas, locais (bases/destinos), campo de visão, detecções.
- **Dados:** [`js/geo.js`](js/geo.js) centraliza coordenadas de bairros, locais e frota; é também a
  semente do painel de Ocorrências (mesmas ocorrências). Tempo simulado ×8.
- Arquivos: [`js/videowall.js`](js/videowall.js) · [`css/videowall.css`](css/videowall.css).
  Sem shell/sidebar de propósito (tela de parede); botão "casa" volta ao painel.

## Aplicativos móveis (PWA) — cidadão e equipe da viatura

Dois aplicativos instaláveis (manifest + service worker, abrem offline o *shell*; a API é sempre pela rede),
servidos pelo mesmo servidor e ligados ao mesmo estado da central em tempo real. Os links ficam em
**Ajustes → Sobre → Aplicativos**.

**App do cidadão** — [`cidadao.html`](cidadao.html) · [`js/app-cidadao.js`](js/app-cidadao.js)

- Sem cadastro: botão **SOS** (emergência com localização automática) ou escolha da agência
  (SAMU 192 · Polícia 190 · Bombeiros 193 · Defesa Civil 199) e da situação (naturezas dos Ajustes),
  nome/telefone, descrição, **localização por GPS** com mapa ajustável e endereço.
- O chamado entra na central como ocorrência **"APP"** na etapa de **entrada** (prioridade da natureza) e
  o cidadão recebe as **instruções pré-chegada do protocolo** (ex.: compressões na PCR), a linha do tempo
  (recebido → em regulação → equipe a caminho → em atendimento → encerrado), a viatura empenhada e o
  **chat com a central** (mensagens aparecem no dock de comunicação da tela de regulação/entrada; a
  resposta da central chega ao app em segundos). Pode **atualizar a posição**, **cancelar** e, ao final,
  **avaliar** o atendimento; histórico dos chamados fica no aparelho.
- **Áudio e vídeo reais (WebRTC)** com a central: no chamado em andamento o cidadão toca em "Chamar por vídeo"
  ou "Só áudio"; a central também pode chamar (o app toca, vibra e mostra Atender/Recusar). Durante a chamada:
  mudo, câmera ligada/desligada, trocar câmera (frontal/traseira) e encerrar; a central pode capturar um
  quadro do vídeo como anexo da ocorrência. Núcleo em [`js/rtc-core.js`](js/rtc-core.js) (compartilhado com a
  central), sinalização por SSE + POST no servidor, mídia ponto a ponto criptografada (DTLS-SRTP) com STUN
  público; TURN configurável em `window.__SIRENE_ICE` (necessário para redes com NAT restritivo).
- **Câmera, microfone e GPS em celulares exigem HTTPS**: `npm run cert` gera um certificado autoassinado com o IP
  da máquina e o servidor passa a atender também em `https://<ip>:8443` (aceite o aviso do certificado no
  celular). Em produção, use um certificado válido (Caddy/Let's Encrypt) na frente do servidor.
- API pública (`server/server.js`, `publicApi`): `GET /api/public/config`, `POST /api/public/chamado`
  (devolve id + **ticket**), `GET|POST /api/public/chamado/:id[/msg|pos|cancelar|avaliacao]` exigindo o
  ticket; limite de 60 req/min por IP; nunca expõe dados de outros chamados. Sinalização WebRTC:
  `GET /api/public/chamado/:id/rtc/events` (SSE do cidadão) e `POST /api/public/chamado/:id/rtc` (cidadão →
  central); do lado autenticado `POST /api/rtc` (`para: "cidadao"` ou outro operador) e evento SSE `rtc`.

**App da equipe** — [`equipe.html`](equipe.html) · [`js/app-equipe.js`](js/app-equipe.js)

- Login com o usuário do SIRENE (perfil **Equipe de intervenção**) e escolha da **viatura** (as
  disponíveis para a agência; lembrada no aparelho). Cabeçalho mostra viatura, tripulante, status e
  conexão em tempo real.
- Na central, o vídeo/áudio do cidadão aparece na **Entrada** (painel do app, aba Videoconferência) e no
  **dock de comunicação** (aba Vídeo) da regulação; uma chamada recebida mostra um aviso com Atender em
  qualquer tela ([`js/rtc.js`](js/rtc.js)).
- Aba **Ocorrência** — fluxo operacional da equipe após o despacho, com carimbo de horário em cada passo
  (`equipe.status` + `tempos`), visível à central em tempo real:
  1. **Despacho recebido** — o app toca e vibra até a equipe tocar em **Ciente** (a central vê "aguardando
     ciente").
  2. **Ir ao local** — inicia o deslocamento; o app também **detecta o deslocamento pelo GPS** (velocidade
     acima de 15 km/h em duas leituras) e muda o status sozinho, avisando "detectado pelo GPS".
  3. **Chegada no local** — abre imediatamente o atendimento: **registros por vítima** (triagem, idade,
     sexo, nome, queixa/quadro, estado, **sinais vitais** PA/FC/FR/SpO₂/glicemia/temperatura/Glasgow,
     **procedimentos** realizados, evolução), protocolo de cena e consumo de materiais; cada alteração
     entra na **linha do tempo do atendimento**.
  4. **Contra-regular** — envia a solicitação à central com o resumo dos registros; o status vira
     "aguardando contra-regulação" e a equipe continua registrando (pode atualizar a solicitação).
  5. **Resposta da central** — o contra-regulador delibera (transportar ao destino, orientar e liberar,
     aguardar apoio, óbito/IML, recusa) e informa destino e orientações; o app mostra a resposta e libera
     o passo correspondente: **Iniciar transporte** (destino já aplicado às vítimas), **Encerrar no local
     conforme orientação** ou encerramento por óbito.
  6. **Cheguei ao destino** e **Transferência de cuidados** (receptor obrigatório → encerra e libera a
     viatura); **Solicitar apoio** (192/190/193/199) em qualquer momento.
- Aba **Viatura**: status (disponível/indisponível/manutenção), odômetro e combustível, **checklist do
  turno** por equipamento embarcado (a conferência da tripulação é da central).
- Aba **Consumo**: itens das bolsas/prateleiras da viatura com **consumo por vítima** (FEFO por lote)
  gerando automaticamente a **requisição na farmácia vinculada** ao veículo.
- Aba **Mais**: tripulante, GPS (posição enviada ao servidor e vista no despacho/videowall), trocar de
  viatura, histórico da viatura, sair.
- Tudo é gravado no servidor com as mesmas regras da central (versionamento, conflitos, auditoria) e
  aparece na sala de regulação imediatamente. Testado ponta a ponta: chegada → transporte → destino →
  transferência com a USA-07 (ocorrência encerrada, viatura liberada, requisição REQ aberta).

## Próximos passos sugeridos

1. Alta disponibilidade dos bancos (Patroni + PgBouncer + PITR com pgBackRest) e réplicas do servidor atrás
   de um gateway, conforme [`Requisitos/04`](Requisitos/04-arquitetura-banco-de-dados.md) e 05.
2. Integrações reais (telefonia/CTI, AML, telemetria MQTT, FHIR do censo, push APNs/FCM) substituindo
   os simuladores embutidos; vídeo/áudio WebRTC no app do cidadão.
3. Videowall: ligar ao GPS real das viaturas (já enviado pelo app da equipe), 3D Tiles fotorrealistas.
4. Rebranding das páginas de autenticação e do UI Kit (ainda com textos do template).

> Template de interface para prototipação. Dados exibidos são fictícios.
