# Dashboard — Modal Cálculos & Informações — Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Adicionar um botão `?` no header do Dashboard Operacional que abre um modal estático com definições de todas as métricas e suas fórmulas.

**Architecture:** Todo o conteúdo é HTML estático embutido em `dashboard.php` (nenhuma query, nenhum endpoint). O botão fica em `dash-daterange-wrap` após o botão de tema. O modal segue exatamente o padrão visual do `#dash-modal-ocorrencias` já existente. O JS de abertura/fechamento é adicionado ao bloco `document.addEventListener('click')` e `document.addEventListener('keydown')` existentes em `dashboard.js`.

**Tech Stack:** PHP (HTML estático), CSS inline no `<style>` existente, Vanilla JS, Bootstrap Icons (já carregado).

---

## Arquivos modificados

| Arquivo | O que muda |
|---|---|
| `html/dashboard.php` | (1) Novo `<style>` block antes do modal de calculos; (2) botão `?` em `dash-daterange-wrap`; (3) HTML do modal ao final de `#dash-op-root` |
| `html/dashboard.js` | Extensão dos dois `document.addEventListener` existentes (click + keydown) |

---

### Task 1: Adicionar CSS do modal de cálculos em `dashboard.php`

**Files:**
- Modify: `componentes/Missoes/Logistica/sinais/Dashboard/html/dashboard.php` — adicionar bloco `<style>` logo antes de `</div><!-- /#dash-op-root -->`

- [ ] **Step 1: Localizar o ponto de inserção**

Abrir `html/dashboard.php`. A última linha do arquivo é:
```
</div><!-- /#dash-op-root -->
```
(linha ~3206). Inserir o CSS antes dessa linha.

- [ ] **Step 2: Adicionar o bloco de estilos**

Inserir imediatamente antes de `</div><!-- /#dash-op-root -->`:

```html
    <!-- ===== MODAL CÁLCULOS & INFORMAÇÕES — CSS ===== -->
    <style>
        #btn-dash-info {
            width: 2.4rem; height: 2.4rem;
            background: rgba(255,255,255,0.04);
            border: 1px solid rgba(255,255,255,0.10);
            color: rgba(255,255,255,0.75);
            border-radius: 0.5rem;
            cursor: pointer;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            transition: background 0.15s, color 0.15s, border-color 0.15s;
            font-size: 1rem;
        }
        #btn-dash-info:hover {
            color: #f1f5f9;
            background: rgba(255,255,255,0.08);
            border-color: rgba(96,165,250,0.45);
        }
        body.dash-tema-claro #btn-dash-info { color: #475569; background: rgba(15,23,42,0.05); border-color: rgba(15,23,42,0.12); }
        body.dash-tema-claro #btn-dash-info:hover { color: #1e293b; background: rgba(15,23,42,0.09); border-color: rgba(15,23,42,0.25); }

        #dash-modal-calculos { animation: dashModalFadeIn 0.18s ease-out; }
        #dash-modal-calculos > .dash-modal-card { animation: dashModalSlideIn 0.22s cubic-bezier(0.16, 1, 0.3, 1); }
        #dash-calculos-fechar:hover { background: rgba(239,68,68,0.12) !important; border-color: rgba(239,68,68,0.55) !important; color: #fca5a5 !important; }

        .dash-info-secao {
            font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
            letter-spacing: 0.09em; color: #6366f1;
            padding: 1.1rem 1.4rem 0.5rem;
            border-top: 1px solid rgba(255,255,255,0.06);
            margin-top: 0.4rem;
        }
        .dash-info-secao:first-child { border-top: none; margin-top: 0; padding-top: 0.7rem; }
        .dash-info-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
            gap: 0.75rem;
            padding: 0 1.4rem 0.4rem;
        }
        .dash-info-item {
            background: rgba(255,255,255,0.03);
            border: 1px solid rgba(255,255,255,0.07);
            border-radius: 0.65rem;
            padding: 0.85rem 1rem;
        }
        .dash-info-nome {
            font-size: 0.88rem; font-weight: 700; color: #e2e8f0;
            display: flex; align-items: center; gap: 0.45rem;
            margin-bottom: 0.4rem;
        }
        .dash-info-nome i { color: #818cf8; font-size: 0.9rem; }
        .dash-info-desc { font-size: 0.82rem; color: #94a3b8; line-height: 1.55; }
        .dash-info-formula {
            font-family: 'Courier New', monospace;
            font-size: 0.79rem; color: #c4b5fd;
            background: rgba(99,102,241,0.09);
            border: 1px solid rgba(99,102,241,0.18);
            border-radius: 0.4rem;
            padding: 0.45rem 0.65rem;
            margin: 0.5rem 0 0;
            white-space: pre-wrap;
            line-height: 1.5;
        }
        .dash-info-semaforos {
            display: flex; gap: 0.4rem; flex-wrap: wrap;
            margin-top: 0.55rem;
        }
        .dash-info-badge {
            font-size: 0.74rem; font-weight: 700;
            padding: 0.2rem 0.55rem;
            border-radius: 0.35rem;
            white-space: nowrap;
        }
        .dash-info-badge.success { background: rgba(34,197,94,0.14); color: #4ade80; border: 1px solid rgba(34,197,94,0.30); }
        .dash-info-badge.warning { background: rgba(245,158,11,0.14); color: #fbbf24; border: 1px solid rgba(245,158,11,0.30); }
        .dash-info-badge.danger  { background: rgba(239,68,68,0.14);  color: #f87171; border: 1px solid rgba(239,68,68,0.30); }

        body.dash-tema-claro .dash-info-secao { color: #4f46e5; border-top-color: #e2e8f0; }
        body.dash-tema-claro .dash-info-item { background: rgba(15,23,42,0.02); border-color: #e2e8f0; }
        body.dash-tema-claro .dash-info-nome { color: #1e293b; }
        body.dash-tema-claro .dash-info-nome i { color: #4f46e5; }
        body.dash-tema-claro .dash-info-desc { color: #475569; }
        body.dash-tema-claro .dash-info-formula { color: #4338ca; background: rgba(99,102,241,0.07); border-color: rgba(99,102,241,0.20); }
    </style>
```

- [ ] **Step 3: Verificar sintaxe (sem PHP — só CSS)**

Não há lógica PHP nesse bloco. Confirmar que o bloco `<style>` está dentro de `#dash-op-root` mas antes de `</div><!-- /#dash-op-root -->`.

---

### Task 2: Adicionar botão `?` em `dash-daterange-wrap` em `dashboard.php`

**Files:**
- Modify: `componentes/Missoes/Logistica/sinais/Dashboard/html/dashboard.php` — adicionar botão após `dash-btn-tema`

- [ ] **Step 1: Localizar o ponto de inserção**

No arquivo `html/dashboard.php`, encontrar o bloco (em torno da linha 1069):
```php
            <button type="button" class="dash-btn-tema" id="dash-btn-tema" title="<?= show('Alternar tema') ?>" aria-label="<?= show('Alternar tema') ?>">
                <i class="bi-sun"></i>
            </button>
        </div>
    </div>
```

- [ ] **Step 2: Inserir o botão após `dash-btn-tema` (antes do `</div>` que fecha `dash-daterange-wrap`)**

Substituir:
```php
            <button type="button" class="dash-btn-tema" id="dash-btn-tema" title="<?= show('Alternar tema') ?>" aria-label="<?= show('Alternar tema') ?>">
                <i class="bi-sun"></i>
            </button>
        </div>
    </div>
```

Por:
```php
            <button type="button" class="dash-btn-tema" id="dash-btn-tema" title="<?= show('Alternar tema') ?>" aria-label="<?= show('Alternar tema') ?>">
                <i class="bi-sun"></i>
            </button>
            <button type="button" id="btn-dash-info" title="<?= show('Cálculos & Informações') ?>" aria-label="<?= show('Cálculos & Informações') ?>">
                <i class="bi-info-circle"></i>
            </button>
        </div>
    </div>
```

---

### Task 3: Adicionar HTML do modal de cálculos em `dashboard.php`

**Files:**
- Modify: `componentes/Missoes/Logistica/sinais/Dashboard/html/dashboard.php` — adicionar após o bloco `</style>` da Task 1, antes de `</div><!-- /#dash-op-root -->`

O CSS da Task 1 e o HTML desta Task ficam na mesma posição (CSS primeiro, depois o `<div>` do modal). O CSS já foi inserido — este task insere o HTML do modal logo após o `</style>` da Task 1.

- [ ] **Step 1: Inserir HTML do modal após o `</style>` adicionado na Task 1**

Logo após o `</style>` da Task 1 (e antes de `</div><!-- /#dash-op-root -->`), inserir:

```html
    <!-- ===== MODAL CÁLCULOS & INFORMAÇÕES ===== -->
    <div id="dash-modal-calculos" style="display:none;
         position:fixed; inset:0; z-index:10000;
         background:radial-gradient(circle at center, rgba(15,12,41,0.78), rgba(0,0,0,0.85));
         backdrop-filter:blur(4px);
         align-items:center; justify-content:center; padding:1.5rem;">
        <div class="dash-modal-card" style="
                    background:linear-gradient(145deg, rgba(20,18,50,0.98) 0%, rgba(15,12,41,0.98) 100%);
                    border:1px solid rgba(99,102,241,0.18); border-radius:0.95rem;
                    width:min(920px, 100%); max-height:88vh; display:flex; flex-direction:column;
                    box-shadow:0 32px 80px rgba(0,0,0,0.65), 0 0 0 1px rgba(255,255,255,0.04) inset;
                    overflow:hidden;">

            <!-- Header -->
            <div style="display:flex; align-items:center; gap:0.9rem;
                        padding:1.1rem 1.4rem; border-bottom:1px solid rgba(255,255,255,0.07);
                        background:linear-gradient(180deg, rgba(99,102,241,0.06), transparent); flex-shrink:0;">
                <div style="display:inline-flex; align-items:center; justify-content:center;
                            width:2.5rem; height:2.5rem; border-radius:0.7rem;
                            background:rgba(99,102,241,0.14);
                            box-shadow:inset 0 0 0 1px rgba(99,102,241,0.4);
                            color:#a5b4fc; flex-shrink:0;">
                    <i class="bi-calculator" style="font-size:1.15rem;"></i>
                </div>
                <div style="flex:1; min-width:0;">
                    <div style="font-size:1.05rem; font-weight:700; color:#f1f5f9; letter-spacing:0.005em;">
                        <?= show('Cálculos & Informações') ?>
                    </div>
                    <div style="font-size:0.78rem; color:#94a3b8; margin-top:0.2rem;">
                        <?= show('Definições e fórmulas de todas as métricas do Dashboard') ?>
                    </div>
                </div>
                <button id="dash-calculos-fechar" type="button" aria-label="<?= show('Fechar') ?>"
                        style="background:transparent; border:1px solid rgba(255,255,255,0.10);
                               color:#cbd5e1; border-radius:0.5rem; width:2.2rem; height:2.2rem;
                               display:flex; align-items:center; justify-content:center; cursor:pointer;
                               transition:all 0.15s; flex-shrink:0;">
                    <i class="bi-x-lg" style="font-size:0.9rem;"></i>
                </button>
            </div>

            <!-- Corpo (scrollável) -->
            <div style="overflow:auto; flex:1;">

                <!-- ── 1. KPIs — Visão Geral ───────────────────────────── -->
                <div class="dash-info-secao"><i class="bi-speedometer2"></i> <?= show('KPIs — Visão Geral') ?></div>
                <div class="dash-info-grid">
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-box-seam"></i><?= show('Separações') ?></div>
                        <div class="dash-info-desc"><?= show('Quantidade de missões de separação (tipo 2) concluídas no período. Cada missão representa uma rota completa — não conta itens individuais.') ?></div>
                    </div>
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-truck"></i><?= show('Carregamentos') ?></div>
                        <div class="dash-info-desc"><?= show('Quantidade de missões de carregamento (tipo 3) concluídas no período. Mede o ritmo de saída de mercadoria do armazém.') ?></div>
                    </div>
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-list-ol"></i><?= show('Itens Separados') ?></div>
                        <div class="dash-info-desc"><?= show('Soma de todos os itens bipados nas missões de separação do período. Um item = uma linha de produto em uma rota.') ?></div>
                    </div>
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-boxes"></i><?= show('Cubagem Total') ?></div>
                        <div class="dash-info-desc"><?= show('Soma do volume (m³) dos itens nas missões de separação. Indica a massa de mercadoria movimentada, independentemente da quantidade de itens.') ?></div>
                    </div>
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-stopwatch"></i><?= show('Lead Time Sep→Carg') ?></div>
                        <div class="dash-info-desc"><?= show('Mediana (em minutos) entre o fim de uma separação e o início do carregamento do mesmo romaneio. Romaneios sem separação registrada são excluídos. Mede a agilidade na transição entre etapas.') ?></div>
                        <div class="dash-info-formula"><?= show('mediana(início_carregamento − fim_separação) por romaneio') ?></div>
                    </div>
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-moon-stars"></i><?= show('Overnight') ?></div>
                        <div class="dash-info-desc"><?= show('Romaneios cuja separação foi concluída num dia mas o carregamento só iniciou no dia seguinte (ou posterior). Indica mercadoria "dormindo" no armazém entre etapas.') ?></div>
                    </div>
                </div>

                <!-- ── 2. Separação ───────────────────────────────────── -->
                <div class="dash-info-secao"><i class="bi-box-seam"></i> <?= show('Separação') ?></div>
                <div class="dash-info-grid">
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-check2-circle"></i><?= show('Assertividade') ?></div>
                        <div class="dash-info-desc"><?= show('Percentual de itens separados corretamente. Um item é "errado" quando o conferente registra separou_errado = 1 no carregamento. Retorna "—" quando o separador não tem itens no período.') ?></div>
                        <div class="dash-info-formula">assertividade = itens_corretos / itens_total × 100</div>
                        <div class="dash-info-semaforos">
                            <span class="dash-info-badge success"><?= show('≥ 95% — Verde') ?></span>
                            <span class="dash-info-badge warning"><?= show('≥ 88% — Amarelo') ?></span>
                            <span class="dash-info-badge danger"><?= show('< 88% — Vermelho') ?></span>
                        </div>
                    </div>
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-star-half"></i><?= show('Score de Separador') ?></div>
                        <div class="dash-info-desc"><?= show('Nota de 0 a 100 calculada com três componentes: assertividade (qualidade), velocidade (tempo por item vs mediana) e volume (quantidade de itens).') ?></div>
                        <div class="dash-info-formula"><?= show("score = assertividade×0,40 + velocidade×0,40 + volume×0,20\n\nvelocidade = min(1, tempo_ref / tempo_item) × 40\nvolume     = min(1, itens / 200) × 20") ?></div>
                        <div class="dash-info-semaforos">
                            <span class="dash-info-badge success"><?= show('≥ 70 — Verde') ?></span>
                            <span class="dash-info-badge warning"><?= show('≥ 45 — Amarelo') ?></span>
                            <span class="dash-info-badge danger"><?= show('< 45 — Vermelho') ?></span>
                        </div>
                    </div>
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-arrow-counterclockwise"></i><?= show('Retrabalho') ?></div>
                        <div class="dash-info-desc"><?= show('Percentual de missões que precisaram ser refeitas total ou parcialmente. Semáforo calculado sobre a taxa de acerto (100 − retrabalho_pct).') ?></div>
                        <div class="dash-info-semaforos">
                            <span class="dash-info-badge success"><?= show('Acerto ≥ 95% — Verde') ?></span>
                            <span class="dash-info-badge warning"><?= show('Acerto ≥ 85% — Amarelo') ?></span>
                            <span class="dash-info-badge danger"><?= show('Acerto < 85% — Vermelho') ?></span>
                        </div>
                    </div>
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-clock-history"></i><?= show('Tempo por Item') ?></div>
                        <div class="dash-info-desc"><?= show('Média de segundos gastos por item separado no período. Serve como referência de velocidade no cálculo do score: separadores acima da mediana ganham pontuação máxima no componente de velocidade.') ?></div>
                    </div>
                </div>

                <!-- ── 3. Carregamento ────────────────────────────────── -->
                <div class="dash-info-secao"><i class="bi-truck"></i> <?= show('Carregamento') ?></div>
                <div class="dash-info-grid">
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-boxes"></i><?= show('Cubagem de Carregamento') ?></div>
                        <div class="dash-info-desc"><?= show('Soma do volume (m³) dos itens bipados nas missões de carregamento (tipo 3). Pode diferir da cubagem de separação quando há ajustes no carregamento (devoluções, trocas de produto).') ?></div>
                    </div>
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-stopwatch"></i><?= show('Lead Time Sep→Carg (Mediana)') ?></div>
                        <div class="dash-info-desc"><?= show('Mesmo indicador do painel de KPIs. Calculado por romaneio e apresentado como mediana do período. Romaneios com lead time negativo (inconsistência de dados) são descartados.') ?></div>
                        <div class="dash-info-formula"><?= show('mediana(início_carregamento − fim_separação) em minutos') ?></div>
                    </div>
                </div>

                <!-- ── 4. Ranking ─────────────────────────────────────── -->
                <div class="dash-info-secao"><i class="bi-trophy"></i> <?= show('Ranking') ?></div>
                <div class="dash-info-grid">
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-star"></i><?= show('Score de Conferente') ?></div>
                        <div class="dash-info-desc"><?= show('Nota de 0 a 100 calculada com dois componentes: acurácia (taxa de não-erros) e baixo retrabalho.') ?></div>
                        <div class="dash-info-formula"><?= show("score = acurácia×0,60 + baixo_retrabalho×0,40\n\nacurácia         = (1 − erros/cargas) × 60\nbaixo_retrabalho = (1 − retrabalho_pct/100) × 40") ?></div>
                        <div class="dash-info-semaforos">
                            <span class="dash-info-badge success"><?= show('≥ 70 — Verde') ?></span>
                            <span class="dash-info-badge warning"><?= show('≥ 45 — Amarelo') ?></span>
                            <span class="dash-info-badge danger"><?= show('< 45 — Vermelho') ?></span>
                        </div>
                    </div>
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-exclamation-octagon"></i><?= show('Erros de Conferência') ?></div>
                        <div class="dash-info-desc"><?= show('Contados pelo campo separou_errado = 1 nos itens de carregamento. Cada combinação única de (pedido_id, produto_id) acusada conta como 1 erro, independentemente da quantidade bipada. O conferente que registra o erro é responsabilizado; o separador é identificado pelo concluido_por da missão de separação correspondente.') ?></div>
                    </div>
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-pin-map"></i><?= show('Liderança de Rota') ?></div>
                        <div class="dash-info-desc"><?= show('Conta quantas rotas distintas um separador liderou (foi o responsavel_id da missão de separação) no período. Separadores que trabalharam como auxiliares em rotas de outros não têm essa contagem incrementada.') ?></div>
                    </div>
                </div>

                <!-- ── 5. Pesagem ─────────────────────────────────────── -->
                <div class="dash-info-secao"><i class="bi-speedometer"></i> <?= show('Pesagem') ?></div>
                <div class="dash-info-grid">
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-rulers"></i><?= show('O que é Pesagem') ?></div>
                        <div class="dash-info-desc"><?= show('Registro do peso real dos volumes/caixas aferido no momento do carregamento. Permite comparar peso conferido (físico) vs peso esperado (sistema/ERP) e identificar divergências antes do embarque.') ?></div>
                    </div>
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-exclamation-triangle"></i><?= show('Divergência de Peso') ?></div>
                        <div class="dash-info-desc"><?= show('Diferença entre peso_conferido e peso_esperado, expressa em valor absoluto (kg) e percentual. Itens acima do threshold configurado são marcados como divergentes e aparecem em destaque no relatório.') ?></div>
                        <div class="dash-info-formula"><?= show('divergência = |peso_conferido − peso_esperado| / peso_esperado × 100') ?></div>
                    </div>
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-bar-chart"></i><?= show('Métricas de Pesagem') ?></div>
                        <div class="dash-info-desc"><?= show('Total de pesagens realizadas no período; peso total conferido (kg); percentual de itens dentro da tolerância; ranking de conferentes por volume pesado (quantidade de pesagens por pessoa).') ?></div>
                    </div>
                </div>

                <!-- ── 6. Erros ───────────────────────────────────────── -->
                <div class="dash-info-secao"><i class="bi-exclamation-octagon"></i> <?= show('Erros') ?></div>
                <div class="dash-info-grid">
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-box-seam"></i><?= show('Erro de Separação') ?></div>
                        <div class="dash-info-desc"><?= show('Identificado durante o carregamento: o conferente bipa um item e detecta que foi separado incorretamente (produto trocado, unidade errada). Registrado como separou_errado = 1 no item de carregamento. Responsabilidade imputada ao separador da missão correspondente.') ?></div>
                    </div>
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-truck"></i><?= show('Erro de Carregamento') ?></div>
                        <div class="dash-info-desc"><?= show('Discrepância entre o que foi carregado no caminhão e o manifesto da rota. Registrado no próprio carregamento (quantidade_carregada ≠ quantidade_esperada). Responsabilidade do motorista/conferente de carregamento.') ?></div>
                    </div>
                    <div class="dash-info-item">
                        <div class="dash-info-nome"><i class="bi-diagram-2"></i><?= show('Diferença entre os dois tipos') ?></div>
                        <div class="dash-info-desc"><?= show('Erro de separação = produto errado colocado na rota (detectado no conferimento). Erro de carregamento = quantidade errada embarcada no caminhão (detectado no manifesto). São contados e exibidos em painéis distintos na aba Erros.') ?></div>
                    </div>
                </div>

                <!-- Espaçador final -->
                <div style="height:1.2rem;"></div>
            </div>
        </div>
    </div>
```

- [ ] **Step 2: Confirmar posição final no arquivo**

A estrutura ao final de `dashboard.php` deve ser (nessa ordem):
1. `</section><!-- /#aba-pesagem -->`
2. `<!-- ===== MODAL DE DRILL-DOWN ===== -->` (já existia)
3. `<div id="dash-modal-ocorrencias" ...>` (já existia)
4. `<!-- ===== MODAL CÁLCULOS & INFORMAÇÕES — CSS ===== -->` (Task 1)
5. `<div id="dash-modal-calculos" ...>` (esta Task)
6. `</div><!-- /#dash-op-root -->`

---

### Task 4: Adicionar JS de abertura/fechamento em `dashboard.js`

**Files:**
- Modify: `componentes/Missoes/Logistica/sinais/Dashboard/html/dashboard.js` — estender dois `document.addEventListener` existentes

- [ ] **Step 1: Estender o handler de `click` (linha ~4703)**

Localizar o bloco:
```javascript
document.addEventListener('click', (e) => {
    // 1) Click no X do modal
    if (e.target.closest('#dash-modal-fechar')) {
        DashboardDrillModal.fechar();
        return;
    }
    // 2) Click no backdrop (fora do card interno)
    if (e.target.id === 'dash-modal-ocorrencias') {
        DashboardDrillModal.fechar();
        return;
    }
```

Adicionar logo **após** `// 2) Click no backdrop` e antes de `// 3) Click em linha de tipo`:

```javascript
    // 2b) Modal Cálculos: abrir via botão info
    if (e.target.closest('#btn-dash-info')) {
        const m = document.getElementById('dash-modal-calculos');
        if (m) m.style.display = 'flex';
        return;
    }
    // 2c) Modal Cálculos: fechar via X
    if (e.target.closest('#dash-calculos-fechar')) {
        const m = document.getElementById('dash-modal-calculos');
        if (m) m.style.display = 'none';
        return;
    }
    // 2d) Modal Cálculos: fechar via backdrop
    if (e.target.id === 'dash-modal-calculos') {
        e.target.style.display = 'none';
        return;
    }
```

- [ ] **Step 2: Estender o handler de `keydown` (linha ~4739)**

Localizar o bloco:
```javascript
document.addEventListener('keydown', (e) => {
    if (e.key === 'Escape' && DashboardDrillModal._ctx) {
        DashboardDrillModal.fechar();
    }
});
```

Substituir por:
```javascript
document.addEventListener('keydown', (e) => {
    if (e.key === 'Escape') {
        if (DashboardDrillModal._ctx) DashboardDrillModal.fechar();
        const mc = document.getElementById('dash-modal-calculos');
        if (mc && mc.style.display !== 'none') mc.style.display = 'none';
    }
});
```

---

### Task 5: Smoke test manual + commit

**Files:** nenhum arquivo novo

- [ ] **Step 1: Verificar que o arquivo PHP não tem erros de sintaxe**

```bash
php -l componentes/Missoes/Logistica/sinais/Dashboard/html/dashboard.php
```
Resultado esperado: `No syntax errors detected`

- [ ] **Step 2: Abrir o dashboard no browser e testar**

Navegar até a página do Dashboard Logística. Verificar:
1. Botão `?` aparece após o botão de tema (ícone `bi-sun`) no header
2. Clicar no `?` abre o modal com header "Cálculos & Informações"
3. Todas as 6 seções aparecem com conteúdo (KPIs, Separação, Carregamento, Ranking, Pesagem, Erros)
4. Clicar no `×` fecha o modal
5. Clicar fora do card (no backdrop escuro) fecha o modal
6. Tecla `Escape` fecha o modal
7. Verificar no tema claro (botão ☀️): cores dos cards e badges ficam legíveis
8. Verificar que o modal de drill-down (click em KPI) ainda funciona normalmente após abrir/fechar o modal de cálculos

- [ ] **Step 3: Commit**

```bash
git add componentes/Missoes/Logistica/sinais/Dashboard/html/dashboard.php \
        componentes/Missoes/Logistica/sinais/Dashboard/html/dashboard.js
git commit -m "feat(dashboard): modal Cálculos & Informações com definições de todas as métricas"
```
