# Shell de produto — navegação lateral e controle contextual v1.5

## Objetivo

Este contrato permite que qualquer produto da família Baluarte implemente o mesmo shell sem reinterpretar dimensões, identidade, movimento ou acessibilidade. Ele complementa `SIDE-NAVIGATION.md`, `ICON-BUTTON.md`, `GAUSSIAN-GLASS.md` e `NAV-SECTION.md`.

A referência comportamental é o ClickAgents preservado em modo somente leitura. O Baluarte mantém a lógica de baixo ruído — marca em repouso e ação contextual em hover/foco — e a normaliza para Phosphor, Squircle, tokens, hit area ampliada e acessibilidade da família.

## Geometria normativa

| Papel | Fine pointer | Coarse pointer | Observação |
|---|---:|---:|---|
| Header esquerdo e direito | 54px | 54px | Um grid compartilhado; sem borda horizontal inferior |
| Sidebar expandida | 260px | 260px | Divisor lógico de 1px até a base do viewport |
| Sidebar colapsada | 49px | 49px | Área interna de 48px + divisor de 1px; eixo fixo em 24px |
| Drawer móvel aberto | 260px | 260px | Nunca exceder viewport de 320px |
| Superfície de navegação | 36px de altura | 44px de altura | Squircle/fallback 16px |
| Superfície colapsada | 36 × 36px | 44 × 44px | Controle, nav item e Entity Item compartilham o perímetro |
| Ícones de navegação | 20px | 20px | Centralização por layout, nunca por offset corretivo |
| Comando do controle | 20px Regular | 20px Regular | `ph ph-sidebar-simple`, igual aos destinos do menu |
| Logo oficial | canvas 32px | canvas 32px | Sem fundo; escuro no tema claro e claro no escuro |
| Fallback sem logo | 24px Fill | 24px Fill | Phosphor Fill semanticamente representativo |
| Padding do frame | 7px início / 6px fim | igual | O divisor de 1px completa 7px visuais no fim |
| Gap glyph/label | 8px | 8px | Label começa a 35px no frame expandido |
| Intervalo de seção | 20px | 20px | Sem gap entre itens consecutivos |
| Footer | 60px | 60px | Fora do corpo rolável e ancorado à base |
| Conta expandida | 45px | 45px | Avatar 20px no eixo 24px; nome/plano 16/15px, gap 2px |

O track do conteúdo é sempre `minmax(0, 1fr)`. Header e corpo consomem o mesmo token de largura para que divisor, sidebar e limite do conteúdo permaneçam colineares. No colapso, a área interna de 48px centraliza a superfície fine com 6px/6px e a coarse com 2px/2px; ícones continuam iniciando em 14px e centrados no eixo 24px, exatamente como no estado expandido.

## Tipografia de brand

Brand é uma escala própria: não herda classes de título nem de corpo.

| Parte | Tamanho | Peso | Line-height | Cor |
|---|---:|---:|---:|---|
| Nome “Baluarte” | 18px | 500 | 22px | `--text` |
| Descritor “Material Design” | 14px | 500 | 20px | `--text-subtle` |

O nome usa tracking `-0.01em`; o descritor não recebe tracking corretivo. O peso 500 é invariável nas duas partes: a hierarquia vem de tamanho e cor, evitando que a marca ganhe densidade ao ganhar escala. Use `--b-brand-*`; não derive estes valores de `.b-title-*`, `.b-subtitle` ou `.b-body`.

Não use opacity, text-shadow, síntese de fonte ou peso fracionário para simular leveza. O lockup começa no inset óptico de 14px e alterações no controle não deslocam esse início no estado expandido.

## Controle contextual

### Estado expandido

1. Em repouso com mouse fora do rail, o controle não é desenhado visualmente.
2. Hover sobre o header esquerdo ou qualquer área da sidebar revela o controle.
3. Foco de teclado também revela o controle, independentemente de hover.
4. O glyph é `ph ph-sidebar-simple`, Regular 20px como os demais ícones do menu, em LTR e RTL.
5. LTR usa `cursor: w-resize`; RTL usa `cursor: e-resize`.
6. A superfície mede 36 × 36px fine ou 44 × 44px coarse e termina a 9px do divisor em fine.

### Estado colapsado

1. Em repouso, o slot mostra a marca do produto, sem fundo.
2. Somente hover/foco no próprio controle substitui a marca pelo Sidebar Simple Regular de 20px; percorrer outras áreas do rail mantém o logo.
3. LTR usa `cursor: e-resize`; RTL usa `cursor: w-resize`.
4. O logo usa canvas óptico de 32px; o fallback usa caixa Fill de 24px; o comando usa 20px. Todos compartilham exatamente o centro da superfície 36 × 36px/44 × 44px.
5. O botão permanece com `aria-expanded`, `aria-controls` e nome “Expandir navegação”.
6. Todo espaço vazio do header esquerdo e da sidebar colapsada também expande o rail por clique/toque. Links, botões, inputs e outras ações nunca são interceptados.
7. Todos os destinos primários permanecem visíveis e acionáveis, inclusive os pertencentes a grupos fechados; labels ficam em zero, contexto permanece oculto e a preferência do grupo não é alterada.

Em coarse pointer, não dependa de hover: logo e hit area vazia continuam acionáveis. O controle explícito preserva foco visível e operação por teclado.

## Logo e identidade provisória

O logo oficial é preferencial, local e sempre sem placa, chip, círculo ou fundo próprio. No tema claro, renderize a versão escura; no escuro, a clara. O canvas de 32px compensa a área transparente do asset Baluarte e permanece geometricamente centralizado; outra marca deve documentar sua normalização óptica sem offsets de layout.

Quando um produto ainda não tiver logo:

1. escolha um ícone Phosphor Fill de 24px que represente claramente o domínio;
2. não use menu, chevron, estrela ou forma abstrata apenas para preencher o espaço;
3. use `--text`, sem fundo adicional e sem cor privada;
4. registre o ícone no checklist de adoção;
5. mantenha-o `aria-hidden`; o nome acessível pertence ao botão;
6. o runtime deve mostrar o fallback quando o asset estiver ausente ou falhar e restaurar o logo após carga válida;
7. substitua-o pelo logo oficial sem alterar centro, frame ou superfície.

A identidade provisória não autoriza nova cor, novo shape ou outra largura de rail.

## Navegação

- tipografia de links e headings: 14px/400/21px;
- distância título–primeiro destino: 10px; grupos irmãos: 20px;
- links usam `--text`; headings usam `--nav-section-title`; hover muda somente `--surface-hover` nos destinos;
- selecionado: `aria-current` verdadeiro e `--surface-selected`;
- ícones: Phosphor Regular, caixa 20px;
- item: 36px fine ou 44px coarse, Squircle 16px;
- labels, headings, âncoras e status ocultos recebem `inert` + `aria-hidden`;
- no colapso, marks, ícones de destinos e avatar de 20px permanecem no eixo 24px e centrados verticalmente; labels, ações auxiliares e spinners desaparecem;
- ao expandir, destinos temporariamente apresentados por um grupo fechado voltam a `hidden`/`inert` em 160ms e o grupo recupera exatamente `aria-expanded="false"`.

## Movimento

| Fase | Layout | Texto | Interatividade oculta |
|---|---:|---:|---|
| Recolher | 160ms | 60ms | removida imediatamente |
| Expandir | 220ms | 200ms após delay de 140ms | restaurada ao final, em 340ms |
| Reduced motion | ≤0,01ms | ≤0,01ms, delay 0 | restaurada imediatamente |

Checkpoints obrigatórios: 60, 160, 139, 140, 220 e 340ms. Destinos de grupos fechados aparecem no rail entre 60–160ms e retornam ao estado fechado nos primeiros 160ms da expansão, sempre com label em zero, clipping limpo, zero hit area sobreposta e cancelamento por revisão quando a direção é invertida. Foco que seria ocultado migra antes para o controle global. Marca, headings, labels, grupos, status, divisor e conteúdo se movem pelo mesmo grid. Não use margem negativa, `translate` corretivo ou troca de shape.

## Margens laterais do conteúdo

O conteúdo principal usa gutters lógicos simétricos. Acima de 960px, `--portal-content-gutter` aplica `clamp(20px, 4vw, 48px)` igualmente no início e no fim de `.app-main`; quando `.content-wrap` alcança o máximo de 1120px, `margin-inline: auto` distribui o espaço excedente por igual. A distância entre o divisor da sidebar e a primeira aresta do conteúdo deve coincidir com a distância entre a última aresta e o fim do viewport, com tolerância de 0,5px CSS. O contrato vale com sidebar expandida ou recolhida e em LTR ou RTL. Em 960px ou menos, o shell mantém 10px simétricos. Não compense assimetria com margem negativa, `transform`, largura local ou override por página.

## Responsividade

- acima de 960px, o portal usa sidebar persistente;
- em 960px ou menos, usa drawer com trigger no header direito;
- um produto pode antecipar o drawer conforme seu conteúdo, mas nunca manter rail persistente abaixo de 767px;
- o drawer usa 260px, header interno de 54px, overlay modal, contenção de foco e restauração imediata ao fechar;
- o estado persistido do rail desktop não reduz o drawer móvel.

## Sem JavaScript

Sem runtime, controles inoperantes desaparecem e o shell permanece expandido. Navegação estática e conteúdo continuam acessíveis. A marca provisória nunca pode ser a única forma de chegar ao conteúdo.

## Critérios de aceite

A implementação deve provar em Chromium, Firefox e WebKit:

- tracks 260/49px em fine e coarse, com área interna de 48px antes do divisor;
- superfícies colapsadas 36/44px, Sidebar Simple Regular central de 20px, logo sem fundo em canvas 32px e fallback Fill 24px;
- brand separado: nome 18/500/22 e descritor 14/500/20;
- controle invisível fora do rail expandido, visível em hover e foco;
- logo em repouso colapsado, inclusive ao percorrer espaços vazios; Sidebar Simple somente no hover/foco do controle;
- expansão por clique/toque em todo vazio colapsado sem interceptar elementos nativos, `[role]`, `[tabindex]` ou controles ARIA;
- falha real do asset revela fallback Fill 24px centralizado e a carga válida restaura o logo;
- cursor correto em LTR/RTL;
- foco visível, Tab seguro, ARIA sincronizada e fallback sem JavaScript;
- gutters esquerdo/direito do conteúdo com delta ≤0,5px em sidebar expandida/recolhida, LTR/RTL e breakpoints desktop; em mobile, 10px dos dois lados;
- movimento normal e reduced motion;
- zero overflow, quebra do divisor ou deslocamento dos itens expandidos.
