# P5.6 — Fluidez, contraste e composição do CMS e do portal

| Campo | Valor |
|---|---|
| Programa | F7+ — operação, estrutura e conteúdo |
| Status | `planejado` |
| Depende de | P0, P1, P3 (superfícies de configuração/suporte), P5.5 (**implementado**) |
| Habilita | P6 (retomada da paridade editorial) |
| Tamanho | **M** (7 sessões) |
| ADRs | ADR-001, ADR-002, ADR-009, ADR-010, ADR-016, ADR-017, ADR-020, ADR-022, ADR-025, ADR-026, ADR-028, ADR-033, ADR-035, ADR-036, ADR-043, ADR-046 |

## 1. Objetivo

Revisitar **CMS, portal e UI/UX** antes de acrescentar a paridade editorial do P6, para que as
telas novas nasçam sobre um padrão já testado — não sobre uma segunda convenção. A fase traduz
**comportamento** dos layouts e do `StructuralLandingEditor` do `sucesu-cms/V2`, nunca a stack:
Bootstrap/jQuery, HTML/Blade no banco, `eval`, CodeMirror e biblioteca de CSS/JS ficam fora.

**Resultado observável:**

- o painel tem hierarquia de áreas clara (header, navegação, conteúdo, rodapé), **contraste real**
  entre a sidebar e o miolo, grupos recolhíveis com o grupo atual aberto e **auto-colapso
  controlado**, título de marca, atalho “Ir para o Portal”, menu de usuário e botão “Pesquisar”
  que abre a busca (o provedor de conteúdo entra no P7);
- listas e formulários usam os mesmos padrões (filtros, contagem, ordenação, ações rotuladas,
  barra de ações, estado “sujo”, feedback e erro) em todas as telas de conteúdo;
- tema e shell têm escala fechada de superfícies/identidade, **contraste medido**, escolha
  automática de texto sobre a cor da marca, prévia e restauração de padrões — sem CSS livre;
- existe uma tela de **Composição** que mostra as áreas canônicas do site (header, cabeçalho de
  conteúdo, laterais, miolo, rodapé de conteúdo e rodapé), a pilha de widgets, os menus, a
  identidade e o banner vigente, com atalhos para os editores canônicos e prévia por locale;
- o portal ganha cabeçalho/rodapé com identidade, contato, redes sociais, painel de acesso à
  informação, ações de conteúdo (imprimir/atualizado) e navegação móvel sem hover-only;
- o time de suporte tem uma **biblioteca de assets** (CSS/JS; imagens seguem no Media/Gallery)
  isolada, versionada e auditada, sem HTML/Blade e sem CDN — exceção controlada, nunca
  disponível ao cliente;
- navegação entre páginas usa View Transitions de forma progressiva, desligada por
  `prefers-reduced-motion` e pela camada `a11y`.

### 1.1 Requisitos do dono (2026-10-26)

- **Auto-colapso** da navegação lateral é obrigatório; não é mais rejeitado. Ele é controlado
  (pausa com foco/ponteiro, não fecha o grupo atual, respeita movimento reduzido).
- **Contraste real** entre a sidebar e o miolo, incluindo título e estado ativo — não duas
  superfícies claras iguais.
- **Cabeçalho completo:** título de marca, botão “Ir para o Portal”, ícone de usuário com menu,
  botão “Pesquisar” que ativa a busca (o provedor de conteúdo entra no P7).
- **SLE assets** é aceito pela empresa como recurso especial **exclusivo do time de suporte**;
  a tradução é uma biblioteca controlada de arquivos CSS/JS, sem HTML/Blade/snippet.

## 2. Por que esta fase vem antes do P6

As telas do P6 (categorias, tags, galeria, super-destaque, preview e prev/next) seriam a primeira
grande superfície editorial construída **depois** do P5. Cada uma herdaria os padrões atuais —
ícones sem rótulo, filtros sem resumo, formulários longos sem barra de ações, ausência de estado
“sujo” e uma navegação lateral plana. Corrigir depois custaria mais do que fechar o padrão agora.
A fase é **intermediária**: não reabre dados nem serviços, só consolida a camada de apresentação
que o P6 vai reutilizar.

## 3. O que foi estudado no V2 (evidência)

### 3.1 CMS (`sucesu-cms/V2/layouts/cms`)

- `layouts/app.blade.php`: shell Bootstrap 5 + jQuery + Bootstrap Icons; header, sidebar, footer,
  conteúdo; modal de busca global (`Ctrl+K`), modal de criação e galeria global. Muito do
  comportamento é interessante; a implementação não é porta de entrada (ADR-002/009).
- `layouts/header.blade.php`: atalho “Ir para o Portal”, controles de fonte A−/A+, alto contraste,
  menu de usuário e saída.
- `layouts/sidebar.blade.php`: grupos por módulo, **grupo atual** marcado, grupos recolhíveis com
  estado em `localStorage`, recolhimento automático após 3 s sem interação e `inert` no conteúdo
  quando a sidebar móvel está aberta.
- `dashboard.blade.php`: barra de boas-vindas, atalhos rápidos, cartões de estatística, treemap
  hierárquico em **ECharts** e ranking SEO em tabela.
- `partials/`: tabela com listras/hover, filtros com busca + filtros avançados colapsáveis, badge
  de status com ponto, estado vazio com ação, paginação, modal de confirmação, modal de sucesso de
  criação, alertas dispensáveis e botão com estado de carregamento.
- Tema do CMS (`components/seo-button.blade.php`, `layouts/app.blade.php`): marca, logo, favicon,
  cores de sidebar/header/body e folha de identidade injetada.

### 3.2 Portal (`sucesu-cms/V2/layouts/portal`)

- Áreas: barra de acessibilidade → header → banner global → header de conteúdo (breadcrumb,
  título/lead) → grade de três colunas (esquerda, conteúdo, direita) → rodapé de conteúdo →
  rodapé institucional → botão “voltar ao topo”.
- Header: logo, navegação de até três níveis, busca expansível e menu móvel off-canvas; links
  principais em caixa alta, com borda inferior no item atual.
- Rodapé: logos institucionais, colunas de links, contato, redes sociais, painel de transparência
  (LAI), copyright e metadados do CMS.
- Cores: marca (primária/secundária/acento/link), superfícies (body/canvas), semântica fixa e
  modo de alto contraste. A tipografia é Nunito Sans com títulos de peso 800; a barra de
  acessibilidade oferece contraste, dislexia, fonte, animações, destaque de links, guia de leitura
  e VLibras.
- Contraste medido da paleta padrão (WCAG 2.x):

| Par | Razão | Leitura |
|---|---:|---|
| `#1075ad` sobre branco | 5,04:1 | AA para texto normal |
| `#0a4c8c` sobre branco | 8,66:1 | AAA |
| `#6c757d` sobre branco | 4,69:1 | AA |
| `#6c757d` sobre `#f8f9fa` | **4,45:1** | **falha AA** para texto normal |
| `#fece50` sobre branco | 1,48:1 | só decorativo; nunca texto |
| branco sobre `#222d32` | 14,10:1 | AAA |
| amarelo sobre preto (alto contraste) | 19,56:1 | AAA |

  O caso `#6c757d` sobre canvas mostra que “cinza padrão” não é garantia: superfície e texto
  precisam ser medidos como par, não herdados de um preset.

### 3.3 `StructuralLandingEditor` (SLE)

O SLE é uma ferramenta de **suporte**, não um editor de conteúdo editorial. Ele tem cinco
submódulos e uma biblioteca de arquivos:

| Submódulo | O que faz | O que ensina |
|---|---|---|
| Meta | metadados, SEO, e-mail | configuração global em abas, com help e histórico |
| Tema | tema do portal, tema do CMS, logos, banner | identidade separada por área; prévia e “restaurar padrões” |
| Dados estruturais | links de header/footer, contato, redes, LAI | menus como árvore, contato como dado, LAI como lista |
| Features | VLibras, barra eMAG, widget de acessibilidade, LAI | toggles com efeito global e independente |
| Editor | HTML/Blade por área (`header`, `main_left`, `main_right`, `footer`, `css_head`, `css_foot`, `js_head`, `js_foot`) | abas por área, editor de código, aviso de alterações não salvas, histórico e manual |
| Assets | upload de CSS/JS/imagens e “copiar snippet” | biblioteca com tipos, mas também o maior risco de CSP/CDN |

O renderizador do SLE compila Blade e executa `eval()` sobre o conteúdo gravado no banco. Isso é
**incompatível** com ADR-017/020/028 e com a CSP estrita: não é traduzível. O que se traduz é a
**arquitetura de informação**: áreas nomeadas, conteúdo por área, prévia, estado “dirty”,
histórico, ajuda e atalhos — com dado estruturado no lugar do código.

### 3.4 O que o V7 já tem (não duplicar)

- Shell do CMS como dado (`CmsShellResolver`, ADR-036), sidebar esquerda/direita/orbital, menu
  agrupado pelo manifesto, paleta `Ctrl+K` com Popover nativo e `inert`/off-canvas móvel.
- Shell do portal com presets, regiões, grid, slots e widgets (ADR-016); páginas e home como
  documento de seções (P5); destaque/banner com vigência e mídia (P5).
- Configurações tipadas (`institution`, `theme`, `cms`, `support`) com schema, cache e auditoria;
  editores de tema e de shell; prévia de tema same-origin.
- Menus do site (3 níveis, rota viva, locale) via porta `Navigation`; Media/Gallery com
  `<picture>`; SEO de base; a11y em `@layer a11y`; auditoria e painel de suporte.
- Componentes `x-cms::*` (alert, badge, breadcrumb, button, card, confirm, data-table, empty-state,
  fieldset, filter-card, form-errors, form-group, modal, pagination, status-badge, table,
  table-actions) e `x-layout::*` (portal/cms/blocks/slot/widget).

### 3.5 Lacunas verificadas

- Navegação lateral **plana** (sem grupos recolhíveis/estado do grupo atual/auto-colapso) e sem
  atalho para o portal, menu de usuário e botão de busca no header; o menu móvel não prende foco.
- Sidebar e miolo compartilham a mesma superfície clara: falta contraste real de área, inclusive
  no título e no estado ativo.
- Dashboard mínimo (nome/versão/cards) sem boas-vindas, atalhos por permissão ou leitura de
  operação; nenhuma visão de “o que está no ar”.
- Não existe biblioteca de assets de suporte; o SLE do V2 resolvia isso com upload de CSS/JS +
  snippet, sem os controles necessários.
- Listas: ações de linha só com ícone + `sr-only`, sem contagem “x–y de z”, sem resumo de filtros
  ativos e sem cabeçalho fixo; filtros avançados não usam o padrão nativo.
- Formulários longos: sem barra de ações fixa, sem estado “sujo”, sem proteção contra duplo
  envio; o resumo de erro existe, mas o padrão não está aplicado em todas as telas.
- Tema: sem `on_primary`/contraste validado, sem superfícies/“tom” de área e sem restauração de
  padrões; o shell não distingue visualmente a navegação do miolo.
- Portal: rodapé sem contato/redes/LAI/menus de rodapé; sem ações de conteúdo (imprimir,
  atualizado); navegação móvel quebra linha em vez de ter um painel; sem View Transitions.
- Composição: não existe uma visão única das áreas/pilhas/menus/tema/banner; o SLE só existe como
  editor de código.
- Cores/contraste: a paleta do V2 ilustra o risco de cinza sobre canvas; o V7 ainda não testa
  pares de contraste do tema do tenant nem escolhe o texto sobre a cor da marca.

## 4. Tradução V2 → V7

| Referência V2 | Decisão no V7 | Justificativa |
|---|---|---|
| Bootstrap/jQuery/Bootstrap Icons | **Rejeitar**; Blade + Tailwind/daisyUI + Lucide vendorizado | ADR-002/009; sem dependência nova |
| Sidebar com grupos recolhíveis, grupo atual e persistência | **Traduzir** com `<details>` nativo + persistência opcional em JS | comportamento útil sem esconder a navegação de quem não usa JS |
| Recolhimento automático após 3 s | **Traduzir como auto-colapso controlado** | pausa com foco/ponteiro dentro, não fecha o grupo atual, sem animação em `prefers-reduced-motion` e com preferência manual persistida |
| Título/marca, “Ir para o Portal”, menu de usuário, busca | **Traduzir** | cabeçalho do painel precisa das mesmas rotas de contexto; a busca abre o painel nativo e o P7 troca o provedor de resultados |
| Sidebar escura × miolo claro | **Traduzir** com `sidebar_tone=inverted` como padrão | contraste de área real, medido (texto ≥ 7:1 e alvo ativo com par próprio) |
| “Ir para o Portal”, A−/A+, contraste | **Traduzir**; a barra de a11y já cobre fonte/contraste | atalho de contexto e controle do usuário |
| Dashboard com boas-vindas/atalhos/cards/treemap | **Traduzir** boas-vindas, atalhos e cards; **rejeitar ECharts** | gráfico exige biblioteca, canvas e alternativa acessível; ranking em tabela resolve |
| Tabela com listras/hover, status, vazio, paginação, confirmação | **Traduzir** nos componentes `x-cms::*` | consistência e a11y |
| Modal de sucesso de criação | **Traduzir** como alerta inline com link “ver no portal” | não bloqueia o fluxo nem exige `Esc` |
| Editor de código por área + `eval` | **Rejeitar** | ADR-017/020/028; CSP estrita e conteúdo como AST |
| Assets CSS/JS do SLE | **Aceitar como exceção controlada de suporte** (S6) | sem HTML/Blade/snippet, só arquivos same-origin versionados, MIME/limite/hash, CSP `'self'` e auditoria; nunca para o cliente |
| Abas por área, prévia, “dirty”, histórico, manual | **Traduzir** para formulários/seções, prévia autenticada, `beforeunload` e auditoria | mesma ergonomia, sem código no banco |
| Menus header/footer/LAI + contato + redes | **Traduzir** para Menus (`principal`, `rodape`, `transparencia`) + `institution` | uma verdade por dado; sem tabela nova |
| Logo/favicon/logos institucionais | **Traduzir** o essencial (marca do `institution`/SEO e mídia); múltiplos logos ficam para P14 | evita schema especulativo |
| Banner global com imagem, destino e vigência | **Reutilizar** `Destaques` + perfil de layout | P5 já entrega vigência, mídia, alt e slot |
| Features (VLibras/widget/LAI) | **Traduzir** como estado real do shell: a11y sempre, VLibras sob demanda, LAI por menu | não criar “flags engine” (ADR-034) |
| Cores livres + superfícies + tipografia 14–18 px | **Traduzir** para `ThemeSchema` fechado + tons de superfície + `on_primary` automático; manter escala fluida | contraste e WCAG 1.4.4 |
| Barra de a11y com destaque de links/guia de leitura | **Traduzir** destaque de links; guia de leitura fica no P26 | ganho real com pouco risco; overlay de guia é escopo de a11y |
| Busca expansível no header | **Adiar** para P7 (ADR-050) | não criar rota morta nem segunda busca |
| HTML de erro 403/404/500/503 estilizado | **Traduzir** só os estados que o shell já cobre; páginas globais ficam no P26 | evita duplicar tratamento de exceção |

## 5. Escopo fechado

### S1 — Fundamentos de áreas, superfícies e contraste

**Entrega:** tokens semânticos de área e superfície, escala fechada de tom/sombra, texto
automático sobre a cor da marca e teste de contraste. O CMS e o portal passam a compartilhar a
mesma gramática visual.

- Tokens em `src/layout/Assets/css/tokens.css` (e ponte para o daisyUI):
  `--surface`, `--surface-muted`, `--surface-raised`, `--surface-inverse`, `--ink`,
  `--ink-muted`, `--ink-inverse`, `--line`, `--line-strong`, `--focus-ring`, `--link-hover`,
  `--shadow-card`, `--area-header-bg`, `--area-sidebar-bg`, `--area-footer-bg`.
- `config/design.php` + `ThemeSchema`: acrescentar `surface_tone` (`plain|soft|tinted`),
  `shadow` (`flat|soft|raised`) e manter cor/raio/fonte existentes. A cor da marca continua
  livre dentro do schema; **não** se expõem superfícies livres.
- `ThemeTokens`: calcular `--tenant-on-primary`/`--tenant-on-accent` por contraste WCAG
  (`auto` = preto ou branco, o que passar; o padrão atual continua branco quando passa).
- `Layout\Engine\ThemeContrast` (novo, concreto): converte `hex`/`rgb`/`hsl`/`oklch` para
  luminância e compara pares; valor não suportado **não** derruba o request — cai no fallback e
  registra `theme.contrast_unsupported` sem PII.
- `config/cms.php` + `CmsShellSchema` + `CmsShellResolver`: `sidebar_tone`
  (`surface|inverted`), com par de texto/linha/ativo correspondente; **default `inverted`**
  para dar contraste real entre a navegação e o miolo (texto ≥ 7:1 e estado ativo com par
  próprio). `config('cms.nav_auto_collapse_ms')` declara o tempo do auto-colapso (default
  3000; `0` desliga).
- `@layer a11y` remapeia os novos tokens (contraste, dislexia, espaçamento, grayscale) sem
  tocar em marcação.
- Editores de tema/shell ganham os novos controles automaticamente (schema-driven), a prévia
  mostra os tokens salvos e uma ação **Restaurar padrões** (POST + CSRF + confirmação nativa)
  limpa os overrides da seção, com auditoria — sem `GET` mutando.
- O tema padrão e o “inverted” passam por teste de contraste (pares texto/fundo ≥ 4,5:1 para
  texto normal; ≥ 3:1 para texto grande/ícones conforme WCAG).

**Prova:** `DesignTokensTest`, `ThemeSchemaTest`, `ThemeContrastTest`, `CmsShellTest`,
`ConfiguracoesThemeTest`/`ConfiguracoesShellTest`; axe no CMS/portal; `sniff` sem `fail`.

### S2 — Shell do CMS: navegação, foco e operação

**Entrega:** o painel deixa de ter uma lista plana e passa a ter grupos navegáveis com
auto-colapso, contraste de área, título, atalho para o portal, menu de usuário, busca e dashboard
útil.

- `cms::partials.nav-items` (variante sidebar): grupo vira `<details>` com `<summary>` e o
  rótulo traduzido; o grupo da rota atual nasce `open`; item ativo recebe `aria-current="page"`.
  Sem JS, o usuário abre o grupo; com JS, o estado manual é persistido em `localStorage`.
- **Auto-colapso:** após `config('cms.nav_auto_collapse_ms')` sem interação, fecha os grupos
  abertos que **não** são o atual. O timer reinicia com ponteiro/foco/teclado dentro da sidebar
  e não fecha o grupo que contém o foco; `prefers-reduced-motion`/`data-a11y-motion` fecha sem
  animação; nenhuma preferência manual é sobrescrita silenciosamente.
- `cms::partials.header`: título de marca visível e com contraste próprio; link “Ir para o
  Portal” com ícone Lucide, texto visível, `target="_blank"`, `rel="noopener"` e aviso de nova
  aba; **menu de usuário** nativo (`<details>` ou Popover) com ícone, nome, papel/e-mail,
  perfil (quando houver rota) e saída; mantém a paleta `Ctrl+K`.
- **Busca:** botão “Pesquisar” com ícone e atalho abre o painel nativo de busca (o mesmo
  `cms-palette`, que já filtra navegação). O P7 troca/adiciona o provedor de resultados de
  conteúdo sem mudar o gatilho; nenhuma rota `/busca` é criada nesta fase.
- Sidebar móvel: ao abrir, `main`/`footer` ficam `inert`, o foco vai para o primeiro item e
  volta ao gatilho ao fechar; `Escape` e clique no overlay fecham; sem JS a navegação continua
  no fluxo.
- Dashboard: boas-vindas com nome/papel/e-mail (sem “último acesso” inventado), atalhos
  filtrados por permissão e rota viva (`noticias`, `paginas`, `destaques`, `dados-abertos`),
  cartões de métrica existentes e uma lista/tabela de leitura de operação — sem gráfico.
- `cms::partials.footer`: versão + link “voltar ao topo” nativo (`#cms-content`), sem JS.
- Foco visível e alvos ≥ 2,75 rem em ponteiro grosso; ordem de foco = ordem visual nas três
  posições de sidebar.

**Prova:** `CmsShellTest`, teste de `PanelMenu`/navegação, e2e desktop/mobile/no-JS; axe.

### S3 — Listas, filtros, formulários e feedback

**Entrega:** a mesma ergonomia em todas as telas de conteúdo e configuração.

- Tabelas (`x-cms::table`/`data-table`): cabeçalho fixo quando a tabela rola, `caption`,
  `aria-sort`, links de ordenação reais; ações de linha com rótulo visível no desktop e
  `sr-only` no mobile; linha inteira não é clique.
- Filtros (`x-cms::filter-card`): busca sempre visível; filtros avançados em `<details>` nativo;
  resumo dos filtros ativos com remoção individual; contagem “x–y de z” e página atual;
  `GET` compartilhável; “limpar” sem perder a rota.
- Estado vazio com ação primária e, quando houver filtro, ação “limpar filtros”.
- Formulários: `x-cms::form-actions` (barra de ações com primário/secundário, sticky no rodapé
  da tela quando o formulário é longo, sem `position: fixed` sobre conteúdo); atributo
  `data-dirty-guard` ativa `beforeunload` e indicador textual “alterações não salvas”; o envio
  duplo é bloqueado no `submit` (sem remover o botão que carrega `name`/`value`);
  `aria-busy` no formulário durante o envio; resumo de erro com foco no primeiro campo
  (comportamento atual) preservado.
- Alertas de sucesso: inline, com link “ver no portal” quando a entidade tem URL pública; nada
  de modal bloqueante.
- Adoção nas telas existentes: Notícias, Páginas, Home, Destaques, Dados Abertos, Menus,
  Configurações e Acesso (somente os componentes e atributos; nenhuma regra de negócio muda).

**Prova:** testes estruturais dos componentes, e2e do fluxo de lista/edição, teste de
`beforeunload`/duplo envio (Playwright), axe e no-JS.

### S4 — Composição do site (SLE traduzido sem código)

**Entrega:** uma tela **de leitura** que responde “o que está no ar e onde edito isso”, no lugar
do editor de HTML/CSS/JS do SLE.

- Rota `GET /cms/configuracoes/composicao` (nome `configuracoes.cms.composicao`), antes da rota
  dinâmica `/{section}`, com `configuracoes.ver`; controller invocável + Action de leitura.
- A Action reúne, sem importar módulo:
  - o perfil global `*` e os perfis de área via `LayoutProfileReader` (pilhas por slot, ordem,
    vigência, widget resolvido);
  - os menus `principal`, `rodape` e `transparencia` via porta `Navigation` (contagem e
    primeiros itens; vazio/inativo não quebra);
  - `institution` (contato, endereço, horário, redes) via `Settings`;
  - tema/shell resolvidos (tokens salvos, posição/tom da sidebar) via `ThemeResolver`/
    `CmsShellResolver`;
  - presença de banner por `WidgetRegistry`/perfil (sem consultar a tabela de Destaques).
- A view usa `<x-layout::cms>`, tabelas/descrições semânticas e links **reais** (`Route::has`)
  para: Menus, Layout da área, Aparência, Painel, Destaques, Configurações → Geral e a prévia da
  home em cada locale. Não há formulário de HTML, CSS, JS, Blade, `iframe` ou “copiar snippet”.
- Estados: sem menu de rodapé → explica como criar; módulo inativo → não aparece como link
  morto; sem banner vigente → “nenhum” (nunca um card vazio).
- Texto de política na tela: imagens vão para Media/Gallery; CSS/JS são arquivos versionados,
  não conteúdo do painel (ADR-002/020/028).
- O histórico continua sendo a trilha de auditoria existente; a tela só aponta para ela.

**Prova:** `ComposicaoTest` (permissão, menus vazios/ativos, módulo inativo, sem N+1, links
vivos, nenhum HTML/CSS no payload), e2e; axe.

### S5 — Portal: identidade, áreas e conteúdo

**Entrega:** cabeçalho, rodapé e ações de conteúdo com a riqueza do V2, sobre dados existentes.

- Header: marca de `institution.name` + símbolo Lucide; se `seo.site_logo` já estiver
  configurado, usa a URL same-origin com dimensões explícitas (sem schema novo de logos),
  navegação com indicador do item atual, skip links e painel móvel nativo
  (Popover/`<details>`) que não depende de hover; menu de até 3 níveis continua abrindo sem JS.
- Rodapé: colunas a partir do menu `rodape` (topo = coluna, filhos = links), contato/endereço/
  horário de `institution`, redes sociais de `institution.social` com ícones Lucide/Simple
  Icons vendorizados, painel “Acesso à Informação” a partir do menu `transparencia`, copyright e
  nota de versão. Sem menu, o rodapé degrada para marca + contato, sem wrapper vazio.
- `x-layout::content-actions`: link de voltar (URL de seção informada pela view, não
  `javascript:history`), botão Imprimir (enhancement JS, `hidden` sem JS; CSS de impressão
  continua valendo) e “Atualizado em” com `LocalizedDate` e a data do **dado**, não do deploy.
  Aplicado em Notícias, Páginas e Dados Abertos (detalhe).
- Barra de a11y: acrescentar “destacar links” (CSS em `@layer a11y`, estado no cookie) sem
  alterar a estrutura; guia de leitura fica no P26.
- Banner global: documentar e provar o caminho `Destaques` + perfil de layout; nenhum novo
  armazenamento.
- No CMS, o gatilho de busca já existe (S2); no portal, o campo só entra com o P7 (ADR-050), sem
  rota provisória nem link morto.

**Prova:** `LayoutAreaTest`/`LayoutContractTest`, `PortalHeaderFooterTest` (novo), e2e
desktop/mobile/no-JS, axe, `LocaleUrlInvariantTest`; nenhum link sem locale.

### S6 — Biblioteca de assets do suporte (exceção controlada)

**Entrega:** o recurso “SLE assets” do V2 traduzido como biblioteca **exclusiva do time de
suporte** — sem HTML/Blade, sem snippet e sem editor de código.

- Módulo/grupo `Suporte` (invisível e 404 para quem não é da equipe, ADR-033), permissões
  `assets.ver`/`assets.editar`; nenhuma permissão para cliente comum.
- **Arquivos** CSS/JS (e imagens via Media/Gallery, não duplicadas): upload no S3, caminho
  imutável `assets/support/{type}/{uuid}.{ext}`, checksum SHA-256, MIME pelo conteúdo,
  allowlist (`text/css`, `text/javascript`/`application/javascript`), limite de bytes e
  extensão coerente; HTML/SVG/PHP/`application/octet-stream` recusados.
- **Metadados** na seção `assets` de `core_settings` (schema fechado, ADR-046): lista ordenada
  de `{id,type,path,mime,size,checksum,name,placement,enabled}` com `placement` em
  `head|footer`; nada de HTML/CSS/JS no JSON além de caminho/checksum. A ordem é determinística.
- **Inclusão no HTML:** `<link rel="stylesheet">`/`<script defer src>` **same-origin**, com
  `?v={checksum}` e `integrity` quando o tipo permitir; nenhum `<style>`/`<script>` inline e
  nenhuma URL externa. CSP continua `'self'` (sem `unsafe-inline`/`unsafe-eval`).
- **Controles:** upload/remoção/ativação auditados (`assets.uploaded`/`assets.removed` etc.),
  remoção é arquivamento lógico (nunca apaga a versão), confirmação explícita e aviso de risco;
  o painel de suporte mostra o checksum e o estado. Não há “copiar snippet”.
- **Limpeza:** CSS com `@import`/URL externa é recusado; JS é aceito apenas por ser recurso de
  suporte — o risco residual é declarado e coberto por allowlist de acesso, auditoria e CSP.
- Antes de codar, registrar a emenda/ADR da exceção (ADR-020/022/002) e a decisão no `kd`;
  sem essa decisão aceita, a fatia não abre.

**Prova:** `SupportAssetsTest` (permissão, MIME/limite, path traversal, checksum, ordem,
placement, HTML sem inline, CSP headers), e2e do upload/attach, axe, auditoria.

### S7 — Fluidez, fechamento e evidência

**Entrega:** o comportamento de navegação e a prova final da fase.

- View Transitions **cross-document** no portal e no CMS com `@view-transition` e uma transição
  curta de opacidade; desligada em `prefers-reduced-motion`, em `data-a11y-motion="reduced"` e
  em navegadores sem suporte (fallback = navegação normal). Nenhum JS obrigatório.
- Rolagem suave apenas quando o usuário não pediu movimento reduzido; âncoras com
  `scroll-margin` (foco não obscurecido).
- Ajustes finais de foco, contraste, alvos e estados `hover/active/disabled` medidos com
  `sniff` desktop 1366×768 e mobile 390×844; `SNIFF_A11Y_STRICT=1` sem `warn` fora do ledger.
- Criar `_to_revision/P5.6-fluidez-cms-portal.md`, registrar na fila e seguir o PROTOCOLO;
  `make check` 2× + `E2E=1`; evidência no `kd` (tempos e contagens).

**Prova:** e2e de transição/reduced motion, `sniff`, axe, `make check` 2× + `E2E=1`, ficha de
revisão.

### Fora de escopo (explícito)

- Editor de HTML/Blade/CSS/JS no banco, `eval`, CodeMirror, snippets e biblioteca de código
  (ADR-017/020/028). O S6 aceita **arquivos** CSS/JS de suporte, nunca código editável no banco.
- Biblioteca de assets para o cliente ou por módulo; ela é exclusiva do grupo `Suporte`.
- Bootstrap, jQuery, ECharts, ícones de CDN e qualquer dependência nova de UI.
- Busca de **conteúdo** no portal (P7); no CMS o gatilho já abre a paleta. Guia de leitura/
  overlay (P26), múltiplos logos institucionais e parceiros (P14), páginas globais de erro
  estilizadas (P26).
- Mudança de dados de módulos, novo service, nova tabela ou “flags engine” de features.
- Dark mode, múltiplos temas simultâneos, editor visual de CSS, drag-and-drop de seções.

## 6. Contratos e dados

- **Sem tabela nova.** A composição lê `core_settings`, `layout_profiles`, `menus`/
  `menu_items`, `core_module_discovery_settings` e o catálogo de widgets; o banner continua em
  `destaques`. O S6 usa a seção `assets` de `core_settings` (schema fechado), com o binário no
  S3 — não uma tabela paralela.
- **Settings:** novas chaves apenas em `theme.portal` (`surface_tone`, `shadow`) e `cms`
  (`sidebar_tone`), validadas por `ThemeSchema`/`CmsShellSchema`; a seção `assets` (S6) segue o
  mesmo schema/auditoria. `config('cms.nav_auto_collapse_ms')` é configuração, não dado de
  tenant. Nenhuma chave livre.
- **Tema:** `--tenant-on-primary`/`--tenant-on-accent` são derivados, não digitados; o editor
  continua um controle por token.
- **Shell:** `sidebar_tone` é enum fechado; o DOM e a ordem de foco não mudam.
- **Menu:** `rodape` e `transparencia` são menus normais (slug único, 3 níveis, rota viva);
  não há coluna nova.
- **Rotas:** nova `configuracoes.cms.composicao` (GET, `configuracoes.ver`) e as rotas de
  assets do grupo `Suporte` (upload/stream/remoção, `assets.*`); as demais rotas existentes não
  mudam de método/URL.
- **Permissões:** `configuracoes.ver` para a composição; `assets.ver`/`assets.editar` para a
  biblioteca (grupo `Suporte`, invisível/404 para os demais); edição continua nas telas
  canônicas (`menus.editar`, `layout.editar`, `configuracoes.editar`, `destaques.editar`).
  Deny-by-default.
- **i18n:** chaves novas em `configuracoes::cms` (4 locales) e `layout::shell`/`layout::a11y`
  (4 locales); nenhuma string visível hardcoded.
- **Observabilidade/auditoria:** a composição é leitura (sem mutação); as telas canônicas já
  emitem span/log/auditoria. Nenhum PII.

## 7. Fatias de sessão

| Sessão | Entrega | Prova | Depende |
|---|---|---|---|
| **P5.6.S1** | tokens de área/superfície, `on_primary` automático, `surface_tone`/`shadow`, `sidebar_tone`, contraste | testes de schema/contraste; axe; `sniff` | P1 |
| **P5.6.S2** | sidebar em grupos `<details>`, grupo atual, auto-colapso, contraste/título, atalho do portal, menu de usuário, botão de busca, foco móvel, dashboard | `CmsShellTest`; e2e desktop/mobile/no-JS | S1 |
| **P5.6.S3** | tabelas/filtros/estado vazio, barra de ações, “dirty”, duplo envio, feedback, adoção nas telas | testes de componente; e2e; axe | S1 |
| **P5.6.S4** | tela “Composição” (áreas canônicas, menus, tema, banner, contato), atalhos e prévia por locale | `ComposicaoTest`; e2e; sem HTML/CSS | S2, S3 |
| **P5.6.S5** | header/rodapé do portal, contato/redes/LAI, painel móvel, ações de conteúdo, destaque de links | `PortalHeaderFooterTest`; e2e/axe; locale | S1, S3 |
| **P5.6.S6** | biblioteca de assets de suporte (CSS/JS, sem HTML/snippet), CSP/checksum/auditoria | `SupportAssetsTest`; e2e; axe | S1 |
| **P5.6.S7** | View Transitions, reduced motion, auditoria visual, ficha de revisão, 2× check + `E2E=1` | `make check` 2× + `E2E=1`; `sniff`; ficha | S1–S6 |

## 8. Riscos e mitigação

| Risco | Mitigação |
|---|---|
| Tema do tenant quebra contraste | `on_primary` automático + teste de contraste + axe; superfícies só em escala fechada |
| “Composição” virar um segundo editor de layout | tela **read-only**; edição só nas telas canônicas; teste que proíbe campos de HTML/CSS/JS |
| Regressão de a11y com animações/transições | View Transitions só com `prefers-reduced-motion: no-preference`; `data-a11y-motion` desliga; e2e |
| Navegação escondida atrás de `<details>`/auto-colapso | grupo atual aberto; timer pausa com foco/ponteiro dentro; sem JS o painel continua no fluxo; e2e no-JS |
| JS/CSS arbitrário da biblioteca de suporte | grupo `Suporte` invisível/404; MIME/limite/checksum; CSP `'self'` sem inline; auditoria; versão imutável; risco residual declarado |
| Botão de busca parecer “quebrado” antes do P7 | ele abre a paleta de navegação real; nenhuma rota provisória; P7 só adiciona o provedor de conteúdo |
| Escopo crescer para “dark mode”/temas | fora de escopo; um tema + a11y; nova decisão exigiria ADR |
| Listas/formulários migrados pela metade | S3 adota os componentes nas telas listadas e o teste estrutural cobra o padrão |
| Consultas extras na composição | uma leitura por fonte; sem import de módulo; teste de queries; cache do que já é cacheado |
| E2E instável por animação | desligar transições no contexto de teste; dados determinísticos; `fullyParallel: false` |

## 9. DoD

- [ ] Tokens/`ThemeSchema`/`CmsShellSchema` com escala fechada, `on_primary` e contraste testado.
- [ ] Sidebar em grupos com grupo atual, auto-colapso controlado, contraste real/título, atalho do portal, menu de usuário e botão de busca; foco móvel.
- [ ] Tabelas/filtros/formulários com barra de ações, “dirty”, duplo envio e feedback; adotados nas telas listadas.
- [ ] Tela de Composição read-only com as áreas canônicas, menus, tema, banner, contato e prévia por locale; sem HTML/CSS/JS no banco.
- [ ] Biblioteca de assets exclusiva do `Suporte` (CSS/JS), com CSP/checksum/auditoria e sem HTML/snippet.
- [ ] Portal com header/rodapé ricos (menus, contato, redes, LAI) e ações de conteúdo; sem link morto.
- [ ] View Transitions progressivas e reduced motion; `sniff` desktop/mobile sem `fail`.
- [ ] 4 locales, `route()`/`localized_url()`, nenhuma string visível hardcoded.
- [ ] `make check` 2× + `E2E=1`; PHPStan max 0; cobertura ≥90%; arch/name/migration/vendor/docs.
- [ ] Ficha `_to_revision/P5.6-fluidez-cms-portal.md` preenchida e registrada na fila.
- [ ] `kd` com épico/fatias fechados e evidência; `kd rewind`.

## 10. Fechamento — ficha de revisão

Na abertura da implementação, criar `_to_revision/P5.6-fluidez-cms-portal.md` com o cabeçalho
padrão, o inventário de arquivos e as seções S1–S7. Na revisão, aplicar o
[`PROTOCOLO`](../../_to_revision/PROTOCOLO.md), registrar cada achado no `kd` (`--anchor`) e só
mudar o status da fase depois da seção “Notas da revisão” preenchida.
