﻿/* =============================================================================
   VR7 — Design System (folha única, self-hosted, sob CSP — FR-198/FR-199)
   -----------------------------------------------------------------------------
   Fonte ÚNICA de decisões visuais: cor, tipografia, espaçamento, forma, foco,
   grade de formulário. Nenhuma tela define aparência isolada (FR-198). Toda cor
   nasce de token; paleta amostrada do manual da marca (preto + champanhe),
   portada do ciclo de identidade visual (FR-200a).

   Índice:
     1. Tokens (variáveis) — cores AA, tipografia, espaçamento
     2. Fontes self-hosted (@font-face) — sem CDN
     3. Reset/base do documento
     4. Shell (cabeçalho, main, rodapé)
     5. Componentes com classes estáveis (vr7-*)
     6. GRADE DE FORMULÁRIO (FR-203) — contrato verificado por teste de HTML
     7. Acessibilidade (foco visível, reduced-motion) e responsividade (FR-201/202)
   ========================================================================== */

/* 1. TOKENS — NÃO MORAM MAIS AQUI (§V, emenda constitucional v3.8.0) ---------
   Eles vivem em `vr7-tokens.css`, que é o arquivo de tokens **da casa**:
   carregado por este shell E pelo da superfície pública, com UM vocabulário
   para os dois. Esta folha passou a apenas **consumir**.

   Por que a mudança: até 2026-08-06 este `:root` declarava 36 variáveis que só
   a área autenticada lia, enquanto o `vr7-tokens.css` declarava outras 35 que
   só a vitrine lia — ≥19 pares de conceito duplicado, com nomes que colidiam e
   valores incompatíveis (`--vr7-esp-2` era 1rem; `--esp-2` é 0.5rem). Toda
   correção de contraste precisava ser feita duas vezes, e a segunda é a que se
   esquece: foi assim que o FAB de incidentes acabou com duas implementações
   concorrentes, uma em cada folha (spec 069.1 · FR-111).

   O §V exigia um ARQUIVO nomeado; passou a exigir UNICIDADE — um conjunto,
   todos os shells, sem camada de apelido. A redação antiga descrevia só este
   lado da casa e, por isso, não era violada pela divergência que existia.

   ⚠️ Ao acrescentar um token, acrescente-o LÁ. Declarar `:root` aqui devolve os
   dois vocabulários; `tests/test_padrao_tokens_unicos.py` não pega isso (ele
   caça o prefixo aposentado), mas a próxima correção de contraste pega — do
   jeito caro. */


/* 2. FONTES self-hosted (sem CDN — CSP). Os arquivos .woff2 entram em
   app/static/fonts/ no deploy; os @font-face ficam prontos e comentados para
   não referenciar asset ausente enquanto a pilha do sistema cobre.
   @font-face { font-family:"Inter"; src:url("../fonts/inter.woff2") format("woff2"); }
   @font-face { font-family:"Poppins"; src:url("../fonts/poppins.woff2") format("woff2"); } */

/* 3. RESET/BASE ------------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* `hidden` volta a esconder — e a ausência disto era um defeito SILENCIOSO
   (spec 073.1, 2026-08-11).

   O atributo `hidden` vale por uma regra de folha do NAVEGADOR
   (`[hidden] { display: none }`), de especificidade mínima. Qualquer classe
   desta folha que declare `display` a derrota: `.vr7-nota { display: block }`
   fazia um `<p class="vr7-nota" hidden>` aparecer na tela, com o atributo lá,
   escrito, dizendo o contrário do que acontecia.

   Foi assim que o aviso de "meio ainda não executado" apareceu numa tela sem
   destino nenhum escolhido — e o defeito não tem sintoma no código: o HTML
   servido está certo, o teste que procura `hidden` no corpo passa, e só a
   imagem de referência mostra. Vale para a casa inteira, não para esta spec:
   `.vr7-acoes-menu__lista`, os toasts e qualquer bloco futuro que combine
   classe com `hidden` dependem da mesma garantia.

   `!important` é deliberado e é o único jeito de a regra cumprir o que o
   atributo promete: ela precisa vencer QUALQUER `display` de classe, presente
   ou futuro, sem depender de ordem de declaração. */
[hidden] {
  display: none !important;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--cor-fundo);
  color: var(--cor-texto);
  font-family: var(--fonte-corpo);
  font-feature-settings: var(--num-tabular);
  line-height: 1.5;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

h1,
h2,
h3 {
  font-family: var(--fonte-display);
  color: var(--cor-marca-forte);
}

a {
  color: var(--cor-marca);
}
a:hover {
  color: var(--cor-marca-forte);
}

/* 4. SHELL ------------------------------------------------------------------ */
/* Todo layout com cabeçalho empurra o pop-up de feedback para BAIXO da barra —
   é o shell (e não cada tela) que declara o deslocamento, então vale no projeto
   inteiro. O layout de auth, que não tem cabeçalho, fica com o padrão do :root. */
.vr7-shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  --toast-topo: calc(var(--altura-cabecalho) + var(--esp-3));
}

.vr7-cabecalho {
  display: flex;
  align-items: center;
  gap: var(--esp-4);
  padding: var(--esp-3) var(--esp-6);
  border-bottom: 1px solid var(--cor-dourado); /* linha/detalhe dourado do cabeçalho */
  /* C3 — o cabeçalho não declarava posição na escala: a lateral lia
     `--cor-superficie`, o conteúdo usa `--cor-fundo`, e ele ficava no meio sem
     dizer o que era. Os dois passam a pedir o degrau `cromo`. A linha dourada
     acima NÃO é separação: é marca (README §6), e já foi apagada uma vez por
     um `border-bottom` de separação escrito por cima dela. */
  background: var(--camada-cromo-fundo);
  /* A sombra que existia aqui SAIU (spec 071 · research D-002): medido, o
     cabeçalho não adere ao topo na rolagem, e sombra é reservada ao que flutua
     sobre o conteúdo. Nada entra no lugar — a linha dourada acima já separa a
     barra do que vem abaixo, e acrescentar um `border-bottom` de separação aqui
     **sobrescreveria** aquela linha, apagando o detalhe da marca sem que
     nenhum teste acusasse. */
  position: relative;
  z-index: 5;
}

/* Logo inline recolorível (currentColor): herda a cor da MARCA do tema — creme
   no escuro, quase-preto no claro (E-004). Sem asset por tema, sem "flash". */
.vr7-logo {
  display: inline-flex;
  align-items: center;
  color: var(--cor-marca);
}
.vr7-logo img {
  height: 28px;
  width: auto;
  display: block;
}
.vr7-logo svg {
  height: 28px;
  width: auto;
  display: block;
}

.vr7-main {
  flex: 1 1 auto;
  width: 100%;
  max-width: var(--largura-conteudo);
  margin: 0 auto;
  padding: var(--esp-6);
}

.vr7-rodape {
  padding: var(--esp-4) var(--esp-6);
  border-top: var(--camada-separacao-borda);
  color: var(--cor-texto-2);
  font-size: 0.875rem;
}

/* ── spec 033 — rodapé da área logada: marca VR7 · versão · marca BSCode ──
   Três zonas numa linha; em tela estreita empilham centralizadas. A zona da
   versão fica no meio por `margin: auto` (não precisa de grid de 3 colunas,
   que quebraria quando uma das pontas some). */
/* Escrito do telefone para cima (spec 068 / FR-011a / FR-011b): o corte era
   `640px`, que é o mesmo valor de `40rem` escrito noutra unidade — a duplicata
   que a FR-011b manda eliminar. Agora a faixa telefone é o estado BASE e o
   `48rem` devolve a linha única, sem fronteira fracionária. */
.vr7-rodape:has(.vr7-rodape__versao) {
  display: flex;
  align-items: center;
  flex-direction: column;
  gap: var(--esp-2);
  text-align: center;
  padding-right: var(--esp-6);
}
.vr7-rodape:has(.vr7-rodape__versao) .vr7-rodape__versao {
  margin: 0;
}
@media (min-width: 48rem) {
  .vr7-rodape:has(.vr7-rodape__versao) {
    flex-direction: row;
    flex-wrap: wrap;
    gap: var(--esp-4);
    text-align: initial;
    /* O FAB "Reportar problema" (spec 017) é fixo no canto inferior direito e
       cobria a marca da BSCode, o item mais à direita do rodapé. Reservar espaço
       só à DIREITA tira a marca de baixo do botão sem engordar a barra (altura
       normal). O botão-pílula ocupa ~230px do canto; a reserva o folga.
       A reserva vive só desta faixa para cima porque no telefone o botão
       flutuante NÃO EXISTE — ele virou slot da barra inferior (068 / FR-017). */
    padding-right: 250px;
  }
  .vr7-rodape:has(.vr7-rodape__versao) .vr7-rodape__versao {
    margin: 0 auto;
  }
}
.vr7-rodape__marca-vr7 {
  color: var(--cor-texto-2);
}
.vr7-rodape__versao {
  margin: 0 auto;
  font-variant-numeric: tabular-nums;
}
/* Alvo de toque completo sem inflar a altura do rodapé: o padding é horizontal
   e a área clicável cresce pelo `inline-block`. */
a.vr7-rodape__versao {
  display: inline-block;
  padding: var(--esp-1) var(--esp-2);
  border-radius: var(--raio);
}
a.vr7-rodape__versao:hover {
  border-color: var(--cor-dourado);
}
a.vr7-rodape__versao:focus-visible {
  outline: var(--foco-anel);
  outline-offset: 2px;
}
/* O crédito da desenvolvedora (`.vr7-rodape__marca-parceiro`) mudou de folha em
   2026-08-07: foi para `vr7-tokens.css`, a compartilhada, quando a vitrine
   passou a exibi-lo também (069.1). Uma definição só — §V, reusar antes de
   criar. */

/* 4b. NAVEGAÇÃO DE NÍVEL BANCÁRIO (US-F / FR-204-FR-206) --------------------
   Menu lateral com grupos e ícones, busca global no cabeçalho, modo
   estendido/compacto e toasts de feedback. Só aparece quando `authenticated`. */

/* Ações do cabeçalho (busca, alternância de menu, identidade, sair) */
/* ── C1/C4 — a faixa vira três zonas, e ela encolhe ────────────────────────
   Eram seis controles heterogêneos numa fileira só, sem nada separando
   "identidade" de "contexto de trabalho" de "ferramentas". Agora são três
   zonas, e o que as separa é o intervalo: apertado DENTRO (`--esp-2`), largo
   ENTRE (`--esp-6`, no `gap` do próprio cabeçalho). Agrupar por distância não
   custa pixel nenhum de moldura — é a mesma gramática do degrau: quem se
   distingue por posição dispensa contorno.

   C4 — e a fileira passa a caber. O `min-width: 0` é o que faltava, e a razão
   é contraintuitiva: o padrão de um item flex é `min-width: auto`, que é a
   largura MÍNIMA DO CONTEÚDO. O `flex-shrink: 1` estava lá o tempo todo e
   nunca agiu, porque o piso era o conteúdo. Com o nome da loja sem truncar,
   esse piso era o nome inteiro — medido: 969 px de cabeçalho numa janela de
   768 com um nome longo, 880 com um curto, em 11 das 34 telas retratadas. */
.vr7-cabecalho__acoes {
  display: flex;
  align-items: center;
  gap: var(--esp-6);
  margin-left: auto;
  min-width: 0;
  flex-wrap: wrap;
}
.vr7-cabecalho__identidade,
.vr7-cabecalho__contexto,
.vr7-cabecalho__ferramentas {
  display: flex;
  align-items: center;
  gap: var(--esp-2);
  min-width: 0;
}
/* A zona do contexto é a que cede espaço: busca e seletor de loja são os dois
   controles cuja largura depende de CONTEÚDO, e são os únicos que podem
   encolher sem sumir. Identidade e ferramentas são ícones de tamanho fixo — o
   mínimo de toque não é negociável (FR-201).

   ⚠️ **Sem `overflow: hidden` aqui, e isto foi medido num retrato**: a primeira
   versão desta zona o declarava, e em 768 o botão "Buscar" simplesmente
   DESAPARECEU — cortado, não encolhido. A fileira passou a caber às custas de
   um controle que submete o formulário. Encolher é redistribuir espaço; cortar
   é esconder o problema atrás da moldura, e a imagem de referência teria
   congelado isso como se fosse o certo. */
.vr7-cabecalho__contexto {
  flex: 0 1 auto;
}
/* A ORDEM em que se cede espaço, declarada — sem ela o navegador escolhe, e
   escolhe pelo tamanho de cada um, que não é critério nenhum:
   1º o campo de busca (tem `placeholder`, e a página de busca tem um campo
      inteiro — é a mesma ordem que o telefone já seguia, FR-005);
   2º o nome da loja, que trunca com reticências;
   nunca o botão de submeter nem os alvos de toque. */
.vr7-busca-global {
  min-width: 0;
  flex: 1 1 auto;
}
.vr7-busca-global .vr7-btn {
  flex: 0 0 auto;
}
.vr7-seletor-loja {
  min-width: 0;
  flex: 0 1 auto;
}
.vr7-cabecalho__identidade,
.vr7-cabecalho__ferramentas {
  flex: 0 0 auto;
}
.vr7-usuario {
  color: var(--cor-texto-2);
}

/* ── spec 033 — menu do usuário (identidade + papel efetivo) ──
   Mesmo mecanismo do seletor de loja (019): `<details>` nativo, logo funciona
   sem JS. Reusa a base visual do gatilho do seletor; só o painel é novo. */
.vr7-usuario-menu {
  position: relative;
}
.vr7-usuario-menu__gatilho {
  display: inline-flex;
  align-items: center;
  gap: var(--esp-1);
  min-height: var(--alvo-toque);
  padding: 0 var(--esp-3);
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: var(--raio);
  color: var(--cor-texto);
  list-style: none;
}
.vr7-usuario-menu__gatilho::-webkit-details-marker {
  display: none;
}
.vr7-usuario-menu__gatilho::after {
  content: "▾";
  color: var(--cor-dourado);
}
.vr7-usuario-menu__gatilho:hover {
  border-color: var(--cor-borda);
}
.vr7-usuario-menu__gatilho:focus-visible {
  outline: var(--foco-anel);
  outline-offset: 2px;
}
.vr7-usuario-menu[open] .vr7-usuario-menu__gatilho {
  border-color: var(--cor-dourado);
}
/* ── spec 039 — central de notificações (sino no cabeçalho) ──
   Mesmo padrão do menu do usuário: `<details>` nativo, painel absoluto sob o
   gatilho. O contador é TEXTO (não pseudo-elemento) para o leitor de tela
   alcançá-lo junto do rótulo do `<summary>`. */
.vr7-notificacoes {
  position: relative;
}
/* ⚠️ `min-width` **junto** com `min-height`, e a ausência dela custou o achado:
   o gatilho media **38×44px** — alto o bastante e estreito demais (roteiro · A5,
   FR-201, 069.4). Alvo de toque é área, não altura: um dedo erra na horizontal
   tanto quanto na vertical, e o sino é o controle mais fino do cabeçalho porque
   veste só um ícone. */
.vr7-notificacoes__gatilho {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--esp-1);
  min-width: var(--alvo-toque);
  min-height: var(--alvo-toque);
  padding: 0 var(--esp-2);
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: var(--raio);
  color: var(--cor-texto-2);
  list-style: none;
}
.vr7-notificacoes__gatilho::-webkit-details-marker {
  display: none;
}
.vr7-notificacoes__gatilho:hover {
  color: var(--cor-marca);
}
.vr7-notificacoes[open] .vr7-notificacoes__gatilho {
  border-color: var(--cor-dourado);
}
.vr7-notificacoes__gatilho .vr7-icone {
  width: 1.25rem;
  height: 1.25rem;
}
/* Contador: pílula compacta em cor de atenção — o mesmo token de validação por
   campo, que é o único que o design system destina a chamar o olho em texto. */
.vr7-notificacoes__contador {
  min-width: 1.25rem;
  padding: 0 0.35rem;
  border-radius: 999px;
  background: var(--cor-erro);
  color: var(--cor-sobre-solido);
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.25rem;
  text-align: center;
}
.vr7-notificacoes__painel {
  position: absolute;
  right: 0;
  top: calc(100% + var(--esp-1));
  z-index: 30;
  min-width: 20rem;
  max-width: 24rem;
  padding: var(--esp-3);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  background: var(--cor-superficie);
  box-shadow: var(--sombra-suspensa);
}
.vr7-notificacoes__lista {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 18rem;
  overflow-y: auto;
  display: grid;
  gap: var(--esp-2);
}
.vr7-notificacoes__item {
  display: grid;
  gap: 0.15rem;
  padding: var(--esp-2);
  border-radius: var(--raio);
  background: var(--cor-superficie-2);
  border-left: 3px solid var(--cor-borda);
}
/* A categoria vira acento na borda — cor NUNCA é o único indicador: o texto da
   mensagem já diz o que aconteceu. */
.vr7-notificacoes__item--sucesso {
  border-left-color: var(--cor-sucesso);
}
.vr7-notificacoes__item--erro {
  border-left-color: var(--cor-erro);
}
.vr7-notificacoes__texto {
  color: var(--cor-texto);
}
.vr7-notificacoes__quando {
  font-size: 0.8125rem;
}
.vr7-notificacoes__vazio {
  margin: 0;
}
.vr7-notificacoes__acoes {
  display: flex;
  /* Desde a 067 são dois gestos, e eles pesam diferente: "Ver todas" é saída de
     leitura (à esquerda, discreta) e "Limpar" é a ação (à direita). */
  justify-content: space-between;
  align-items: center;
  gap: var(--esp-2);
  margin: var(--esp-3) 0 0;
}
/* "Há N avisos em outras lojas" (067 · FR-009a). Fica entre a lista e as ações
   porque explica a diferença entre o contador e o que está listado — lido
   depois dos itens, antes de decidir o que fazer. */
.vr7-notificacoes__outras {
  margin: var(--esp-2) 0 0;
  font-size: 0.8125rem;
}
/* A baixa individual do aviso persistido (067 · FR-015). Discreta: ela é a
   saída de quem já leu, não a ação principal do painel. */
.vr7-notificacoes__baixa {
  margin: var(--esp-1) 0 0;
}

.vr7-usuario-menu__painel {
  position: absolute;
  right: 0;
  top: calc(100% + var(--esp-1));
  z-index: 30;
  min-width: 15rem;
  padding: var(--esp-3);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  background: var(--cor-superficie);
  box-shadow: var(--sombra-suspensa);
}
.vr7-usuario-menu__nome {
  margin: 0;
  color: var(--cor-texto);
  font-weight: 600;
}
.vr7-usuario-menu__email {
  margin: var(--esp-1) 0 0;
  color: var(--cor-texto-2);
  font-size: 0.875rem;
  word-break: break-all;
}
/* Texto em `--cor-texto`, não em dourado: no tema claro o dourado dá ~3,3:1
   sobre o branco (abaixo de AA). O design system reserva dourado a acento
   (borda/foco) justamente por isso — aqui o acento é a borda superior. */
.vr7-usuario-menu__papel {
  margin: var(--esp-2) 0 var(--esp-3);
  padding-top: var(--esp-2);
  border-top: 1px solid var(--cor-dourado);
  color: var(--cor-texto);
  font-size: 0.875rem;
  font-weight: 600;
}
.vr7-usuario-menu__sair {
  width: 100%;
}

/* Item de ação do menu do usuário — hoje "Segurança da conta" (064.4 · FR-021).
   Nasceu com a entrada para a gestão de 2FA, que existia e era inalcançável.

   ⚠️ Não reusa `.vr7-acoes-menu__item`: aquele é do kebab de LISTAGEM, tem
   `role="menuitem"` e vive dentro de `<ul role="menu">`. Aqui o contêiner é um
   `role="group"` — usar a classe do outro sugeriria uma semântica de menu que a
   marcação não tem. */
.vr7-usuario-menu__item {
  display: block;
  padding: var(--esp-2) 0;
  color: var(--cor-texto);
  text-decoration: none;
  border-top: 1px solid var(--camada-separacao-borda);
}
.vr7-usuario-menu__item:hover {
  color: var(--cor-marca);
}

/* ── spec 019 — seletor de loja ativa (store switcher) no cabeçalho ── */
.vr7-seletor-loja {
  position: relative;
}
/* C4 — o rótulo trunca em vez de empurrar a página.
   As três declarações andam juntas e nenhuma age sozinha: `text-overflow`
   só atua sobre conteúdo que já transborda um bloco, o que exige
   `overflow: hidden` E `white-space: nowrap`. Cobrar uma delas deixaria passar
   as outras duas — e a largura do cabeçalho seguiria sendo a do nome da loja. */
.vr7-seletor-loja__atual-rotulo {
  display: inline-flex;
  align-items: center;
  gap: var(--esp-1);
  min-height: var(--alvo-toque);
  min-width: 0;
  max-width: 100%;
  padding: 0 var(--esp-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  color: var(--cor-texto);
  list-style: none;
}
/* O `<span>` do nome é quem carrega o texto; o `text-overflow` do pai só o
   alcança se ele também for um bloco que transborda. O ícone e a seta ficam de
   fora do encolhimento — são a parte que identifica o controle. */
.vr7-seletor-loja__atual-rotulo span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.vr7-seletor-loja__atual-rotulo::-webkit-details-marker {
  display: none;
}
.vr7-seletor-loja[open] .vr7-seletor-loja__atual-rotulo {
  border-color: var(--cor-dourado);
}
/* Painel do seletor de loja — harmonizado com o combobox do DS (spec 040):
   mesma elevação/borda/raio da .vr7-combobox__lista para uma língua visual
   única. A busca continua no servidor (não é o combobox; ver clarify 040). */
.vr7-seletor-loja__painel {
  position: absolute;
  z-index: 20;
  margin-top: var(--esp-1);
  right: 0;
  min-width: 18rem;
  max-width: 24rem;
  padding: var(--esp-3);
  background: var(--camada-cartao-fundo);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  box-shadow: var(--sombra-destacada);
}
/* O campo de busca vive num formulário para degradar sem JavaScript (064.4 ·
   `019·T034`). O painel é um bloco simples, então o formulário não muda o
   desenho — esta regra só impede que a margem padrão do agente de usuário
   apareça entre o campo e a lista. */
.vr7-seletor-loja__busca {
  margin: 0;
}
.vr7-seletor-loja__filtro {
  width: 100%;
  margin-bottom: var(--esp-2);
}
.vr7-seletor-loja__lista {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 18rem;
  overflow-y: auto;
}
.vr7-seletor-loja__lista li {
  margin: 0;
}
.vr7-seletor-loja__lista li .vr7-link {
  display: block;
  width: 100%;
  text-align: left;
  padding: var(--esp-2) var(--esp-2);
  border-radius: var(--raio);
}
.vr7-seletor-loja__lista li .vr7-link:hover {
  background: var(--cor-superficie-2);
}
.vr7-seletor-loja__nome {
  display: block;
}
.vr7-seletor-loja__doc {
  display: block;
  font-size: 0.8125rem;
  color: var(--cor-texto-2);
}
.vr7-seletor-loja__dica {
  margin-top: var(--esp-2);
}
.vr7-seletor-loja__atual {
  font-weight: 600;
  color: var(--cor-marca-forte);
}
.vr7-seletor-loja__atual .vr7-seletor-loja__nome {
  color: var(--cor-marca-forte);
}
.vr7-menu-toggle-form,
.vr7-logout-form {
  margin: 0;
}

/* ---------------------------------------------------------------------------
   Base de todo controle de formulário (design system — §V). Garante aparência
   consistente em QUALQUER contexto: cantos arredondados (--raio, nunca
   quadrado), altura de toque, borda e padding dos tokens. Regras contextuais
   (auth-card, formulario-grade, busca) só ajustam o que muda (largura, fundo),
   nunca redefinem a base — sem isto, um contexto novo (ex.: `.vr7-busca`)
   herdava um input cru/quadrado.
   -------------------------------------------------------------------------- */
input.campo,
select.campo,
textarea.campo {
  min-height: var(--alvo-toque);
  padding: var(--esp-2) var(--esp-3);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  background: var(--cor-superficie);
  color: var(--cor-texto);
  font: inherit;
  /* Um controle NUNCA empurra o contêiner (spec 068 / SC-004). Sem estas duas
     linhas, um `<select>` se dimensiona pela opção mais longa — "Robson Rogério
     Silva — 123.231.971-03" e afins — e, como item de grade ou de flex, o
     `min-width: auto` padrão o impede de encolher: ele vaza para fora da coluna,
     a página inteira ganha rolagem horizontal e o cabeçalho passa a "parar antes
     do fim da tela". Era o que o dono via nos cartões do pedido (2026-08-05).

     A regra vive AQUI, na base do componente, e não no cartão que reclamou: o
     mesmo `<select>` longo existe em cadastro de cliente, em fornecedor e em
     toda tela futura. O sintoma já tinha aparecido antes noutra ficha (ver o
     comentário de `.vr7-snapshot .vr7-dados`, que resolveu o caso dele sozinho)
     — corrigir só onde dói é como esta classe de defeito sobrevive. */
  max-width: 100%;
  min-width: 0;
}
/* type=search no WebKit vem com chrome nativo (cantos/lupa/limpar) que ignora o
   raio — neutraliza para herdar o arredondado do design system. */
input[type="search"].campo {
  -webkit-appearance: none;
  appearance: none;
}

/* Combobox do design system (spec 040) — <select> aprimorado com filtro/busca.
   Sem JS, o <select> nativo aparece normalmente; o ligarCombobox() marca o
   invólucro com --js, oculta o select (mantido no DOM como controle do form) e
   mostra a camada de busca. Tokens do DS; contraste AA nos 2 temas. */
.vr7-combobox {
  position: relative;
}
.vr7-combobox--js > select {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
.vr7-combobox__campo {
  width: 100%;
}
.vr7-combobox__lista {
  position: absolute;
  z-index: 30;
  left: 0;
  right: 0;
  top: calc(100% + 4px);
  max-height: 16rem;
  overflow-y: auto;
  margin: 0;
  padding: var(--esp-1);
  list-style: none;
  background: var(--camada-cartao-fundo);
  color: var(--cor-texto);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  box-shadow: var(--sombra-destacada);
}
.vr7-combobox__lista[hidden] {
  display: none;
}
.vr7-combobox__opcao {
  padding: var(--esp-2) var(--esp-3);
  border-radius: var(--raio);
  cursor: pointer;
}
.vr7-combobox__opcao[hidden] {
  display: none;
}
.vr7-combobox__opcao--ativa,
.vr7-combobox__opcao:hover {
  background: var(--cor-superficie-2);
}
.vr7-combobox__opcao[aria-selected="true"] {
  color: var(--cor-dourado-bri);
  font-weight: 600;
}
.vr7-combobox__vazio {
  padding: var(--esp-2) var(--esp-3);
  color: var(--cor-texto-2);
}
.vr7-combobox__vazio[hidden] {
  display: none;
}

/* Busca global do shell */
.vr7-busca-global {
  display: flex;
  align-items: center;
  gap: var(--esp-2);
  margin: 0;
}
/* 44px, do token, e não 2.25rem (36px). O desvio era invisível: a regra do
   estreito já pedia `--alvo-toque` no `__enviar`, mas `.vr7-busca-global
   .vr7-btn` tem DUAS classes e vencia por especificidade — o mínimo de toque
   estava escrito e nunca chegou a valer. E a altura casa: o cabeçalho é
   `calc(var(--alvo-toque) + var(--esp-3) * 2)`, então o campo passa a ocupar
   exatamente a faixa que ele reservou. */
.vr7-busca-global .campo {
  min-height: var(--alvo-toque);
  max-width: 16rem;
  /* C4 — o campo é o outro piso da fileira. Sem `min-width: 0` ele não desce
     abaixo da largura intrínseca do `placeholder`, e o `max-width` acima só
     limita para cima. */
  min-width: 0;
  padding: var(--esp-1) var(--esp-3);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  background: var(--cor-superficie);
  color: var(--cor-texto);
  font: inherit;
}
.vr7-busca-global .vr7-btn {
  min-height: var(--alvo-toque);
  padding: var(--esp-1) var(--esp-3);
}

/* Alternância do menu — ícone discreto no cabeçalho, ao lado do logo
   (padrão do legado: ação circular pequena na barra, não um botão grande). */
.vr7-menu-toggle-form {
  margin: 0;
}
.vr7-menu-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 44px, do token — não 2.5rem (spec 068 / FR-008 / research D15). Media 40px
     e ficava abaixo do mínimo de toque; é a classe DOS DOIS controles do
     cabeçalho (preferência de menu e tema), então o desvio valia em dobro. */
  width: var(--alvo-toque);
  height: var(--alvo-toque);
  padding: 0;
  /* C2 — era `border: 0` e `border-radius: 50%`: um círculo sem contorno
     encostado num campo de busca com borda e num seletor de loja com borda. O
     que incomodava não era o círculo, era ele ser a ÚNICA densidade diferente
     da faixa.
     O padrão certo já existia ao lado, no gatilho do sino: contorno
     RESERVADO em transparente, que aparece quando o controle abre. Adotá-lo
     aqui não acrescenta traço nenhum à tela em repouso — só faz os controles
     do cabeçalho lerem-se como a mesma família. */
  border: 1px solid transparent;
  border-radius: var(--raio);
  background: transparent;
  color: var(--cor-texto-2);
  cursor: pointer;
  transition:
    background-color 0.15s ease,
    color 0.15s ease;
}
.vr7-menu-toggle:hover {
  background: var(--cor-superficie-2);
  color: var(--cor-marca);
}
.vr7-menu-toggle .vr7-icone {
  width: 1.4rem;
  height: 1.4rem;
}

/* Corpo: menu lateral + conteúdo */
.vr7-corpo {
  flex: 1 1 auto;
  display: flex;
  width: 100%;
}

.vr7-sidebar {
  flex: 0 0 auto;
  /* B3 — era `15rem` escrita aqui. O valor mora no vocabulário desde a 069.4:
     a lateral acompanha a escala, com piso no que era antes. */
  width: var(--largura-lateral);
  border-right: var(--camada-separacao-borda);
  background: var(--camada-cromo-fundo);
  padding: var(--esp-4) var(--esp-3);
}
.vr7-nav-lista {
  list-style: none;
  margin: 0;
  padding: 0;
}
/* A3 — o grupo tinha só `margin-top`, e espaço já é o que separa os itens DE
   DENTRO do grupo: a mesma pista para duas fronteiras diferentes. A linha é o
   degrau de separação, que já divide a lateral do conteúdo.

   Sem regra de exceção para o primeiro: ele nunca é o primeiro filho do `<nav>`
   — antes dele vem sempre a `<ul>` com os itens soltos (Início, Suporte) —, e
   é justamente dessa lista que ele precisa se separar. Uma regra `:first-child`
   aqui seria CSS morto, e CSS morto sobrevive a revisões porque parece em uso. */
.vr7-nav-grupo {
  margin-top: var(--esp-6);
  padding-top: var(--esp-4);
  border-top: var(--camada-separacao-borda);
}
.vr7-nav-grupo__titulo {
  margin: 0 0 var(--esp-2);
  /* B2 — o título partia da coluna do ÍCONE, e o rótulo do item partia de
     outra: duas colunas de início na mesma lista. Agora ele parte da coluna do
     rótulo, e o ícone fica sendo a única coisa à esquerda dela.
     ⚠️ `research.md` · D-006 · B2 descreve isto ao contrário; medido na imagem
     de `painel/1440x900-claro.png`, é o título que estava com o ícone. */
  padding: 0 var(--esp-3) 0 var(--nav-recuo-rotulo);
  color: var(--cor-texto-2);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.vr7-nav-item {
  display: flex;
  align-items: center;
  gap: var(--esp-3);
  min-height: var(--alvo-toque);
  /* B1 — o padding vertical era `--esp-2` (8px) e nunca decidiu nada: o
     `min-height` de 44px domina, sempre. Quem fosse ajustar densidade mexeria
     no valor escrito, não veria efeito, e concluiria que o CSS não manda. O
     `min-height` é o mínimo de toque da FR-201 e fica; o padding zera na
     vertical e passa a governar só a horizontal. */
  padding: 0 var(--esp-3);
  border-radius: var(--raio);
  color: var(--cor-texto);
  text-decoration: none;
}
/* A1 — hover e ativo respondem a perguntas diferentes, e até a 069.4 diziam a
   mesma coisa: mesmo fundo, mesmo acento dourado, diferença só em peso de
   fonte. Numa lista de até 18 itens, passar o ponteiro produzia 18 respostas
   para "onde eu estou?".

   Medido (T053): os dois textos distam 1,13:1 no escuro e 1,16:1 no claro —
   e régua nenhuma podia acusar, porque `contraste.py` amostra o estado
   renderizado e nunca simula `:hover`.

   O fundo fica no hover (é resposta ao ponteiro, e some quando ele sai). O
   acento sai: ele passa a significar UMA coisa. */
.vr7-nav-item:hover {
  background: var(--cor-superficie-2);
}
.vr7-nav-item--ativo {
  background: var(--cor-superficie-2);
  color: var(--cor-marca-forte);
  font-weight: 600;
  box-shadow: inset 3px 0 0 var(--cor-dourado); /* item ativo marcado a dourado */
}
.vr7-nav-item--placeholder {
  color: var(--cor-texto-2);
  cursor: default;
}

/* Modo compacto: só os ícones. DOIS caminhos independentes levam a ele — a
   preferência de conta (`.vr7-menu-compacto`, computador — FR-006) e a largura
   da tela (rail do tablet, `48rem`–`64rem` — FR-013). Os dois vão continuar
   existindo; o que não pode existir em dois lugares é o VALOR da largura, e por
   isso ele é `--largura-rail` (spec 068 / research D7).

   O rótulo fica VISUALMENTE oculto, nunca `display: none` (FR-014): removê-lo
   da árvore de acessibilidade deixava cada item SEM NOME, e um leitor de tela
   anunciava o menu inteiro como "link, link, link". Era defeito preexistente
   deste bloco, e a 068 o corrige onde toca.

   A preferência só vale **do computador para cima** (`64rem`), e isso não é
   detalhe de implementação: ela é uma decisão tomada numa tela larga, para
   ganhar espaço de trabalho. No telefone o menu é um painel deslizante sobre a
   tela — espaço não falta ali, e um painel só com ícones é justamente o que o
   dono do projeto apontou como não identificável (2026-08-05). A preferência
   governa o computador; a largura governa o resto (FR-015). */
@media (min-width: 64rem) {
  .vr7-menu-compacto .vr7-sidebar {
    width: var(--largura-rail);
  }
  .vr7-menu-compacto .vr7-nav-rotulo,
  .vr7-menu-compacto .vr7-nav-grupo__titulo {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  .vr7-menu-compacto .vr7-nav-item {
    justify-content: center;
  }
}

/* ===========================================================================
   spec 068 — a área restrita no telefone
   ---------------------------------------------------------------------------
   Escrito DO TELEFONE PARA CIMA. O estado base é o telefone; `48rem` traz o
   tablet e `64rem`, o computador (FR-011a). Os dois cortes já existiam no
   arquivo — a spec não inventa corte novo, só passa a usá-los para a navegação.

   Mobile-first e não `max-width` por um motivo prático: um par
   `max-width: 47.9375rem` / `min-width: 48rem` cria fronteira fracionária que
   erra em zoom e em navegador que arredonda diferente. Com `min-width` a
   fronteira é única e nenhum pixel fica sem regra.
   =========================================================================== */

/* ── Gatilho do painel (telefone) ─────────────────────────────────────────
   `<summary>`: abre E fecha, sem requisição e sem gravar preferência (FR-003).
   É o ÚNICO elemento elástico do cabeçalho — encurta com reticências, nunca
   quebra em segunda linha (FR-005/FR-007). */
.vr7-nav-drawer {
  position: relative;
  /* Controle de tamanho fixo: o rótulo da seção não é exibido no telefone
     (degrau 6 do recolhimento declarado — decisão do dono, 2026-08-05), então
     não há nada elástico aqui. */
  flex: 0 0 auto;
}
.vr7-nav-drawer__gatilho {
  display: inline-flex;
  align-items: center;
  gap: var(--esp-2);
  min-width: var(--alvo-toque);
  min-height: var(--alvo-toque);
  max-width: 100%;
  padding: 0 var(--esp-2);
  border-radius: var(--raio);
  color: var(--cor-texto-2);
  cursor: pointer;
  /* Some o triângulo nativo do `<summary>`: o ícone de menu já é a afordância,
     e o marcador duplicaria o sinal num cabeçalho que não tem espaço sobrando. */
  list-style: none;
}
.vr7-nav-drawer__gatilho::-webkit-details-marker {
  display: none;
}
.vr7-nav-drawer__gatilho:hover {
  background: var(--cor-superficie-2);
  color: var(--cor-marca);
}
.vr7-nav-drawer__gatilho:focus-visible {
  outline: var(--foco-anel);
  outline-offset: 2px;
}
/* O nome da seção fica VISUALMENTE oculto no telefone — degrau 6 do
   recolhimento declarado (research D6), aplicado por decisão do dono do projeto
   em 2026-08-05: o cabeçalho já carrega a identidade da conta num botão ao lado
   do sino, e um segundo rótulo ali competia por atenção num espaço que não
   sobra. Foi o último degrau a ser usado, como estava escrito, porque é o único
   que degrada a FR-007 — e ele degrada só a metade **visual**: o `aria-label` do
   gatilho continua nomeando a seção, então o leitor de tela anuncia
   "Menu — Cadastros" como antes.

   Nunca `display: none`, pela mesma regra da FR-014: o texto continua no
   documento, e é dele que os testes de seção leem. */
.vr7-nav-drawer__secao {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* ── Os painéis do cabeçalho, no telefone ─────────────────────────────────
   Seletor de loja, sino e menu do usuário abrem ancorados ao gatilho
   (`position: absolute; right: 0`) com largura mínima de 15rem a 20rem. Isso
   funciona no computador, onde o gatilho tem espaço à esquerda; em 390px o
   gatilho fica a ~250px da borda e um painel de 20rem (320px) **transborda pela
   esquerda** — o dono viu o campo "Buscar por nome ou CNPJ…" cortado e o nome da
   loja começando fora da tela (2026-08-05).

   Na faixa telefone eles deixam de ser ancorados ao gatilho e passam a ser
   ancorados à **janela**: abaixo do cabeçalho, ocupando a largura disponível
   entre as duas margens. A altura é limitada e rola por dentro, para uma lista
   longa de lojas não empurrar a página. */
.vr7-notificacoes__painel,
.vr7-usuario-menu__painel,
.vr7-seletor-loja__painel {
  position: fixed;
  top: calc(var(--altura-cabecalho) + var(--esp-1));
  left: var(--esp-2);
  right: var(--esp-2);
  width: auto;
  min-width: 0;
  max-width: none;
  margin-top: 0;
  max-height: calc(100vh - var(--altura-cabecalho) - var(--esp-6));
  overflow-y: auto;
  overscroll-behavior: contain;
}

@media (min-width: 48rem) {
  /* Do tablet para cima cada painel volta a ser ancorado ao próprio gatilho,
     com as larguras que já tinha — repetidas aqui, uma a uma, porque são
     diferentes entre si e um valor genérico apagaria a diferença. */
  .vr7-notificacoes__painel,
  .vr7-usuario-menu__painel,
  .vr7-seletor-loja__painel {
    position: absolute;
    top: calc(100% + var(--esp-1));
    left: auto;
    right: 0;
    max-height: none;
    overflow-y: visible;
  }
  .vr7-notificacoes__painel {
    min-width: 20rem;
    max-width: 24rem;
  }
  .vr7-usuario-menu__painel {
    min-width: 15rem;
  }
  .vr7-seletor-loja__painel {
    top: auto;
    margin-top: var(--esp-1);
    min-width: 18rem;
    max-width: 24rem;
  }
}

/* ── Máscara ───────────────────────────────────────────────────────────────
   Começa ABAIXO do cabeçalho de propósito: se cobrisse a barra, o gatilho
   ficaria inalcançável — e sem JavaScript ele é a única saída (research D2).
   Camada 25, conforme o orçamento da D13. */
.vr7-nav-mascara {
  position: fixed;
  inset: var(--altura-cabecalho) 0 0 0;
  z-index: 25;
  background: var(--veu-suave);
}

/* ── O painel É a barra lateral, não uma cópia dela ───────────────────────
   O `<summary>` fica no cabeçalho (FR-019) e a navegação, no corpo: não são
   irmãos, e nenhum combinador de CSS atravessa isso. `:has()` é o que permite
   UMA navegação no DOM servindo painel e barra lateral (research D1).

   Tudo dentro de `@supports`, e isso NÃO é zelo decorativo: a regra que tira a
   navegação do fluxo e a que a traz de volta são a mesma decisão. Num navegador
   sem `:has()`, a primeira valeria sozinha e o telefone ficaria SEM NAVEGAÇÃO —
   troca de um defeito feio por um impeditivo. Sem suporte, nada é escondido e
   vale o comportamento anterior a esta spec: menu empilhado, feio e alcançável. */
@supports selector(:has(*)) {
  .vr7-sidebar {
    position: fixed;
    inset: var(--altura-cabecalho) auto 0 0;
    width: min(18rem, 82vw);
    z-index: 26;
    overflow-y: auto;
    overscroll-behavior: contain;
    border-right: var(--camada-separacao-borda);
    transform: translateX(-100%);
    transition: transform 0.18s ease;
  }
  .vr7-shell:has(.vr7-nav-drawer[open]) .vr7-sidebar {
    transform: none;
  }

  /* Do tablet para cima a navegação volta ao fluxo: só o que a fuga do fluxo
     alterou precisa voltar aqui dentro. A LARGURA de cada faixa fica fora do
     `@supports`, logo abaixo — ela não depende de `:has()`, e mantê-la aqui
     daria a um navegador antigo uma barra de 15rem com os rótulos escondidos
     pela faixa do rail: espaço gasto sem informação. */
  @media (min-width: 48rem) {
    .vr7-sidebar {
      position: static;
      inset: auto;
      z-index: auto;
      overflow-y: visible;
      transform: none;
      transition: none;
    }
  }
}

/* Largura por faixa — fora do `@supports`, porque não depende de `:has()`. */
@media (min-width: 48rem) {
  .vr7-sidebar {
    width: var(--largura-rail);
  }
}
@media (min-width: 64rem) {
  /* B3 — é AQUI que a largura do computador é decidida, e não na regra base.
     O `15rem` que estava escrito neste ponto sobrevivia a trocar o valor da
     base: o retrato de 1440 saiu idêntico depois do conserto, e foi medir a
     coluna do pixel que denunciou (a lateral seguia em 240 px). Duas escritas
     do mesmo valor, e só uma delas manda. */
  .vr7-sidebar {
    width: var(--largura-lateral);
  }
  .vr7-menu-compacto .vr7-sidebar {
    width: var(--largura-rail);
  }
}

/* ── Dentro do painel: um nível só, com os títulos fixos no rolar ─────────
   Com 18 itens no pior caso, recolher grupos cobraria um toque a mais de TODO
   MUNDO para resolver um problema que só existiria com o dobro de itens
   (FR-020). O `sticky` orienta sem cobrar nada. */
.vr7-sidebar .vr7-nav-grupo__titulo {
  position: sticky;
  top: 0;
  z-index: 1;
  padding-top: var(--esp-2);
  padding-bottom: var(--esp-1);
  background: var(--camada-cromo-fundo);
}

/* ── Trava de rolagem do fundo (FR-004a) ──────────────────────────────────
   Aplicada pelo `vr7.js` enquanto o painel está aberto. Sem ela, rolar o painel
   rola a página atrás — o defeito clássico deste padrão, que o legado já evitava
   (`blocked-scroll`). O `top` negativo é escrito em runtime, para a posição ser
   restaurada ao fechar. */
body.vr7-sem-rolagem {
  position: fixed;
  width: 100%;
  overflow: hidden;
}

/* ── Recolhimento declarado do cabeçalho (FR-005 / research D6) ───────────
   O cabeçalho DEVE caber em uma linha em 390px. O que não couber é recolhido de
   forma declarada — nunca quebrado em segunda linha por transbordo, que era o
   comportamento anterior (`flex-wrap: wrap`, removido do bloco de 40rem).

   Ordem em que os elementos cedem, escrita ANTES de alguém estar com o pixel na
   mão: (1) a busca vira lupa; (2) seletor de loja e menu do usuário perdem o
   texto VISUAL — nunca o nome acessível; (3) a preferência de menu não existe
   nesta faixa; (4) o tema migra para o rodapé do painel. Se ainda faltar
   espaço, os degraus seguintes são logo→marca e depois o rótulo da seção — nesta
   ordem, e só nesta. */
/* O orçamento do cabeçalho em 390px é apertado e foi medido, não estimado: com
   os valores anteriores sobravam **31px** para o rótulo da seção — duas ou três
   letras, que não dizem nada (relato do dono na tela, 2026-08-05). Os três
   ajustes abaixo devolvem ~46px, e "Cadastros" passa a caber inteiro.

   É o **degrau 5** do recolhimento declarado (research D6) aplicado ao ativo que
   temos: o logo do VR7 é um logotipo único, sem símbolo separável, então em vez
   de "só a marca" ele **encolhe**. O degrau 6 (esconder o rótulo, mantendo o
   nome acessível) continua guardado para o caso de ainda faltar espaço — e
   continua sendo o último, porque é o único que degrada a FR-007. */
.vr7-cabecalho {
  flex-wrap: nowrap;
  gap: var(--esp-2);
  padding: var(--esp-2) var(--esp-3);
}
.vr7-cabecalho__acoes {
  flex-wrap: nowrap;
  gap: var(--esp-1);
  /* As ações não encolhem nem crescem: são alvos de toque de tamanho fixo. Quem
     cede espaço é o rótulo da seção, que tem reticências. */
  flex: 0 0 auto;
}
.vr7-logo svg {
  height: 20px;
}
.vr7-busca-global__rotulo,
.vr7-seletor-loja__atual-rotulo span,
.vr7-usuario-menu__gatilho .vr7-usuario {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.vr7-busca-global .campo {
  display: none;
}
/* O gatilho do menu do usuário fica com o nome oculto nesta faixa; sem um
   símbolo ele viraria um botão vazio com uma setinha. */
.vr7-usuario-menu__icone {
  display: inline-flex;
  align-items: center;
}
.vr7-usuario-menu__gatilho {
  min-width: var(--alvo-toque);
  min-height: var(--alvo-toque);
}
.vr7-busca-global__enviar {
  min-width: var(--alvo-toque);
  min-height: var(--alvo-toque);
  padding: 0 var(--esp-2);
}
/* A preferência de menu é do COMPUTADOR (FR-006/FR-019): no telefone o lugar
   dela é ocupado pelo gatilho do painel, e no tablet a largura já decide. */
.vr7-menu-toggle-form--preferencia {
  display: none;
}
/* O tema sai do cabeçalho e reaparece no rodapé do painel (research D6). */
.vr7-tema-toggle-form--cabecalho {
  display: none;
}
.vr7-nav-drawer-tema {
  margin-top: var(--esp-6);
  padding-top: var(--esp-4);
  border-top: var(--camada-separacao-borda);
}
/* O controle reusa `.vr7-nav-item` para ficar idêntico aos vizinhos do painel —
   só o que muda de um `<button>` para um `<a>` é ajustado aqui, sem redefinir a
   base (§V). Estilo inline seria a saída fácil e é justamente a que a
   constituição proíbe. */
.vr7-nav-drawer-tema .vr7-nav-item {
  width: 100%;
  border: 0;
  background: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

@media (min-width: 48rem) {
  /* Tablet e computador: o painel não existe, e o cabeçalho volta ao normal. */
  .vr7-nav-drawer,
  .vr7-nav-drawer-tema {
    display: none;
  }
  .vr7-cabecalho {
    gap: var(--esp-4);
    padding: var(--esp-3) var(--esp-6);
  }
  /* C4 — aqui mora o defeito, e ele não era o `min-width`.
     A regra base do telefone declara `flex: 0 0 auto` nas ações, com razão
     escrita: no telefone quem cede espaço é o rótulo da seção, que tem
     reticências. Só que essa regra está FORA de `@media` e nunca era desfeita
     — então `flex-shrink: 0` valia também no tablet e no computador, onde o
     rótulo da seção nem existe e não há mais ninguém para ceder. A fileira
     empurrava a página, e a largura capturada acompanhava o nome da loja:
     969 px numa janela de 768.

     `0 1 auto` devolve o encolhimento a partir do tablet; o `min-width: 0` é o
     que faz o `flex-shrink` poder agir (o piso padrão de um item flex é a
     largura mínima do conteúdo, não zero). Os dois são necessários — nenhum
     dos dois basta. */
  .vr7-cabecalho__acoes {
    gap: var(--esp-6);
    flex: 0 1 auto;
    min-width: 0;
  }
  .vr7-logo svg {
    height: 28px;
  }
  .vr7-busca-global .campo {
    display: initial;
  }
  .vr7-usuario-menu__icone {
    display: none;
  }
  .vr7-tema-toggle-form--cabecalho {
    display: initial;
  }
  .vr7-busca-global__rotulo,
  .vr7-seletor-loja__atual-rotulo span,
  .vr7-usuario-menu__gatilho .vr7-usuario {
    position: static;
    width: auto;
    height: auto;
    /* `hidden`, e não `visible` — esta regra desfaz o ocultamento visual do
       telefone, e o `visible` que ela repunha apagava o truncamento do C4
       justamente na faixa onde ele importa. Texto que volta a aparecer volta
       com reticências, não empurrando a página. */
    overflow: hidden;
    clip: auto;
  }
  /* ⚠️ **O cabeçalho CEDE em altura, e não em conteúdo** (075, 2026-08-14 —
     conserto autorizado pelo dono, medido).

     Em `48rem` as três etiquetas voltam ao fluxo (nome do usuário, rótulo da
     busca, rótulo do seletor de loja) e o campo de busca aparece. Mas
     `.vr7-cabecalho__acoes` era `flex-wrap: nowrap` com `flex: 0 0 auto` — ele
     **não cedia de jeito nenhum**, e o bloco simplesmente passava da janela:

     | tela | vazamento | o que o cabeçalho carregava |
     | --- | ---: | --- |
     | `/contas-a-pagar` (lista, formulário e **ficha**) | 108px | busca, sino, menu |
     | `/loja/estornos` | **199px** | o mesmo **+ seletor de loja** |
     | qualquer uma em 390 ou 1440 | 0px | — |

     `span.vr7-usuario` sozinho mede 108px. A **ficha** não tem tabela de dados e
     vazava igual à lista — é o que prova que a causa era o cabeçalho, e não a
     tabela de alguma tela.

     ⚠️ **A primeira tentativa foi esconder as etiquetas em 768** (o *degrau 6*
     que a 068 guardou), e o hook de pre-push a reprovou com razão:
     `test_presenca_por_faixa` **declara** que naquela faixa a busca está
     presente e o ícone do menu está ausente. Esconder etiqueta ali não era
     conserto — era revogar, em silêncio, um contrato de outra spec.

     Deixar o bloco **quebrar linha** resolve o transbordo sem tirar nada de
     ninguém: todo controle continua presente, na mesma faixa, com o mesmo alvo
     de toque. O cabeçalho fica mais alto no tablet, e altura é o recurso que
     sobra — largura é o que falta.

     O degrau 6 continua guardado, para o dia em que a altura também acabar. */
  .vr7-cabecalho__acoes {
    flex-wrap: wrap;
    justify-content: flex-end;
    /* ⚠️ **`flex: 0 1 auto` + `min-width: 0` é o que faz a quebra ACONTECER.**
       Só `flex-wrap: wrap` não bastou, e o motivo é sutil: com `flex: 0 0 auto`
       o bloco tem a largura do próprio conteúdo e **nunca é estreitado** — e o
       que não é estreitado nunca quebra. Medido: os 108px continuaram
       idênticos.

       A 068 escreveu `flex: 0 0 auto` com a razão de que *"as ações não encolhem
       nem crescem: são alvos de toque de tamanho fixo"*. A razão continua de pé,
       e é justamente por isso que a quebra vem junto: quem cede é o **bloco**,
       que passa a ocupar duas linhas; os **itens** dentro dele mantêm o tamanho
       e o alvo de toque. Sem o `flex-wrap: wrap`, tornar isto encolhível
       espremeria os alvos — que é o que a 068 proibiu. */
    flex: 0 1 auto;
    min-width: 0;
  }
}

@media (min-width: 64rem) {
  /* Só no computador a preferência de menu compacto aparece (FR-019). */
  .vr7-menu-toggle-form--preferencia {
    display: initial;
  }
}

/* ── Barra inferior de atalhos (FR-016 / US5) ─────────────────────────────
   Só na faixa telefone. Camada 40, conforme o orçamento da D13: acima do
   conteúdo e sem disputar espaço com os pop-ups de feedback, que são ancorados
   no topo.

   Seis slots numa linha, sem rolagem horizontal e sem quebra (SC-013). Com
   ~65px por item o rótulo fica apertado — encurtá-lo é decisão declarada
   (FR-016c), barra que rola é defeito. Por isso `flex: 1 1 0` com `min-width: 0`
   em vez de largura mínima por item: todos cedem juntos, ninguém transborda. */
.vr7-barra-inferior {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 40;
  display: flex;
  align-items: stretch;
  background: var(--camada-cromo-fundo);
  border-top: var(--camada-separacao-borda);
  padding-bottom: env(safe-area-inset-bottom);
}
.vr7-barra-inferior__item {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  /* O alvo de toque continua sendo o token, mesmo com o rótulo espremido
     (FR-008 · FR-016c). */
  min-height: var(--alvo-toque);
  padding: var(--esp-1) 2px;
  color: var(--cor-texto-2);
  text-decoration: none;
  font-size: 0.6875rem;
}
.vr7-barra-inferior__item--ativo {
  color: var(--cor-marca-forte);
  font-weight: 600;
  box-shadow: inset 0 2px 0 var(--cor-dourado);
}
.vr7-barra-inferior__item:focus-visible {
  outline: var(--foco-anel);
  outline-offset: -2px;
}
.vr7-barra-inferior__rotulo {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* A barra cobre o fim do conteúdo se ninguém reservar o espaço dela. O token é
   o mesmo consumido pelo deslocamento da barra de ações do pedido.

   A reserva é INCONDICIONAL de propósito, e o refinamento vem depois: sem
   `:has()` sobra um espaço em branco no fim das telas anônimas (landing, login),
   que não têm barra. Errar para o lado do espaço sobrando é errar para o lado
   certo — o contrário esconderia a última linha de conteúdo atrás da barra. */
/* O padding do conteúdo no telefone mora AQUI, e não no bloco de 40rem onde
   morava — pela terceira vez nesta spec, uma regra escrita antes perdia para
   outra escrita depois. Lá o atalho `padding: var(--esp-4)` reescrevia as
   quatro bordas e apagava a reserva de espaço da barra inferior; o resultado na
   tela era a barra cobrindo o fim do conteúdo. Aqui as duas coisas vivem na
   mesma declaração e não há como uma anular a outra por descuido. */
.vr7-main {
  padding: var(--esp-4);
  /* ⚠️ A reserva de espaço da barra **saiu daqui** e foi para o `.vr7-rodape`
     (069.4, 2026-08-09), porque `.vr7-main` **não é o último elemento da
     página** — o rodapé é, e é ele que a barra cobria. Medido em 390×844: o
     rodapé terminava em 844 e a barra começava em 799, então os últimos 45px —
     onde mora o link da versão (spec 033) — ficavam atrás dela.

     A reserva aqui protegia o conteúdo e criava um vão **antes** do rodapé; a
     quarta ocorrência da mesma armadilha que os comentários acima registram
     três vezes. Espaço para elemento fixo se reserva no **fim do documento**,
     não no meio. */
  /* Última linha de defesa do SC-004 (zero rolagem horizontal em 390px), NÃO a
     correção: o que não pode vazar é corrigido na origem — hoje, na base do
     `.campo`. Este recorte existe para que um vazamento novo apareça como um
     canto cortado numa tela, e não como a página inteira rolando de lado com o
     cabeçalho terminando antes do conteúdo. Conteúdo largo de verdade (tabela)
     continua rolando no próprio contêiner, com `.vr7-rolagem-x`. */
  min-width: 0;
  overflow-x: clip;
}
/* A reserva, no fim do documento — que é onde a barra de fato cobre. */
.vr7-rodape {
  padding-bottom: calc(var(--altura-barra-inferior) + var(--esp-4));
}
@supports selector(:has(*)) {
  /* Shell **sem** barra (telas anônimas: landing, login) não reserva nada. Sem
     `:has()` sobra um espaço em branco no fim delas — errar para o lado do
     espaço sobrando é errar para o lado certo, como o comentário acima já
     dizia; o contrário esconderia o rodapé atrás de uma barra inexistente. */
  .vr7-shell:not(:has(.vr7-barra-inferior)) .vr7-rodape {
    padding-bottom: var(--esp-4);
  }
}

/* A colisão com a barra de ações do pedido (research D11) é resolvida na regra
   ORIGINAL de `.vr7-acoes-pedido`, mais abaixo neste arquivo — e não aqui. Uma
   regra nova neste ponto teria a mesma especificidade e **perderia por ordem de
   arquivo** para o `bottom: 0` de lá: pareceria resolvida sem estar. */

/* O botão flutuante de incidentes NÃO existe no telefone (FR-017) — mas a regra
   que o esconde mora JUNTO da definição dele, mais abaixo neste arquivo, e não
   aqui. Escrita neste ponto ela teria a mesma especificidade e perderia por
   ordem para o `display: inline-flex` de lá: foi exatamente o que aconteceu, e o
   dono do projeto viu os DOIS botões ao mesmo tempo no telefone. Segunda vez que
   a mesma armadilha morde nesta spec (a primeira foi `.vr7-acoes-pedido`). */

@media (min-width: 48rem) {
  /* Do tablet para cima: a barra some e o conteúdo recupera o espaço dela. */
  .vr7-barra-inferior {
    display: none;
  }
  .vr7-main {
    padding: var(--esp-6);
    overflow-x: visible;
  }
  /* ⚠️ E a reserva termina aqui também — a barra não existe nesta faixa, e
     56px de vão sob o rodapé é espaço morto.

     Antes da 069.4 isso acontecia **por acidente**: a reserva morava no
     `padding-bottom` do `.vr7-main`, e o `padding:` acima, sendo shorthand, a
     apagava sem que ninguém escrevesse nada. Ao mudar a reserva de elemento
     (para o fim do documento, que é onde a barra de fato cobre), o acidente
     não veio junto — medido na geração de 2026-08-10: toda tela autenticada
     ficou **56px mais alta** em 768 e 1440. Agora é explícito, e o que era
     implícito virou linha que se lê. */
  .vr7-rodape {
    padding-bottom: var(--esp-4);
  }
  .vr7-acoes-pedido {
    bottom: 0;
  }
}

/* ── Rail do tablet (FR-013 / FR-015) ─────────────────────────────────────
   A faixa `48rem`–`64rem` aplica o MESMO estado compacto, por largura. A
   preferência de conta não é lida nem escrita aqui: ela governa o computador, a
   largura governa o tablet. Quem volta ao computador reencontra a preferência
   dele intacta, porque ela nunca foi sobrescrita — só ficou irrelevante
   enquanto a largura mandava. */
@media (min-width: 48rem) and (max-width: 63.999rem) {
  .vr7-sidebar .vr7-nav-rotulo,
  .vr7-sidebar .vr7-nav-grupo__titulo {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  .vr7-sidebar .vr7-nav-item {
    justify-content: center;
  }
}

/* ── O rótulo oculto reaparece ao apontar OU focar (FR-014) ───────────────
   Os dois gestos, não um. `title` cobriria só o ponteiro — nunca aparece no
   foco por teclado — e ainda concorreria com o nome acessível, que alguns
   leitores anunciam em dobro e outros substituem. O `::after` com
   `attr(data-rotulo)` resolve os dois sem tocar na semântica: o nome continua
   vindo do texto do item, que segue no HTML.

   Vale nos DOIS caminhos do rail (preferência e largura), porque o problema é
   o mesmo: ícone sem legenda. */
.vr7-menu-compacto .vr7-nav-item,
.vr7-sidebar .vr7-nav-item {
  position: relative;
}
@media (min-width: 64rem) {
  /* Só onde o rótulo está escondido — e no computador ele só some por
     preferência. No telefone o painel mostra o rótulo, e a dica seria ruído. */
  .vr7-menu-compacto .vr7-nav-item:hover::after,
  .vr7-menu-compacto .vr7-nav-item:focus-visible::after {
    content: attr(data-rotulo);
    position: absolute;
    left: calc(100% + var(--esp-2));
    top: 50%;
    transform: translateY(-50%);
    z-index: 27;
    padding: var(--esp-1) var(--esp-3);
    border: 1px solid var(--cor-borda);
    border-radius: var(--raio);
    background: var(--cor-superficie-2);
    color: var(--cor-texto);
    white-space: nowrap;
    pointer-events: none;
  }
}
@media (min-width: 48rem) and (max-width: 63.999rem) {
  .vr7-sidebar .vr7-nav-item:hover::after,
  .vr7-sidebar .vr7-nav-item:focus-visible::after {
    content: attr(data-rotulo);
    position: absolute;
    left: calc(100% + var(--esp-2));
    top: 50%;
    transform: translateY(-50%);
    z-index: 27;
    padding: var(--esp-1) var(--esp-3);
    border: 1px solid var(--cor-borda);
    border-radius: var(--raio);
    background: var(--cor-superficie-2);
    color: var(--cor-texto);
    white-space: nowrap;
    pointer-events: none;
  }
}

/* Toasts de feedback (flash). `top` vem de --toast-topo: abaixo do cabeçalho
   onde há shell, no topo simples no layout de auth (que não tem barra). */
.vr7-toasts {
  position: fixed;
  top: var(--toast-topo);
  right: var(--esp-4);
  display: grid;
  gap: var(--esp-2);
  max-width: 24rem;
  z-index: 30;
  /* A pilha acumula: um pop-up sobrevive à navegação enquanto não expira nem é
     fechado, então várias ações seguidas empilham mensagens. Teto na altura da
     viewport (com rolagem própria) para a sequência nunca vazar tela afora e
     deixar um ✕ fora de alcance. */
  max-height: calc(100vh - var(--toast-topo) - var(--esp-4));
  overflow-y: auto;
}
.vr7-toast {
  display: flex;
  align-items: flex-start;
  gap: var(--esp-3);
  background: var(--cor-superficie-2);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  padding: var(--esp-3) var(--esp-4);
  transition: opacity 0.2s ease;
}
.vr7-toast__msg {
  flex: 1 1 auto;
  min-width: 0;
}
/* Fade-out de saída (spec 031). Sob prefers-reduced-motion a regra global
   (~L1051) zera a duração → remoção instantânea, sem 2ª media query. */
.vr7-toast--saindo {
  opacity: 0;
}
/* Botão de fechar do toast (spec 031). Alvo de toque cheio, recuado nas margens
   para não inflar o toast; nasce `hidden` (revelado pelo vr7.js) — sem JS, some.
   Foco reusa o anel global :focus-visible. */
.vr7-toast__fechar {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--alvo-toque);
  min-height: var(--alvo-toque);
  margin: calc(var(--esp-2) * -1) calc(var(--esp-2) * -1)
    calc(var(--esp-2) * -1) 0;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: var(--raio);
  color: var(--cor-texto-2);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}
.vr7-toast__fechar:hover {
  color: var(--cor-texto);
}

/* Resultados da busca global */
.vr7-resultados-lista {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--esp-2);
}
.vr7-resultado__link {
  display: flex;
  align-items: center;
  gap: var(--esp-3);
  padding: var(--esp-2) var(--esp-3);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  text-decoration: none;
  color: var(--cor-texto);
}
.vr7-resultado__link:hover {
  background: var(--cor-superficie-2);
}
.vr7-resultado__nome {
  flex: 1 1 auto;
}

/* Landing institucional (US-C) */
.vr7-landing {
  max-width: 52rem;
  display: grid;
  gap: var(--esp-4);
}
.vr7-landing__eyebrow {
  color: var(--cor-marca);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.875rem;
  margin: 0;
}
.vr7-landing__titulo {
  margin: 0;
}
.vr7-landing__lema {
  color: var(--cor-texto-2);
  font-size: 1.125rem;
  margin: 0;
}
.vr7-landing__destaques {
  color: var(--cor-texto);
  padding-left: var(--esp-6);
  display: grid;
  gap: var(--esp-2);
}
.vr7-landing__cta {
  margin-top: var(--esp-4);
}

/* 5. COMPONENTES estáveis (classes ancoráveis por teste — FR-200) ---------- */
.vr7-btn {
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--alvo-toque);
  padding: var(--esp-2) var(--esp-4);
  border: 1px solid var(--cor-marca-bronze);
  border-radius: var(--raio);
  background: var(--cor-superficie-2);
  color: var(--cor-marca-forte);
  font: inherit;
  cursor: pointer;
}
.vr7-btn:hover {
  border-color: var(--cor-dourado);
}
.vr7-btn--bloco {
  width: 100%;
}
/* Botão primário: preenchido em dourado (acento), texto escuro para AA.
   INVARIANTE (§V · spec 027 · specs/027-contraste-hover-botao-primario/contracts/botao-primario-estados.md):
   todo estado que altera o FUNDO deste botão DEVE re-fixar a COR DO TEXTO, com contraste
   AA (>=4.5) nos dois temas. Nunca depender de a cor herdada contrastar "por acaso" com
   o novo fundo (foi essa dependência que fez o rótulo sumir no hover do tema claro). */
.vr7-btn--primario {
  background: var(--cor-dourado-solido);
  border-color: var(--cor-dourado-solido);
  color: var(--cor-sobre-dourado);
  font-weight: 600;
}
/* Hover: clareia o dourado e re-fixa o texto escuro (AA: escuro 10.80 / claro 5.72).
   Antes usava --cor-marca-forte (preto no tema claro) sem re-fixar o texto → rótulo sumia. */
.vr7-btn--primario:hover {
  background: var(--cor-dourado-bri);
  border-color: var(--cor-dourado-bri);
  color: var(--cor-sobre-dourado);
}
/* Botão compacto (ações em linha, ex.: enviar documento do sócio). */
/* "Pequeno" é a **densidade tipográfica**, não o alvo de toque.
   `min-height: auto` derrubava o botão para **32px**, contra os 44 do
   `--alvo-toque` (FR-201) — achado da régua de componente sobre o sino aberto
   (069.4, 2026-08-09), onde ele veste "Marcar como lida" e "Limpar": dois
   controles que a pessoa toca de fato, no telefone.

   O desvio era **deliberado** e por isso durou: alguém escreveu `auto` para o
   botão ficar baixo. É a mesma correção que a 068 fez em `.vr7-menu-toggle`
   (40px) e em `.vr7-busca-global__enviar` (36px), e pelo mesmo motivo — o
   mínimo de toque não é estilo, é a FR-201.

   O que sobra de "pequeno" é o que deve sobrar: fonte menor e padding
   horizontal menor. Altura de alvo não é onde se economiza espaço. */
.vr7-btn--pequeno {
  min-height: var(--alvo-toque);
  padding: var(--esp-1) var(--esp-3);
  font-size: 0.9rem;
}
/* Botão de ação destrutiva (ex.: confirmar recusa) — preenchido com a cor de erro,
   texto claro. Re-fixa o texto ao mudar o fundo no hover (invariante §V/027). */
/* O fundo vem de `--cor-erro-solido`, e NÃO de `--cor-erro` (073.1): o segundo é
   o vermelho do TEXTO de erro, claro no tema escuro de propósito — como fundo
   com rótulo branco dava 3,08:1. Razão medida no token. */
.vr7-btn--perigo {
  background: var(--cor-erro-solido);
  border-color: var(--cor-erro-solido);
  color: var(--cor-sobre-solido);
  font-weight: 600;
}
.vr7-btn--perigo:hover {
  background: var(--cor-erro-solido);
  border-color: var(--cor-erro-solido);
  color: var(--cor-sobre-solido);
  filter: brightness(1.08);
}
/* Link de ação destrutiva (ex.: remover sócio) — cor de perigo da marca. */
.vr7-link--perigo {
  color: var(--cor-erro);
}
/* Formulário em linha: campo + botão lado a lado (upload na tabela de sócios). */
.formulario-inline {
  display: flex;
  gap: var(--esp-2);
  align-items: center;
  flex-wrap: wrap;
}

/* Abas de navegação entre etapas (revisão de aprovação, spec 015 — AW-002).
   Linha inferior hairline + indicador de aba ativa em dourado, mesmo padrão
   de acento usado nos demais estados de destaque do sistema. */
.vr7-abas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--esp-1);
  border-bottom: var(--camada-separacao-borda);
  margin-bottom: var(--esp-6);
}
.vr7-abas__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--alvo-toque);
  padding: var(--esp-2) var(--esp-4);
  margin-bottom: -1px; /* sobrepõe o hairline do container ao ficar ativa */
  border: none;
  border-bottom: 2px solid transparent;
  background: none;
  color: var(--cor-texto-2);
  font: inherit;
  font-size: 0.9375rem;
  cursor: pointer;
}
.vr7-abas__item:hover {
  color: var(--cor-marca-forte);
}
.vr7-abas__item--ativo {
  color: var(--cor-marca-forte);
  border-bottom-color: var(--cor-dourado);
  font-weight: 600;
}
/* A aba que ainda impede decidir (076, 2026-08-18 — pedido do dono).
   Reusa `--cor-erro`, o único token que este design system destina a chamar o
   olho em texto (é o mesmo do contador de notificações e o da validação por
   campo). Cor nova aqui seria vocabulário inventado numa tela.

   ⚠️ **A cor não é o aviso**, e por isso não há regra de tamanho ou peso além
   do ícone: quem não distingue a cor recebe o texto oculto que o template
   escreve ao lado, e é ele que carrega a informação. */
.vr7-abas__pendencia {
  display: inline-flex;
  align-items: center;
  margin-left: var(--esp-1);
  color: var(--cor-erro);
}
.vr7-abas__pendencia .vr7-icone {
  width: 1em;
  height: 1em;
}

.vr7-flash {
  border-radius: var(--raio);
  padding: var(--esp-3) var(--esp-4);
}
.vr7-flash--sucesso {
  color: var(--cor-sucesso);
}
.vr7-flash--erro {
  color: var(--cor-erro);
}

.vr7-vazio {
  color: var(--cor-texto-2);
  padding: var(--esp-8);
  text-align: center;
}

.vr7-erro {
  max-width: 36rem;
  margin: 0 auto;
  text-align: center;
}
.vr7-erro__codigo {
  font-family: var(--fonte-display);
  font-size: 3rem;
  color: var(--cor-marca);
}
/* As ações da página de erro. A classe era **citada pelo template e não
   existia** — os controles caíam no fluxo do parágrafo, encostados um no outro,
   e a página parecia mal acabada assim que ganhou a segunda ação (064.3).
   Só layout: nenhuma cor, borda, raio ou sombra nova — o degrau é do
   `.vr7-btn`. */
.vr7-erro__acoes {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--esp-3);
  margin-top: var(--esp-4);
}

/* Autenticação (US-D/E-005) — página standalone (auth/_base_auth.html), sem o
   shell autenticado: cartão centrado na viewport sobre chevrons animados da marca.
   `.vr7-auth` ocupa a tela inteira e centra o cartão; `.vr7-auth-card` é o cartão. */
.vr7-auth {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: var(--esp-6);
  background: var(--cor-fundo);
}
/* Chevrons da marca atrás do cartão — dourado (acento), discretos.
   "Andam" desenhando-se em cascata (como na landing); começam à ESQUERDA da
   página e o traço corre até a direita, parando lá. */
.vr7-auth-chevrons {
  position: absolute;
  top: -2%;
  left: 0; /* ocupa a largura toda: da esquerda até a borda direita */
  width: 100%;
  height: auto;
  pointer-events: none;
  opacity: 0.4;
}
.vr7-auth-chevrons path {
  fill: none;
  stroke: var(--cor-dourado); /* dourado da marca, não o champanhe claro */
  stroke-width: 1.1;
  stroke-dasharray: 3000;
  stroke-dashoffset: 3000;
  animation: vr7-tracar 2.6s cubic-bezier(0.6, 0, 0.2, 1) forwards;
}
/* Traço em cascata: cada chevron começa um pouco depois do anterior. */
.vr7-auth-chevrons path:nth-child(1) {
  animation-delay: 0s;
}
.vr7-auth-chevrons path:nth-child(2) {
  animation-delay: 0.12s;
}
.vr7-auth-chevrons path:nth-child(3) {
  animation-delay: 0.24s;
}
.vr7-auth-chevrons path:nth-child(4) {
  animation-delay: 0.36s;
}
.vr7-auth-chevrons path:nth-child(5) {
  animation-delay: 0.48s;
}
.vr7-auth-chevrons path:nth-child(6) {
  animation-delay: 0.6s;
}
.vr7-auth-chevrons path:nth-child(7) {
  animation-delay: 0.72s;
}
.vr7-auth-chevrons path:nth-child(8) {
  animation-delay: 0.84s;
}
@keyframes vr7-tracar {
  to {
    stroke-dashoffset: 0;
  }
}
.vr7-auth-card {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 26rem;
  display: grid;
  gap: var(--esp-4);
  padding: var(--esp-8);
  background: var(--cor-superficie);
  border: 1px solid var(--cor-borda);
  border-top: 3px solid var(--cor-dourado); /* acento dourado no topo do cartão */
  border-radius: var(--raio);
}
.vr7-auth-card--largo {
  max-width: 34rem;
}

.vr7-auth-marca {
  display: grid;
  justify-items: center;
  gap: var(--esp-2);
  text-align: center;
}
.vr7-auth-marca img {
  width: auto;
}
/* Logo inline recolorível no cartão de auth — herda a cor da marca do tema. */
.vr7-auth-marca .vr7-logo {
  color: var(--cor-marca);
}
.vr7-auth-marca .vr7-logo svg {
  height: 44px;
  width: auto;
  display: block;
}
.vr7-auth-tagline {
  margin: 0;
  color: var(--cor-texto-2);
  font-size: 0.9375rem;
}

.vr7-auth-titulo {
  margin: 0;
  text-align: center;
  font-family: var(--fonte-display);
  color: var(--cor-marca);
}
.vr7-auth-subtitulo {
  margin: 0;
  text-align: center;
  color: var(--cor-texto-2);
}
.vr7-auth-nota {
  font-size: 0.875rem;
}

.vr7-auth-form {
  display: grid;
  gap: var(--esp-4);
}
.vr7-campo-grupo {
  display: grid;
  gap: var(--esp-2);
}

/* Campos dentro do cartão (fora da grade FR-203) herdam a mesma aparência. */
.vr7-auth-card .campo,
.vr7-campo-grupo .campo {
  width: 100%;
  min-height: var(--alvo-toque);
  padding: var(--esp-2) var(--esp-3);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  background: var(--cor-fundo);
  color: var(--cor-texto);
  font: inherit;
}
.vr7-campo-grupo .rotulo {
  color: var(--cor-texto-2);
}
/* Dentro do cartão a grade FR-203 empilha (rótulo acima do campo). */
.vr7-auth-card .formulario-grade {
  grid-template-columns: 1fr;
  max-width: none;
  gap: var(--esp-2);
}
.vr7-auth-card .formulario-grade .acoes {
  display: grid;
  gap: var(--esp-3);
}

/* Campo de senha com botão mostrar/ocultar (revelado por JS; degrada sem ele). */
.vr7-campo-senha {
  position: relative;
  display: flex;
}
.vr7-campo-senha .campo {
  padding-right: 5rem;
}

/* Aviso de Caps Lock sob o campo de senha. Usa a cor de validação por campo
   (--cor-erro, o único token do sistema para chamar atenção em texto — dourado
   é acento e não vai em texto pequeno). `:empty` o some quando não há aviso,
   que é também o estado sem JS: o elemento nasce vazio e some sozinho, sem
   `hidden` e sem reservar espaço (item de grid com display:none não gera gap). */
.vr7-capslock {
  margin: 0;
  color: var(--cor-erro);
  font-size: 0.875rem;
}
.vr7-capslock:empty {
  display: none;
}
/* Mostrar/ocultar senha.
   Duas correções da spec 069.0 (T031), ambas achadas pela régua de navegador
   — a primeira tela que a plataforma abriu para se olhar:

   1. `color` era `--cor-dourado`, e o comentário do próprio token já dizia
      "SÓ acento (bordas, foco, linhas, hover); **nunca texto pequeno**".
      Isto é texto pequeno. Dava 4,25:1 no tema claro, abaixo dos 4,5:1 da
      WCAG AA — o axe concordou. A regra estava escrita há tempos; o que
      faltava era alguém medindo a tela renderizada.
   2. Altura de 32px, abaixo do `--alvo-toque` de 44px que a FR-201 exige.
      É a medida que a 068 delegou à conferência humana e que agora tem
      mecanismo.

   `--cor-texto-2` é o token de texto secundário, declarado AA (~7:1) nos dois
   temas — semanticamente o certo para uma afordância pequena dentro do campo,
   e o sublinhado continua sinalizando que é acionável. */
.vr7-senha-toggle {
  position: absolute;
  right: var(--esp-2);
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  min-height: var(--alvo-toque);
  background: none;
  border: 0;
  color: var(--cor-texto-2);
  cursor: pointer;
  font: inherit;
  text-decoration: underline;
  padding: 0 var(--esp-1);
}

.vr7-auth-links {
  margin: 0;
  display: flex;
  justify-content: flex-end;
}
.vr7-auth-links--centro {
  justify-content: center;
  gap: 0.35rem;
  margin-top: 1rem;
}

/* ── Minhas lojas — cartões (spec 013 / R-101) ──────────────────────────────
   Grade responsiva de cartões; cada um com nome, documento, badge de estado e
   ação contextual. Cores do estado por token da marca, funcionam nos 2 temas. */
.vr7-lojas-grade {
  list-style: none;
  margin: var(--esp-4) 0 0;
  padding: 0;
  display: grid;
  gap: var(--esp-4);
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
}
.vr7-loja-card {
  display: grid;
  gap: var(--esp-2);
  align-content: start;
  padding: var(--esp-4);
  background: var(--cor-superficie);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
}
.vr7-loja-card__topo {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--esp-2);
}
.vr7-loja-card__nome {
  margin: 0;
  font-family: var(--fonte-display);
  font-size: 1.1rem;
  line-height: 1.25;
  color: var(--cor-marca);
}
.vr7-loja-card__doc {
  margin: 0;
  color: var(--cor-texto-2);
  font-size: 0.9rem;
}
.vr7-loja-card__motivo {
  margin: 0;
  font-size: 0.85rem;
  color: var(--cor-texto-2);
  padding: var(--esp-2);
  background: var(--cor-superficie-2);
  border-radius: var(--raio);
}
.vr7-loja-card__acao {
  font-size: 0.9rem;
  font-weight: 600;
}
.vr7-loja-card__prazo {
  margin: 0;
  font-size: 0.85rem;
  color: var(--cor-marca-bronze);
}

/* Documento legal (termos/privacidade) — leitura confortável. */
.vr7-doc-legal {
  max-width: 52rem;
}
.vr7-doc-legal ul {
  margin: var(--esp-3) 0;
  padding-left: var(--esp-6);
  display: grid;
  gap: var(--esp-2);
}
.vr7-doc-legal p {
  margin: var(--esp-3) 0;
  line-height: 1.6;
}
.vr7-doc-legal h2 {
  margin: var(--esp-6) 0 var(--esp-2);
  font-size: 1.15rem;
  color: var(--cor-marca-forte);
}
.vr7-doc-legal h2:first-of-type {
  margin-top: var(--esp-4);
}
.vr7-doc-legal li {
  line-height: 1.55;
}
.vr7-doc-legal .vr7-doc-nota {
  font-size: 0.9rem;
  color: var(--cor-texto-2);
  border-left: 3px solid var(--cor-borda);
  padding-left: var(--esp-3);
}

/* Aceite de termos — checkbox alinhado ao texto. */
.vr7-aceite {
  display: flex;
  align-items: flex-start;
  gap: var(--esp-2);
  cursor: pointer;
}
.vr7-aceite input {
  margin-top: 0.2rem;
  flex-shrink: 0;
}

/* Badge de estado — pílula colorida por semântica. */
/* Nota de apoio ao lado de um selo de estado (spec 043 · FR-016/FR-017).
   Um fato **acessório** do que o selo já diz — "paga em tal dia, creditado tanto"
   —, e por isso em segundo plano: se disputasse atenção com o estado, a leitura
   da parcela passaria a exigir duas paradas em vez de uma. Bloco próprio porque
   ela nasce embaixo do selo, e não ao lado. */
.vr7-nota {
  display: block;
  margin-top: var(--esp-1);
  font-size: 0.75rem;
  line-height: 1.35;
  color: var(--cor-texto-2);
  white-space: normal;
}

.vr7-estado {
  flex-shrink: 0;
  display: inline-block;
  white-space: nowrap;
  padding: 0.15rem var(--esp-2);
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  border: 1px solid transparent;
}
.vr7-estado--aprovada {
  color: var(--cor-sucesso);
  border-color: var(--cor-sucesso);
  background: color-mix(in srgb, var(--cor-sucesso) 12%, transparent);
}
.vr7-estado--rejeitada {
  color: var(--cor-erro);
  border-color: var(--cor-erro);
  background: color-mix(in srgb, var(--cor-erro) 12%, transparent);
}
.vr7-estado--pendente {
  color: var(--cor-marca-bronze);
  border-color: var(--cor-dourado);
  background: color-mix(in srgb, var(--cor-dourado) 14%, transparent);
}
.vr7-estado--rascunho {
  color: var(--cor-texto-2);
  border-color: var(--cor-borda);
}

.vr7-vazio-card {
  margin-top: var(--esp-4);
  padding: var(--esp-8);
  border: 1px dashed var(--cor-borda);
  border-radius: var(--raio);
  text-align: center;
}
.vr7-vazio-card p {
  margin: 0 0 var(--esp-2);
}

/* Feedback ao vivo de senha (só UX; o servidor revalida). Cada critério nasce
   pendente e vira "ok" quando o JS (`senha-forca.js`) o satisfaz. */
.vr7-senha-criterios {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
  font-size: 0.85rem;
  display: grid;
  gap: 0.2rem;
}
.vr7-criterio {
  color: var(--cor-erro);
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
}
.vr7-criterio::before {
  content: "✕";
  font-weight: 700;
}
.vr7-criterio--ok {
  color: var(--cor-sucesso);
}
.vr7-criterio--ok::before {
  content: "✓";
}
.vr7-auth-acoes {
  display: flex;
  gap: var(--esp-3);
}
.vr7-auth-acoes .vr7-btn {
  flex: 1 1 auto;
}
.vr7-auth-acoes--centro {
  justify-content: center;
}

/* Entrada estilo OTP: dígitos espaçados e centralizados (sem 6 caixas — CSP). */
.vr7-otp {
  font-family: var(--fonte-display);
  font-size: 1.25rem;
  letter-spacing: 0.4em;
  text-align: center;
}

/* Avisos, QR, segredo, apps e link (compartilhados no fluxo de auth). */
.vr7-aviso {
  border-radius: var(--raio);
  padding: var(--esp-3) var(--esp-4);
  background: var(--cor-superficie-2);
  color: var(--cor-texto-2);
}
.vr7-aviso--ok {
  /* ⚠️ **`--cor-sucesso-texto`, não `--cor-sucesso`** — o mesmo par de papéis que
     `.vr7-indicador--ativa` já usa, e a nota está no token. `--cor-sucesso`
     existe para pintar selo, borda e fundo esmaecido; aqui ele pinta TEXTO
     sobre `--cor-superficie-2` (o fundo de `.vr7-aviso`) e dava **3,84:1** no
     tema claro, contra os 4,5:1 exigidos. `#266a43` dá **4,95:1**.
     Reprovado pela régua de contraste no veredito da cadeia (077.1), a primeira
     tela registrada que exercita este aviso — nas TRÊS larguras, e só no tema
     claro; no escuro o verde de sempre já passa. O irmão `--erro` está correto:
     `--cor-erro` foi ajustado no token pela 069.2 e dá 4,90:1 sobre o mesmo
     fundo. */
  color: var(--cor-sucesso-texto);
}
.vr7-aviso--erro {
  color: var(--cor-erro);
}

/* Obrigatoriedade e validação por campo (wizard de loja e formulários em geral).
   `:user-invalid` só marca depois de o usuário interagir e deixar inválido — não
   pinta campos ainda intactos. `.campo--erro` é o realce vindo do servidor. */
.vr7-obrigatorio {
  color: var(--cor-erro);
  font-weight: 700;
}
.campo:user-invalid,
/* CAMPO CURTO — a largura vem do DADO, não da linha (2026-08-24).

   Medido em 390px na consulta de regra aplicável: o campo de **parcela** vinha
   com **217px** para receber **um ou dois dígitos**, porque herdava a largura de
   um campo de busca. Dois preços nisso, e o segundo é o que o dono viu: no
   computador ele fica com a caixa de um nome próprio, e no telefone empurra o
   **próprio rótulo para a linha de cima** — a pessoa lê "Parcela" numa linha e
   encontra a caixa noutra, como se fossem coisas diferentes. Com 6rem o par
   mede 164px dos 324 disponíveis e nunca se separa.

   Modificador, e não regra da tela de split: o próximo campo de 1–2 dígitos não
   deve precisar redescobrir isto, e uma regra sem modificador mudaria **todo**
   campo de busca da casa a partir de uma tela. */
.campo--curto {
  width: 6rem;
  /* Item de flex (`.vr7-busca`) não deve crescer nem encolher: o dado tem
     tamanho fixo, e deixá-lo ceder devolveria o problema em outra largura. */
  flex: 0 0 auto;
}
.campo--erro {
  border-color: var(--cor-erro);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--cor-erro) 22%, transparent);
}
.vr7-qr {
  justify-self: center;
  background: var(--cor-leitura-optica);
  padding: var(--esp-2);
  border-radius: var(--raio);
}
.vr7-segredo {
  text-align: center;
  color: var(--cor-texto-2);
}
.vr7-segredo code,
.vr7-recovery code {
  color: var(--cor-marca-forte);
}

.vr7-apps {
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  padding: var(--esp-3) var(--esp-4);
}
.vr7-apps__titulo {
  margin: 0 0 var(--esp-2);
  color: var(--cor-texto-2);
  font-size: 0.875rem;
}
.vr7-apps__lista {
  margin: 0;
  padding-left: var(--esp-6);
  color: var(--cor-texto);
  display: grid;
  gap: var(--esp-1);
}

/* Recovery codes em grade centralizada, com moldura dourada. */
.vr7-recovery {
  list-style: none;
  margin: 0;
  padding: var(--esp-4);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--esp-2) var(--esp-4);
  border: 1px solid var(--cor-dourado);
  border-radius: var(--raio);
  text-align: center;
}
.vr7-recovery-codigo code {
  font-family: var(--fonte-display);
  font-size: 1.0625rem;
  letter-spacing: 0.04em;
}

.vr7-link {
  background: none;
  border: none;
  color: var(--cor-marca);
  cursor: pointer;
  font: inherit;
  text-decoration: underline;
  padding: 0;
}
/* Botão vestido de link ainda é botão, e paga alvo de toque (FR-201).
   Achado da 069.2 na 1ª passagem por `/2fa`: o "Sair" media **24px** — a altura
   da linha, porque `.vr7-link` zera o `padding`.

   A qualificação por elemento não é detalhe. `.vr7-link` também veste
   **âncoras** ("Esqueci minha senha", "Cadastre-se"), e forçar 44px nelas
   transformaria link em meio a parágrafo num bloco — a régua de componente
   diz isso por escrito e por isso só cobra alvo de `input`, `select` e
   `button`. Alargar a classe inteira consertaria a régua e pioraria a tela.

   `inline-flex` porque `min-height` sozinho não centra o texto: o botão
   cresceria para 44px com o rótulo colado no topo. */
button.vr7-link {
  min-height: var(--alvo-toque);
  display: inline-flex;
  align-items: center;
}

/* Painel/home — boas-vindas + KPIs (estrutura honesta) + atalhos + slot futuro. */
.vr7-painel {
  display: grid;
  gap: var(--esp-8);
}

/* O cabeçalho do painel: título e texto de apoio são UM bloco, não dois.
   (spec 069.3 · US5, decisão do dono de 2026-08-14)

   O `gap: --esp-8` acima está certo para o que ele foi calibrado — separar
   blocos INDEPENDENTES. Só que título e apoio não são independentes: o segundo
   explica o primeiro. Como filhos diretos deste grid eles ganhavam o respiro
   grande DUAS vezes antes do primeiro campo, e item de grid **não colapsa
   margem** — a margem padrão do navegador soma ao `gap` em vez de se fundir:

     título → apoio ....... 21px (h1) + 32px (gap) + 16px (p) = ~69px
     apoio  → 1º campo .... 16px (p)  + 32px (gap)            = ~48px

   ~117px mortos numa janela de 844, com o primeiro campo nascendo abaixo da
   dobra em 390px.

   ⚠️ **Este é o QUARTO lugar da folha a consertar a mesma soma** — vieram antes
   `.vr7-form-loja`, `.vr7-form-token` e `.vr7-revisao`, cada um no seu canto e
   os três comentando a mesma frase. Por isso virou degrau do vocabulário em vez
   de um quarto remendo: pelo README do design system, precisar reescrever o
   mesmo conserto é sinal de que faltava uma peça, e a peça nasce aqui — com
   linha no README e guarda em `tests/test_cabecalho_de_painel.py`.

   Quem tiver um cabeçalho de painel novo pede ESTA classe. As três regras acima
   podem migrar para cá quando alguém passar por elas; não foram tocadas agora
   porque mexer em tela fora do escopo desta spec mudaria imagem de referência
   alheia, que é o que o FR-017 proíbe chegar sem a razão junto. */
.vr7-painel__cabecalho {
  display: grid;
  gap: var(--esp-2);
}

.vr7-painel__cabecalho > h1,
.vr7-painel__cabecalho > h2,
.vr7-painel__cabecalho > p {
  margin: 0;
}

/* Desafio de step-up (spec 036): cartão focado e estreito, centrado. */
.vr7-step-up {
  max-width: 26rem;
  margin-inline: auto;
  display: grid;
  gap: var(--esp-4);
  text-align: center;
  padding-block: var(--esp-6);
}
.vr7-step-up__titulo {
  margin: 0;
}
.vr7-step-up__texto {
  margin: 0;
  color: var(--cor-texto-2);
  line-height: 1.55;
  text-align: left;
}
.vr7-step-up form {
  display: grid;
  gap: var(--esp-4);
}
.vr7-step-up .vr7-otp {
  justify-self: center;
  width: 100%;
  max-width: 13rem;
  min-height: 3rem;
  font-size: 1.5rem;
  padding-left: calc(0.35em + var(--esp-3));
}
.vr7-step-up .vr7-otp::placeholder {
  color: var(--cor-texto-2);
  opacity: 0.45;
}
.vr7-step-up__ajuda {
  margin: 0;
  font-size: 0.85rem;
  color: var(--cor-texto-2);
}

.vr7-boas-vindas {
  display: flex;
  align-items: center;
  gap: var(--esp-4);
  padding: var(--esp-6);
  background: var(--cor-superficie);
  border: 1px solid var(--cor-borda);
  border-left: 3px solid var(--cor-dourado); /* acento dourado da marca */
  border-radius: var(--raio);
  flex-wrap: wrap;
}
.vr7-boas-vindas__logo svg {
  height: 40px;
}
.vr7-boas-vindas h1 {
  margin: 0;
}
.vr7-boas-vindas__lema {
  margin: var(--esp-1) 0 0;
  color: var(--cor-texto-2);
}

.vr7-secao__titulo {
  display: flex;
  align-items: center;
  gap: var(--esp-3);
  margin: 0 0 var(--esp-4);
}
.vr7-etiqueta-em-breve {
  font-family: var(--fonte-corpo);
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  /* `--cor-dourado-texto`, não `--cor-dourado`: este é **acento por
     construção** (bordas, foco, linhas), e o comentário do token sempre disse
     "nunca texto pequeno". A régua mediu **3,60:1** aqui — exatamente o número
     que a 069.1 mediu na superfície pública e que fez o par nascer. O mesmo
     defeito, num lugar que ela não alcançou: o shell autenticado.
     A **borda** segue no dourado de acento, que é o papel dele. */
  color: var(--cor-dourado-texto);
  border: 1px solid var(--cor-dourado);
  border-radius: 999px;
  padding: 0.1rem var(--esp-2);
}

.vr7-kpis,
.vr7-cards-atalho {
  display: grid;
  gap: var(--esp-4);
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
}
.vr7-kpi {
  display: grid;
  gap: var(--esp-1);
  padding: var(--esp-4);
  background: var(--cor-superficie);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
}
/* O cartão do que ainda não existe. **Sem `opacity`**, e a ausência é o ponto.
   `opacity: 0.6` no cartão inteiro derrubava o texto de ~7:1 para **2,81:1** no
   tema claro e 3,76:1 no escuro, contra os 4,5:1 da AA — achado da primeira
   passagem da régua de contraste sobre o `/painel` (069.4, 2026-08-09). A tela
   nunca tinha sido verificada, e o defeito estava ali desde que ela nasceu.

   Opacidade é a forma mais fácil de dizer "isto está inativo" e a mais cara: ela
   não distingue o que é decoração do que é **texto que precisa ser lido**, e
   nenhum token consegue compensá-la depois. O sentido já está nas palavras — a
   etiqueta "Em breve" no título da seção — e na semântica, no `aria-disabled`
   do cartão. Quem lê e quem ouve recebem a mesma informação; ninguém precisa
   enxergar através de um véu para isso.

   Se um dia o cartão precisar de distinção **visual**, ela vem do degrau de
   superfície (`app/static/css/README.md` §4), nunca de opacidade sobre texto. */
.vr7-kpi--em-breve {
  border-style: dashed;
}
.vr7-kpi__rotulo {
  color: var(--cor-texto-2);
  font-size: 0.875rem;
}
.vr7-kpi__valor {
  font-family: var(--fonte-display);
  font-size: 1.75rem;
  color: var(--cor-marca-forte);
}
.vr7-kpi__nota {
  color: var(--cor-texto-2);
  font-size: 0.75rem;
}

.vr7-card-atalho {
  display: grid;
  gap: var(--esp-2);
  align-content: start;
  padding: var(--esp-4);
  background: var(--cor-superficie);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  color: var(--cor-texto);
  text-decoration: none;
}
a.vr7-card-atalho:hover {
  border-color: var(--cor-dourado);
  background: var(--cor-superficie-2);
}
.vr7-card-atalho__titulo {
  font-family: var(--fonte-display);
  color: var(--cor-marca);
}
.vr7-card-atalho__desc {
  color: var(--cor-texto-2);
  font-size: 0.875rem;
}
/* Mesma história do `.vr7-kpi--em-breve`, e o mesmo conserto: `opacity: 0.55`
   derrubava a descrição para **3,34:1**, contra os 4,5:1 da AA. O cartão diz o
   que é por escrito e pela semântica; não precisa de véu para isso. */
.vr7-card-atalho--placeholder {
  border-style: dashed;
  cursor: default;
}

.vr7-slot-em-breve {
  border: 1px dashed var(--cor-borda);
  border-radius: var(--raio);
  padding: var(--esp-8);
  text-align: center;
  color: var(--cor-texto-2);
}
.vr7-painel__sair {
  margin: 0;
}

/* 6. GRADE DE FORMULÁRIO (FR-203 / SE-002) ---------------------------------
   CONTRATO ESTÁVEL verificado por teste de HTML (tests/test_design_system.py):
   os rótulos alinham entre si e os campos entre si — grade única de duas
   colunas (rótulo | campo). Nenhum formulário define o próprio alinhamento;
   todos herdam estas três classes. */
.formulario-grade {
  display: grid;
  grid-template-columns: max-content 1fr; /* col 1 = rótulos; col 2 = campos */
  gap: var(--esp-4) var(--esp-6);
  align-items: baseline;
  max-width: 48rem;
}

/* .rotulo — todos na coluna 1, alinhados entre si */
.formulario-grade .rotulo {
  color: var(--cor-texto-2);
  justify-self: start;
  padding-top: var(--esp-2);
}

/* .campo — todos na coluna 2, alinhados entre si */
.formulario-grade .campo {
  width: 100%;
  min-height: var(--alvo-toque);
  padding: var(--esp-2) var(--esp-3);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  background: var(--cor-superficie);
  color: var(--cor-texto);
  font: inherit;
}

/* PAR COMPACTO — rótulo e controle ESTREITO que não se separam (2026-08-24).

   ⚠️ **`display: contents` no computador, e é aqui que mora o cuidado**: os dois
   filhos continuam sendo itens **diretos** da grade única, então o rótulo cai na
   coluna 1 e o controle na coluna 2, alinhados com todos os outros pares
   (FR-203). Um invólucro comum viraria **um** item de grade e desalinharia a
   coluna dos campos exatamente onde ela é visível.

   Na faixa estreita a `.formulario-grade` colapsa para uma coluna e cada rótulo
   passa a gastar uma faixa inteira acima do seu campo. Para um campo largo isso
   está certo; para o par %/R$ + valor, não: medido em 390px, o conteúdo do
   componente mede **218px** dentro de uma coluna de **324px** — sobravam 106px
   à direita e o formulário crescia duas linhas à toa.

   E o componente é `inline-flex`: como item de grade ele ganha
   `justify-self: stretch` e a CAIXA dele media os 324 inteiros, encostando na
   borda do cartão com o conteúdo parado em 218. Como item de flex ele volta a
   medir o próprio conteúdo. */
.vr7-par-compacto {
  display: contents;
}
@media (max-width: 40rem) {
  .vr7-par-compacto {
    display: flex;
    align-items: center;
    gap: var(--esp-3);
    /* ⚠️ **`nowrap`, e a primeira versão errou isto** — com `wrap` o par
       resolvia o caso da grade e NÃO resolvia o do `.vr7-busca`: espremido
       entre os outros controles, ele quebrava POR DENTRO e devolvia o rótulo
       para uma linha e o campo para outra, que é exatamente o defeito que ele
       existe para impedir. Medido: o campo já estava em 96px e continuava
       caindo sozinho. "Não se separam" é o contrato inteiro da classe.

       `flex: 0 0 auto` pela mesma razão, um nível acima: dentro de um flex
       (`.vr7-busca`) o par não pode ser comprimido — quem cede é o espaço
       ENTRE os pares. O contrato da classe é o controle ser estreito, então
       não caber não é um risco que se administre encolhendo. */
    flex-wrap: nowrap;
    flex: 0 0 auto;
  }
  /* O `padding-top` existe para descolar o rótulo do campo **empilhado**; lado
     a lado ele desalinha o par na vertical. */
  .vr7-par-compacto > .rotulo {
    padding-top: 0;
    /* ⚠️ **Um PISO, não uma coluna** — e ele existe porque a medição não pega
       o que o olho pega: "Taxa" mede 35px e "FI" 16px, então dois pares
       empilhados começavam os controles em x diferentes, com 15px de degrau
       entre eles. Cada um cabia na sua linha, que era o pedido, e mesmo assim
       lia como defeito — é a convenção da grade única (FR-203) aparecendo
       onde a grade já colapsou.

       `min-width` e não `width`: rótulo maior que o piso cresce, em vez de
       ser cortado. Pares de rótulos muito desiguais voltam a escalonar, e
       isso é honesto — o piso alinha os curtos, não promete uma coluna. */
    min-width: 2.5rem;
  }
}

/* Ações do formulário ocupam a largura das duas colunas */
.formulario-grade .acoes {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--esp-4);
  margin-top: var(--esp-2);
}

/* Qualquer elemento que NÃO seja par rótulo+campo (ajuda, aviso, parágrafo,
   lista) ocupa a linha inteira — senão consome uma célula e desalinha TODOS os
   pares seguintes (rótulo cai numa coluna, campo na outra). FR-203. */
.formulario-grade > .vr7-ajuda,
.formulario-grade > .vr7-aviso,
.formulario-grade > .vr7-senha-criterios,
.formulario-grade > .vr7-dados-bancarios,
/* spec 042.2 — blocos de linha inteira do formulário de pedido: a composição
   (linhas de pagamento), a prévia de valores trocada pelo htmx e o cadastro
   rápido de cliente. Nenhum deles é par rótulo+campo. */
.formulario-grade > .vr7-linhas-pagamento,
.formulario-grade > .vr7-painel-calculo,
.formulario-grade > .vr7-cliente-rapido,
/* spec 045.2 — o destino de reembolso informado no ato do estorno. Mesmo
   arranjo do cadastro rápido de cliente: um `<details>` recolhido que abre uma
   grade única aninhada dentro do formulário de gravação. */
.formulario-grade > .vr7-destino-novo,
/* 2026-08-25 — a caixa de marcação com texto ao lado. Ela **não é** par
   rótulo+campo: o `<label>` envolve o controle, então o conjunto é UM item, e
   dividi-lo em duas células é o que produz o desalinho.

   ⚠️ **A saída errada é `class="campo"` na marca**, e foi medida antes de esta
   regra existir: a classe dá `width: 100%` e `min-height: 44px`, o navegador
   honra as duas no controle nativo, e a marca vira uma caixa de **217×44px** —
   4px fora do x dos demais campos, ainda por cima. As telas de estorno usam
   essa forma no controle irmão e carregam o mesmo defeito; nenhum retrato as
   fotografa naquele ramo, que é por que ninguém o viu. */
.formulario-grade > .vr7-toggle,
.formulario-grade > p {
  grid-column: 1 / -1;
  margin: 0;
}

/* `min-width: 0` nos blocos de linha inteira. A coluna `1fr` da grade tem
   largura mínima automática igual ao min-content do conteúdo: sem isto, a
   tabela larga da composição empurra a GRADE, a grade empurra a página, e a
   tela de venda ganha rolagem horizontal no celular — apesar de a tabela já
   estar dentro de um `.vr7-rolagem-x`, que nunca chegava a agir. Medido em
   375px: a página pedia 774px de largura. */
.formulario-grade > .vr7-linhas-pagamento,
.formulario-grade > .vr7-painel-calculo,
.formulario-grade > .vr7-cliente-rapido,
.formulario-grade > .vr7-destino-novo {
  min-width: 0;
}
.formulario-grade .vr7-rolagem-x {
  max-width: 100%;
  overflow-x: auto;
  /* `position: relative` para o contêiner virar bloco-contêiner dos
     descendentes absolutos. Os rótulos `.vr7-visual-oculto` da tabela de
     composição são `position: absolute`; sem ancestral posicionado, o bloco
     deles é o inicial (o documento), então escapavam da rolagem e esticavam
     a PÁGINA — a tela de venda ganhava rolagem horizontal no celular (375px
     de viewport pedindo 700px) por causa de rótulos invisíveis. Não afeta o
     menu de ações, que é `position: fixed` e ignora ancestral relativo. */
  position: relative;
}

/* Grid de dados bancários embutida no formulário (spec 026) — sub-seção de
   linha inteira dentro do formulario-grade; h2 + dois grupos (conta bancária
   e chave PIX), cada um com sua própria tabela de colunas FIXAS (sem coluna
   aparecendo/sumindo por linha — daí serem grids separadas) + botão
   adicionar. `align-items: flex-start` evita que os botões "+ linha" (itens
   flex) estiquem até a largura da tabela — sem isso herdam stretch (default
   do eixo cruzado do flex column) e viram uma faixa inteira. */
.vr7-dados-bancarios,
.vr7-dados-bancarios__grupo {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--esp-3);
}
.vr7-dados-bancarios h2,
.vr7-dados-bancarios .vr7-rolagem-x,
.vr7-dados-bancarios__grupo,
.vr7-dados-bancarios__grupo .vr7-rolagem-x {
  align-self: stretch;
}
.vr7-dados-bancarios h2 {
  font-size: 1rem;
  margin: 0;
}
.vr7-dados-bancarios h3 {
  font-size: 0.875rem;
  color: var(--cor-texto-2);
  margin: 0;
}

/* ⚠️ `--conta-bancaria` e `--chave-pix` FORAM REMOVIDAS (075 · T055/T086,
   2026-08-14), e com elas caem as **quatro últimas** variantes ad-hoc que a
   spec catalogou. `--split` saiu na Fase 3, `--cobrancas` na convergência; estas
   duas saíram junto com a última tela que as consumia.

   O que elas faziam: dar `min-width` por posição de coluna
   (`td:nth-child(1) .campo` = 13rem = o seletor de banco, e por aí) porque um
   `.campo` dentro de `<td>` estreito colapsa. Sete regras presas à **ordem** das
   colunas — trocar duas de lugar redimensionava as erradas, em silêncio.

   Não houve substituto a escrever, e é esse o ponto: as duas grades deixaram de
   ser tabela (FR-004). Um campo numa `.formulario-grade` já ocupa a coluna
   inteira, e o `min-width` por posição não tem mais o que consertar.

   Resta declarada **uma** exceção, e ela está no lugar certo: `--matriz`, viva
   em `usuarios/matriz.html`. Ela não é lista — é uma grade de perfis ×
   permissões, onde a coluna É o dado. Ver o comentário dela junto da regra. */

/* 6b. LISTAGEM (gabarito US-H / E-006 — classes estáveis) ------------------- */
.vr7-listagem {
  display: grid;
  gap: var(--esp-4);
}
.vr7-listagem__cabecalho {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--esp-4);
  flex-wrap: wrap;
}

.vr7-busca {
  display: flex;
  align-items: center;
  gap: var(--esp-3);
  flex-wrap: wrap;
  padding: var(--esp-3) 0;
}
.vr7-busca .campo {
  min-height: var(--alvo-toque);
  max-width: 22rem;
}
.vr7-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--esp-2);
  color: var(--cor-texto-2);
}

.vr7-tabela {
  width: 100%;
  border-collapse: collapse;
}
.vr7-tabela th,
.vr7-tabela td {
  text-align: left;
  padding: var(--esp-2) var(--esp-3);
  /* Degrau `separação` da escala (spec 071): dividir itens de uma lista é só
     `border-bottom` — sem fundo e sem contorno fechado. A tabela já fazia isso
     antes da escala existir; o que muda é passar a dizê-lo pelo vocabulário, em
     vez de repetir o valor e torcer para o próximo autor repetir igual. */
  border-bottom: var(--camada-separacao-borda);
}
.vr7-tabela th {
  color: var(--cor-texto-2);
  font-weight: 600;
}
.vr7-tabela tbody tr:hover {
  background: var(--cor-superficie-2);
}
/* Tabela-matriz (capacidade × perfil, ex.: /usuarios/matriz). A marcação de cada
   célula é um símbolo curto (✅/—) que precisa ler sob o rótulo da coluna: célula
   E cabeçalho centralizam juntos, senão a marca fica à esquerda e o olho não
   fecha a coluna. A PRIMEIRA coluna é o rótulo da linha (o perfil) e segue à
   esquerda, porque é texto corrido, não marcação. */
.vr7-tabela--matriz th:not(:first-child),
.vr7-tabela--matriz td:not(:first-child) {
  text-align: center;
}

/* Linha inativa: o esmaecimento marca os DADOS, e sai por COR (token secundário,
   AA), nunca por `opacity`. `opacity` num ancestral cria grupo de composição —
   o menu de ações herdava os 70% e NENHUM descendente consegue desfazer isso
   (`opacity: 1` no filho não reverte o grupo já rasterizado). Era isso que
   deixava o menu do usuário inativo apagado, sugerindo ação desabilitada. Só
   com a cor, o gatilho e os itens do menu — que já re-fixam `--cor-texto` —
   ficam idênticos aos da linha ativa, sem regra de exceção. */
.vr7-linha--inativa {
  color: var(--cor-texto-2);
}

/* Item marcado para REMOÇÃO num rascunho ainda não submetido (064.4 · achado
   `032·T018`). A classe era usada crua em `lojista/alteracao_socios.html` e não
   existia em folha nenhuma: a linha ficava idêntica às demais, e a frase
   "marcado para remoção" ao lado era o único sinal.

   ⚠️ **Não é `--inativa`, e a diferença é de significado.** Inativo é um estado
   PERSISTIDO do registro; removido aqui é uma intenção de rascunho, que a
   submissão pode efetivar ou o cancelamento descartar. Reusar a classe da
   primeira faria as duas parecerem a mesma coisa numa tela onde a distinção
   decide o que o lojista está prestes a enviar. O risco desta é o texto tachado,
   que a `--inativa` não tem. */
.vr7-item--removido {
  color: var(--cor-texto-2);
  text-decoration: line-through;
  text-decoration-color: var(--cor-erro);
}

/* Coluna "Ações" padrão (kebab) — spec 020, contracts/macro-acoes.md.
   Único componente de menu de ações; toda listagem usa a macro `acoes_linha`
   (app/templates/_macros.html), nunca HTML de menu solto por tela. */
.vr7-acoes-menu {
  position: relative;
  display: inline-block;
}
.vr7-acoes-menu__trigger {
  min-width: var(--alvo-toque);
  min-height: var(--alvo-toque);
  padding: 0 var(--esp-2);
  border: 1px solid transparent;
  border-radius: var(--raio);
  background: none;
  color: var(--cor-texto);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}
.vr7-acoes-menu__trigger:hover {
  background: var(--cor-superficie-2);
  border-color: var(--cor-borda);
}
.vr7-acoes-menu__trigger--perigo {
  color: var(--cor-erro);
}
.vr7-acoes-menu__lista {
  /* `position: fixed` (não `absolute`) — o `<td>` costuma estar dentro de `.vr7-rolagem-x`
     (overflow-x: auto), que por regra do CSS força overflow-y a cortar conteúdo posicionado
     via `absolute`. `fixed` escapa desse clipping; posição real (top/left) calculada em JS
     (ligarMenuAcoes em vr7.js) a partir do retângulo do botão de trigger — no abrir do menu
     E a cada rolagem, senão o `fixed` deixaria a lista parada enquanto a linha rola embora. */
  position: fixed;
  z-index: 10;
  margin: 0;
  min-width: 10rem;
  list-style: none;
  padding: var(--esp-1) 0;
  background: var(--cor-superficie);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  box-shadow: var(--sombra-suspensa);
}
.vr7-acoes-menu__item {
  display: block;
  width: 100%;
  min-height: var(--alvo-toque);
  padding: var(--esp-2) var(--esp-3);
  border: none;
  background: none;
  color: var(--cor-texto);
  text-align: left;
  text-decoration: none;
  font: inherit;
  cursor: pointer;
}
.vr7-acoes-menu__item:hover,
.vr7-acoes-menu__item:focus {
  background: var(--cor-superficie-2);
}
.vr7-acoes-menu__item--perigo {
  color: var(--cor-erro);
}
.vr7-acoes-menu__lista form {
  margin: 0;
}
.vr7-acoes-menu__separador {
  margin: var(--esp-1) 0;
  border-top: 1px solid var(--cor-borda);
}
.vr7-acoes-menu__item-custom {
  padding: var(--esp-2) var(--esp-3);
}

/* Troca de perfil dentro do menu de ações (item "custom"). Empilha rótulo,
   select e botão numa coluna — antes era um `<select>` cru (aparência nativa,
   quadrada) com rótulo oculto e um link de submit, que dentro do menu parecia
   um formulário solto. O select usa a classe `campo` do design system, então
   herda raio/altura de toque/borda como qualquer outro controle. */
.vr7-troca-perfil {
  display: grid;
  gap: var(--esp-2);
  min-width: 12rem;
  margin: 0;
}
.vr7-troca-perfil__rotulo {
  color: var(--cor-texto-2);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.vr7-troca-perfil .campo {
  width: 100%;
}
.vr7-troca-perfil__aplicar {
  width: 100%;
}

/* Cabeçalho ordenável: rótulo à ESQUERDA (junto da coluna que ele nomeia), seta
   encostada na borda DIREITA da célula. Enquanto o link era inline, a seta vinha
   colada ao rótulo e, em coluna estreita (o "Código" de /bancos, cujo conteúdo é
   um número de 3 dígitos), quebrava para a linha de baixo — o indicador de ordem
   aparecia SOB o nome, não ao lado dele. Como flex de largura cheia, o par não
   parte (`nowrap`) e a seta ocupa sempre o mesmo canto, em qualquer largura. */
.vr7-ordenar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--esp-2);
  white-space: nowrap;
  text-decoration: none;
}
.vr7-ordenar--ativa {
  color: var(--cor-marca-forte);
}

.vr7-tag {
  display: inline-block;
  margin: 0 var(--esp-1) var(--esp-1) 0;
  padding: 0 var(--esp-2);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  font-size: 0.8125rem;
  color: var(--cor-texto-2);
}

/* ⚠️ **Um GRUPO de selos é UM filho da célula** (2026-08-18, achado ao revisar
   os 24 retratos das naturezas).

   Soltos dentro de uma `<td>`, dois selos parecem inofensivos — e são, até a
   linha abrir no telefone. Ali a célula revelada é `display: grid` de duas
   colunas (rótulo · valor), e **cada filho vira um item de grade**: o primeiro
   selo esticava de ponta a ponta da coluna do valor (item de grade nasce
   `stretch`) e o segundo procurava vaga na coluna 1 da linha seguinte — a do
   rótulo. O conjunto agrupado é um item só, e os dois sintomas somem juntos.

   O nome é genérico porque a armadilha é do design system, não das naturezas:
   toda célula com mais de um filho cai nela. `flex-wrap` porque numa coluna
   estreita os selos precisam quebrar; o espaçamento continua vindo da margem do
   próprio `.vr7-tag`, e não de um `gap` — dois espaçamentos somados afastariam
   os selos o dobro. */
.vr7-selos {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
}

.vr7-paginacao {
  display: flex;
  align-items: center;
  gap: var(--esp-4);
  margin-top: var(--esp-4);
}
.vr7-paginacao__pos {
  color: var(--cor-texto-2);
}

/* Texto secundário/discreto (indicador de alteração pendente, legenda de
   documento, "última atualização" etc.) — usado em vários templates. */
.vr7-suave {
  color: var(--cor-texto-2);
  font-size: 0.875rem;
}

/* Lista de dados somente-leitura (ex.: detalhe de análise em /aprovacoes) —
   mesma grade rótulo:valor do `.formulario-grade`, sem campo editável. */
.vr7-dados {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--esp-2) var(--esp-6);
  align-items: baseline;
  max-width: 40rem;
  margin: 0;
}
.vr7-dados dt {
  color: var(--cor-texto-2);
}
.vr7-dados dd {
  margin: 0;
}

/* `.vr7-acoes__motivo` viveu aqui até 2026-08-14, quando a decisão da análise
   de ciclo foi para a coluna lateral do padrão de duas colunas: o campo passou
   a ser um `.vr7-campo-bloco` do design system, e os botões, uma
   `.vr7-barra-acoes--separada`. Duas classes exclusivas de uma tela a menos —
   o que o `test_nome_generico.py` cobra: conceito que já tem nome genérico não
   ganha um nome de tela. */
.vr7-acoes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--esp-4);
  margin-top: var(--esp-4);
}

/* Formulário de natureza — seletor de ícone e indicadores */
.vr7-icone-seletor,
.vr7-indicadores {
  display: flex;
  flex-wrap: wrap;
  gap: var(--esp-3);
  border: none;
  background: none;
  padding: 0;
  min-height: 0;
}
.vr7-icone-opcao {
  display: inline-flex;
  align-items: center;
  gap: var(--esp-1);
  color: var(--cor-texto-2);
}

.vr7-duplicada {
  border: 1px solid var(--cor-marca-bronze);
  border-radius: var(--raio);
  padding: var(--esp-3) var(--esp-4);
  margin-bottom: var(--esp-4);
}
.vr7-diferencas {
  margin: var(--esp-2) 0;
}
.vr7-diferenca {
  color: var(--cor-erro);
  font-weight: 600;
}

/* Selo "Alterado — valor anterior" (spec 015/021): ocupa a linha inteira do
   grid mesmo aninhado num `display: contents` (#resto-do-formulario), onde o
   seletor `.formulario-grade > p` NÃO alcança — sem isto o selo consome uma
   célula e desalinha todos os pares rótulo/campo seguintes (FR-203). */
.vr7-campo-alterado {
  grid-column: 1 / -1;
  justify-self: stretch;
  margin: 0;
}

/* Blocos do diff de sócios na revisão (spec 021) — respiro entre cada sócio. */
.vr7-diff-socio {
  margin: var(--esp-4) 0;
}
.vr7-diff-socio__titulo {
  margin: 0 0 var(--esp-2);
}

/* ── spec 018 — seletor segmentado (%/R$), selos de regra e diff antes×depois ── */
/* Componente reutilizável (§V): base comum (borda --cor-borda, raio --raio,
   alvo --alvo-toque). Contraste AA nos dois temas (marca × fundo). */
.vr7-segmento {
  display: inline-flex;
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  overflow: hidden;
  min-height: var(--alvo-toque);
}
.vr7-segmento label {
  display: inline-flex;
  align-items: center;
  margin: 0;
}
.vr7-segmento input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}
.vr7-segmento span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 3rem;
  min-height: var(--alvo-toque);
  padding: 0 var(--esp-3);
  color: var(--cor-texto-2);
  cursor: pointer;
}
.vr7-segmento label + label span {
  border-left: 1px solid var(--cor-borda);
}
.vr7-segmento input:checked + span {
  background: var(--cor-marca);
  color: var(--cor-fundo);
  font-weight: 600;
}
/* Segmento com opção desabilitada: a escolha se lê, não se clica. O
   `cursor: pointer` do estado normal é uma promessa, e sobre um rádio
   desabilitado ela é falsa — a pessoa clica, nada acontece, e conclui que a
   tela está quebrada.

   Hoje nenhum segmento da casa nasce desabilitado (a modalidade da conta, que
   nascia, passou a ser navegável na revisão em 2026-08-14). A regra fica porque
   é do COMPONENTE, não de uma tela: quem desabilitar um segmento amanhã não
   deve precisar redescobrir isto. */
.vr7-segmento input:disabled + span {
  cursor: default;
}
.vr7-segmento input:focus-visible + span {
  outline: 2px solid var(--cor-marca-forte);
  outline-offset: -2px;
}
/* Modificador para opções de rótulo longo/assimétrico (ex.: "Transferência
   bancária" × "PIX", spec 022) — sem isto o segmento encolhe ao conteúdo e as
   opções ficam com larguras desiguais e visual quebrado no grid. */
.vr7-segmento--cheio {
  display: flex;
  width: 100%;
}
.vr7-segmento--cheio label {
  flex: 1;
}
.vr7-segmento--cheio span {
  width: 100%;
}

/* Selo da regra aplicável (loja) ou da sua ausência. */
.vr7-tag--loja {
  border-color: var(--cor-marca-bronze);
  color: var(--cor-texto);
}
.vr7-tag--sem-regra {
  border-style: dashed;
  color: var(--cor-texto-2);
}

/* Célula de diff antes × depois (tela de impacto — US4). */
.vr7-antes-depois {
  display: flex;
  gap: var(--esp-2);
  align-items: baseline;
}
.vr7-antes-depois__antes {
  color: var(--cor-texto-2);
  text-decoration: line-through;
}
.vr7-antes-depois__depois {
  font-weight: 600;
}

/* Célula editável da grade de split (seletor %/R$ + valor). */
.vr7-componente {
  display: inline-flex;
  gap: var(--esp-2);
  align-items: center;
}
.vr7-componente .campo {
  width: 7rem;
  min-height: var(--alvo-toque);
}

/* A variante `--split` FOI REMOVIDA (spec 075 · T022, 2026-08-11).

   Ela existia para acomodar o par antigo/novo dentro de uma tabela: cabeçalho de
   grupo centralizado sobre três colunas e o valor antigo apagado, sem quebra. As
   duas telas que a consumiam — `regras_split_loja/lista.html` e
   `categorias_loja/form.html` — deixaram de ser tabela: elas têm CAMPO dentro do
   corpo, e campo em célula não tem para onde encolher.

   O que a variante fazia virou **papel declarado**: `.vr7-comparacao` põe o par
   `de X → [Y]` na horizontal e a variação abaixo. É a mesma intenção dita pelo
   nome certo — e agora vale para qualquer tela que compare valores, em vez de
   ser um ajuste de duas grades.

   ⚠️ Ela saiu JUNTO da última tela que a consumia, não de passagem. Remover
   variante fora da fase que migra muda a aparência sem o antes/depois que a
   revisão do merge julga (FR-016). */

/* Ações das grades de taxas — categoria (`.vr7-regras-split`) e loja
   (`.vr7-grade-split`). Estes <form> NÃO são `.formulario-grade`, e a única regra
   de `.acoes` vive sob aquela classe (ver "Ações do formulário" acima) — então
   aqui o bloco nascia SEM ESTILO: sem `margin-top`, o botão encostava na borda
   inferior da última linha da tabela e lia como parte da grade, não como ação que
   a fecha. Separa do traço que delimita a tabela e alinha como nos demais
   formulários. As duas telas juntas de propósito: é a MESMA grade, e regra
   separada por tela é o que fez uma divergir da outra. */
.vr7-regras-split .acoes,
.vr7-grade-split .acoes {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--esp-4);
  margin-top: var(--esp-4);
}

/* Variação % calculada ao vivo (só visual). */
.vr7-variacao {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  white-space: nowrap;
}
.vr7-variacao--sobe {
  color: var(--cor-erro);
}
.vr7-variacao--desce {
  color: var(--cor-sucesso);
}
.vr7-variacao--igual,
.vr7-variacao--novo,
.vr7-variacao--tipo {
  color: var(--cor-texto-2);
  font-weight: 400;
}

.vr7-visual-oculto {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* 7. ACESSIBILIDADE E RESPONSIVIDADE (FR-201 / FR-202) ---------------------- */

/* Foco de teclado sempre visível (navegação 100% por teclado — FR-201) */
:focus-visible {
  outline: var(--foco-anel);
  outline-offset: 2px;
}

/* Skip-link para pular ao conteúdo (FR-201) */
.vr7-skip {
  position: absolute;
  left: -9999px;
}
.vr7-skip:focus {
  left: var(--esp-4);
  top: var(--esp-4);
  z-index: 10;
  background: var(--cor-superficie);
  padding: var(--esp-2) var(--esp-4);
  border-radius: var(--raio);
}

/* Ícones do sprite herdam a cor do texto (recoloríveis) */
.vr7-icone {
  width: 1.25em;
  height: 1.25em;
  fill: currentColor;
  vertical-align: middle;
}

/* Sem rolagem horizontal da página; listagens largas rolam no próprio contêiner */
.vr7-rolagem-x {
  overflow-x: auto;
  max-width: 100%;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
  /* Chevrons de autenticação: já desenhados, sem traçado animado. */
  .vr7-auth-chevrons path {
    animation: none;
    stroke-dashoffset: 0;
  }
}

/* Em telas estreitas a grade de formulário COLAPSA para uma coluna:
   rótulo acima do campo, mantendo o alinhamento vertical (FR-202).

   Este corte fica em 40rem DE PROPÓSITO, e não vai para os 48rem das faixas de
   navegação (spec 068 / FR-011a / research D4). Formulário é assunto de outra
   spec — "refazer telas individuais" está fora do escopo da 068 —, e mover o
   corte mudaria o layout de todo formulário da plataforma como efeito colateral
   de uma spec de navegação. A FR-011b manda converter os `640px` que DUPLICAM
   este 40rem, não unificar cortes de assuntos diferentes.

   O que saiu daqui na spec 068: as regras de `.vr7-corpo`/`.vr7-sidebar` que
   empilhavam a navegação acima do conteúdo, e o comentário que descrevia um
   layout que o código nunca entregou (prometia uma tira deitada e produzia uma
   coluna de até 18 links acima do conteúdo). A navegação no telefone é painel
   deslizante, e mora com as faixas da FR-011a. */
@media (max-width: 40rem) {
  .formulario-grade {
    grid-template-columns: 1fr;
    gap: var(--esp-2);
  }
  .formulario-grade .rotulo {
    padding-top: var(--esp-3);
  }
}
/* O que era do CABEÇALHO saiu deste bloco na spec 068: o cabeçalho é governado
   pelas faixas da FR-011a (telefone < 48rem), e não pelo corte de formulário.
   Também saiu a regra que esticava o campo de busca em tela estreita — ali ele
   agora nem aparece, cede lugar à lupa (FR-005). */

/* Botão flutuante "Reportar um problema" — presente em todas as telas (spec 017 / US2).

   Escrito do telefone para cima (spec 068 / FR-011b): o corte era `640px`, a
   mesma duplicata de unidade que a FR-011b manda eliminar. O estado BASE é o
   botão redondo só com ícone; o `48rem` devolve a pílula com rótulo. */
/* FAB "Reportar um problema": a BASE mora em `vr7-tokens.css` (FR-115) — a
   folha que os dois shells carregam. Ele existia aqui E lá, com valores
   diferentes: a correção de contraste da 069.0 (bronze sólido, 6,5:1) chegou
   só a esta cópia, e a da vitrine seguiu em 4,25:1 no tema escuro. Ninguém
   percebeu porque as duas "funcionavam".

   O que sobra aqui é ADJETIVO, não segunda definição: no telefone da área
   autenticada o botão some, porque ali ele virou o último slot da **barra
   inferior** (spec 068 · FR-017) — dois controles para a mesma ação, um
   cobrindo o outro, foi o que o dono viu na tela em 2026-08-05.

   ⚠️ A regra é por LARGURA e mora nesta folha, e as duas coisas são o ponto:

   1. Esta folha é carregada **só** pelos shells autenticados. A vitrine nunca
      a lê, então a ocultação não a alcança — e ela **precisa** manter o FAB no
      telefone, porque não tem barra inferior nenhuma.
   2. Condicionar à PRESENÇA da barra (`body:has(.vr7-barra-inferior)`) parece
      mais expressivo e está errado: a barra está no DOM em **toda** largura,
      escondida por CSS no computador. O seletor casaria sempre e o FAB sumiria
      da área autenticada inteira. Tentado em 2026-08-06; reprovado pela 068.

   A ordem também é requisito: o `display: none` vem DEPOIS da definição da base
   (que está na outra folha, carregada antes), senão perde por ordem de arquivo
   e parece aplicado sem estar — a armadilha que a 068 já pagou duas vezes. */
.vr7-fab-incidente {
  display: none;
}

/* A ação de relato NO CORPO da página de erro é o **complemento** do FAB, e as
   duas regras moram juntas de propósito: elas precisam continuar sendo
   complementares, e duas condições de visibilidade escritas em pontas opostas
   do arquivo divergem na primeira vez que alguém mexe numa só.
   `tests/test_incidentes_publico.py::test_a_acao_do_corpo_e_o_complemento_do_fab`
   reprova se os dois pontos de quebra deixarem de coincidir.

   Por que existir: no telefone o FAB **não existe** (regra acima), e é lá que
   quem quebrou a aplicação antes de entrar tem menos caminho de volta. Da faixa
   do tablet para cima ela some — oferecer a mesma coisa duas vezes, a 30 px de
   distância, é o que fazia a tela parecer mal resolvida (revisão do dono,
   2026-08-15). */
.vr7-erro__acao-relato {
  display: inline-flex;
}

@media (min-width: 48rem) {
  /* Da faixa do tablet para cima o botão volta, com a pílula inteira e o
     rótulo à vista — ali a barra inferior não existe. */
  .vr7-fab-incidente {
    display: inline-flex;
    padding: 12px 18px;
  }
  /* …e, pelo mesmo motivo, a ação do corpo sai: o FAB já está na tela. */
  .vr7-erro__acao-relato {
    display: none;
  }
  .vr7-fab-incidente__rotulo {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip: auto;
  }
}

/* ===========================================================================
   Componente de anexo (spec 029, alinhado ao mockup aprovado) — card + linha,
   dropzone tracejada, faixa de tipo, ações em botão-ícone (hover/:focus-within),
   badge de estado com ponto. Tokens do shell; AA nos 2 temas; 44px; foco
   visível; reduced-motion.
   =========================================================================== */
/* ═══ O ANEXO — A FORMA APROVADA (075 · T092) ═══════════════════════════════
   ⚠️ **A referência é a maquete**, não este comentário:
   `specs/075-design-system-tabela/maquetes/anexo-miniatura.html`.

   A primeira implementação leu a decisão em prosa — *"não usar área de arrastar
   no telefone"* — e apenas **trocou o rótulo** da área de arrastar. Era leitura
   possível do texto, e não era o que estava aprovado. O desenho existia e vivia
   fora do repositório; sumiu. Agora ele é artefato da spec.

   A grade quebra **sozinha**: caixa de tamanho fixo mais `flex-wrap` dão duas
   por linha em 390 e quatro em 1440 **sem media query**. Já declarei
   `repeat(2)`/`repeat(4)` aqui e estava errado — contagem fixa não se ajusta à
   sobra da coluna de rótulo dentro do cartão do sócio, onde a mesma grade vive
   com ~254px. Quem conta é a largura disponível. */
.vr7-anexos-grade,
.vr7-dropzone__preview {
  display: flex;
  flex-wrap: wrap;
  gap: var(--esp-2);
  max-width: 100%;
}
.vr7-anexos-lista {
  display: flex;
  flex-direction: column;
  gap: var(--esp-2);
  max-width: 100%;
}

.vr7-anexo {
  position: relative;
  min-width: 0;
}
/* A miniatura: 5,5rem QUADRADA. Documento é retrato ou paisagem, e `cover`
   recorta os dois sem deformar — é o que faz reconhecer sem abrir. */
.vr7-anexo--card {
  width: 5.5rem;
  display: grid;
  gap: 2px;
}
.vr7-anexo__alvo {
  position: relative;
  display: block;
  width: 5.5rem;
  height: 5.5rem;
  overflow: hidden;
  border: var(--camada-aninhado-borda);
  border-radius: var(--camada-aninhado-raio);
  background: var(--camada-destaque-fundo);
  color: var(--cor-texto-2);
  text-decoration: none;
}
.vr7-anexo__thumb {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Sem prévia — o caso do PDF — o TIPO ocupa o lugar da imagem, em letra grande.
   Miniatura genérica não distingue o RG do comprovante, e a pessoa abre os dois
   para achar um. */
.vr7-anexo__tipo {
  display: grid;
  place-items: center;
  height: 100%;
  font-size: 1.6rem;
  font-weight: 600;
}
/* ⚠️ **DETALHE 2 — o rodapé `ver`.** O quadro sozinho é ambíguo, e a pergunta
   que originou isto foi literal: *"parece que não vou conseguir visualizar"*.

   A cor é literal de propósito, como a do `::backdrop` do lightbox: a faixa
   pousa **sobre uma foto**, e precisa ser escura nos dois temas. Um token de
   superfície inverteria no tema escuro e sumiria contra a imagem. */
.vr7-anexo__ver {
  position: absolute;
  inset: auto 0 0 0;
  background: var(--veu-medio);
  color: var(--cor-sobre-solido);
  font-size: var(--texto-xs);
  text-align: center;
  padding: 2px 0;
}
/* O "Baixar" do que NÃO é imagem (PDF e afins). O alvo abre em nova aba, e
   `download` no mesmo link se anularia com `target="_blank"` — então o par
   precisa de dois links. Fica sob o rodapé "ver", com o mesmo alvo de toque
   mínimo que todo controle da casa. */
.vr7-anexo__baixar {
  display: block;
  text-align: center;
  font-size: var(--texto-xs);
  color: var(--cor-texto-2);
  text-decoration: underline;
  min-height: var(--alvo-toque);
  line-height: var(--alvo-toque);
}
/* ⚠️ **DETALHE 3 — o remover, e é o único aqui que apaga documento.**
   Círculo no canto, longe do rodapé: antes era um caractere solto sobre a
   imagem, e no telefone *ver* e *remover* ficavam a poucos pixels um do outro.

   ⚠️ **O BOTÃO é o alvo (44px); o CÍRCULO é o que se vê (22px)** — e a ordem
   importa, porque eu já a inverti e a régua reprovou. A primeira versão
   desenhava o botão com 22px e entregava os 44 por um `::after` invisível: a
   régua de componente mede `boundingBox().height` do **elemento**, então ela
   viu 22 e acusou. Estava certa — o alvo é do controle, não de uma sombra dele.

   Agora o botão tem `--alvo-toque` e é transparente; o círculo vive num filho,
   no canto. A maquete continua obedecida: o que aparece tem 22px.

   ⚠️ **E ele é VERMELHO, não neutro.** A maquete desenha um círculo escuro, e
   ela mesma diz que cor vem dos tokens da casa: a régua exige cor de perigo em
   ação destrutiva (§V), e aqui isso é mais que conformidade — é o controle que
   apaga documento, encostado no que só abre. `--cor-erro-solido` é o vermelho
   de FUNDO (README §11a); `--cor-erro` é o de texto e daria 3,08:1 sob o glifo
   branco. */
.vr7-anexo .vr7-anexo__x {
  position: absolute;
  top: 0;
  right: 0;
  width: var(--alvo-toque);
  height: var(--alvo-toque);
  min-width: var(--alvo-toque);
  min-height: var(--alvo-toque);
  display: grid;
  place-items: start end;
  padding: 3px 3px 0 0;
  border: 0;
  background: none;
  cursor: pointer;
  z-index: 3;
}
.vr7-anexo .vr7-anexo__x-marca {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--cor-erro-solido);
  color: var(--cor-sobre-solido);
  font-size: var(--texto-xs);
  line-height: 1;
}
.vr7-anexo .vr7-anexo__x:focus-visible {
  outline: var(--foco-anel);
  outline-offset: 2px;
}
/* O formulário não é caixa: ele só carrega o POST. `display: contents` deixa o
   botão ser posicionado contra a miniatura, como se fosse filho dela. */
.vr7-anexo__remover {
  display: contents;
}
/* O nome fica, e não é redundância: para PDF ele é a ÚNICA pista. Trunca —
   nome de arquivo não pode empurrar a grade para fora. */
.vr7-anexo__nome {
  font-size: var(--texto-xs);
  color: var(--cor-texto-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
.vr7-anexo__meta {
  color: var(--cor-texto-2);
  font-size: 0.76rem;
  font-variant-numeric: tabular-nums;
}
/* A maquete não traz tipo/tamanho na miniatura: sob 5,5rem vira ruído, e o tipo
   já aparece na prévia. Fica no DOM para a variação em linha e para leitor de
   tela. */
.vr7-anexo--card .vr7-anexo__meta {
  display: none;
}
.vr7-anexo__acao {
  display: inline-grid;
  place-items: center;
  min-width: var(--alvo-toque);
  min-height: var(--alvo-toque);
  padding: 0;
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  background: color-mix(in srgb, var(--cor-fundo) 55%, transparent);
  color: var(--cor-marca);
  font: inherit;
  text-decoration: none;
  cursor: pointer;
}
.vr7-anexo__acao .vr7-icone {
  width: 18px;
  height: 18px;
}
.vr7-anexo__acao__rotulo {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.vr7-anexo__acao:hover {
  border-color: var(--cor-dourado);
  color: var(--cor-dourado-bri);
}
.vr7-anexo__acao:focus-visible {
  outline: var(--foco-anel);
  outline-offset: 2px;
}
.vr7-anexo__acao--perigo:hover {
  border-color: var(--cor-erro);
  color: var(--cor-erro);
}
/* Badge de estado no canto oposto ao remover — os dois no topo brigariam. */
.vr7-anexo__estado {
  position: absolute;
  left: 3px;
  top: 3px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  padding: 0.1rem 0.35rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--cor-fundo) 78%, transparent);
}
.vr7-anexo__estado::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}
.vr7-anexo__estado--enviado {
  color: var(--cor-sucesso);
}
.vr7-anexo__estado--enviando {
  color: var(--cor-dourado);
}
.vr7-anexo__estado--erro {
  color: var(--cor-erro);
}
.vr7-anexo__motivo {
  font-size: 0.78rem;
  color: var(--cor-erro);
}
.vr7-anexo--enviando .vr7-anexo__alvo {
  opacity: 0.65;
}
.vr7-anexo__progresso {
  display: block;
  height: 3px;
  border-radius: 999px;
  background: var(--cor-superficie-2);
  overflow: hidden;
}
.vr7-anexo__progresso-barra {
  display: block;
  height: 100%;
  width: 40%;
  background: var(--cor-dourado);
  animation: vr7-anexo-progresso 1.1s ease-in-out infinite;
}
.vr7-anexo--erro .vr7-anexo__alvo {
  border-color: var(--cor-erro);
}
@keyframes vr7-anexo-progresso {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(320%);
  }
}

/* ── A variação em linha (listas densas) ──────────────────────────────────
   Os MESMOS elementos da miniatura, deitados: caixa pequena à esquerda, nome e
   meta à direita. Um só macro emite os dois arranjos — dois desenhos do mesmo
   objeto divergem no dia em que alguém edita um. */
.vr7-anexo--linha {
  display: flex;
  align-items: center;
  gap: var(--esp-3);
  padding: var(--esp-2) var(--esp-3);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  background: var(--camada-cartao-fundo);
}
.vr7-anexo--linha .vr7-anexo__alvo {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
}
/* Sob 40px não cabe o rodapé: a palavra viraria uma tarja ilegível. Aqui o
   nome ao lado já diz o que é, e o alvo inteiro continua abrindo. */
.vr7-anexo--linha .vr7-anexo__ver {
  display: none;
}
.vr7-anexo--linha .vr7-anexo__tipo {
  font-size: var(--texto-xs);
}
.vr7-anexo--linha .vr7-anexo__nome {
  font-size: 0.9rem;
  color: var(--cor-texto);
  font-weight: 600;
}
/* Anexo em linha dentro de CÉLULA DE TABELA — teto de largura.
   `.vr7-anexo__nome` já trunca com reticências, mas truncar depende de haver um
   limite: numa célula de tabela não há: ela cresce com o conteúdo, e um nome de
   arquivo longo empurra a tabela inteira para fora do cartão. Na revisão de
   sócios isso invadiu a coluna da direita do padrão de duas colunas (visto pelo
   dono, 2026-08-14).
   O teto resolve o caso comum; a rolagem do `.vr7-rolagem-x` em volta continua
   sendo a rede para a tela estreita. */
.vr7-tabela .vr7-anexo--linha {
  max-width: 18rem;
}

/* ── O quadro de acrescentar ──────────────────────────────────────────────
   ⚠️ **Mesma caixa das miniaturas — 5,5rem quadrada, tracejada.** A área de
   arrastar-e-soltar ocupava ~230px de altura em 390px para oferecer um gesto
   que **ninguém faz num celular**; era o maior elemento da etapa de sócios.

   Encolhê-la não bastava, e essa foi a leitura errada da primeira vez: trocar o
   rótulo de uma faixa tracejada continua sendo uma faixa tracejada. O quadro é
   **um item da grade**, do tamanho do que ele acrescenta. */
.vr7-dropzone {
  display: contents;
}
/* O formulário de envio também não é caixa: sem isto ele viraria um item da
   grade com o quadro dentro, e o quadro deixaria de se alinhar às miniaturas.
   O rótulo e o botão vêm depois, e quebram para a linha seguinte. */
.vr7-anexo-envio {
  display: contents;
}
.vr7-anexo-envio__rotulo {
  flex: 1 1 10rem;
  min-width: 0;
  align-self: start;
}
.vr7-dropzone__input {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.vr7-dropzone__area {
  width: 5.5rem;
  /* ⚠️ **`min-height`, e não `height`** (2026-08-16, achado no retrato).
     A caixa é quadrada de 5,5rem e continua sendo — o que muda é o que ela faz
     quando o rótulo não cabe. Com altura fixa e trilha automática, o texto
     **atravessava as duas bordas tracejadas**: `/loja/cadastro/documentos`
     rotula o quadro com *"Adicionar arquivos a comprovante de endereço:"*,
     porque ali ele é o controle principal do passo, e não o "+" ao lado de uma
     miniatura.

     Pego pelo **retrato**, não por teste de arquivo: overflow não muda regra
     nenhuma do CSS — só a caixa desenhada. E congelar a imagem teria canonizado
     o defeito.

     A saída é a mesma do cabeçalho em 768: **cede em altura, não em conteúdo**.
     Quem tem rótulo curto (`anexar`, no sócio) fica no quadrado de sempre e a
     grade não se mexe. */
  min-height: 5.5rem;
  display: grid;
  /* A trilha era automática — ou seja, do tamanho do texto, que é como ele
     saía da caixa. `minmax(0, 1fr)` a prende à largura do quadro, e é o par
     `0`/`1fr` que faz a diferença: sem o mínimo zero, o conteúdo indivisível
     volta a mandar na trilha. */
  grid-template-columns: minmax(0, 1fr);
  align-content: center;
  justify-items: center;
  padding: 0.25rem;
  /* Rótulo de uma palavra só (um tipo de documento sem espaço) quebraria a
     linha por não caber inteiro; `anywhere` deixa quebrar dentro da palavra em
     vez de vazar. */
  overflow-wrap: anywhere;
  gap: 2px;
  text-align: center;
  border: 1px dashed var(--cor-texto-2);
  border-radius: var(--camada-aninhado-raio);
  color: var(--cor-texto-2);
  background: transparent;
  cursor: pointer;
  font-size: var(--texto-xs);
  transition: border-color 0.18s;
}
.vr7-dropzone__area:hover,
.vr7-dropzone--ativa .vr7-dropzone__area {
  border-color: var(--cor-dourado);
}
.vr7-dropzone__input:focus-visible + .vr7-dropzone__area {
  outline: var(--foco-anel);
  outline-offset: 2px;
}
.vr7-dropzone__mais {
  font-size: 1.4rem;
  line-height: 1;
}
.vr7-dropzone__titulo {
  font-size: var(--texto-xs);
}
/* A dica de formato fica no DOM — leitor de tela a lê, e a regra de aceitação
   do campo é a mesma —, mas não cabe em 5,5rem sem virar parede de texto. */
.vr7-dropzone__dica {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* Lightbox (visualizar imagem sem baixar) — <dialog> nativo. */
.vr7-lightbox {
  max-width: 92vw;
  max-height: 92vh;
  padding: 0;
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  background: var(--camada-cartao-fundo);
  color: var(--cor-texto);
}
.vr7-lightbox::backdrop {
  background: var(--veu-forte);
}
.vr7-lightbox__img {
  display: block;
  max-width: 92vw;
  max-height: 82vh;
  object-fit: contain;
}
.vr7-lightbox__barra {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--esp-3);
  padding: var(--esp-2) var(--esp-3);
}
.vr7-lightbox__fechar {
  min-height: var(--alvo-toque);
  min-width: var(--alvo-toque);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  background: transparent;
  color: var(--cor-marca);
  cursor: pointer;
}
.vr7-lightbox__fechar:focus-visible {
  outline: var(--foco-anel);
  outline-offset: 2px;
}

/* Bloco de "um input por documento". */
.vr7-doc-tipo {
  margin-bottom: var(--esp-6);
}
.vr7-doc-tipo__titulo {
  margin-bottom: var(--esp-2);
  font-size: 1.05rem;
}

/* Slot de documento na grade (vigente/enviado × novo em staging) — rótulo curto
   acima do card para o lojista distinguir "Atual" do "Novo" que vai substituir. */
.vr7-doc-slot {
  display: flex;
  flex-direction: column;
  gap: var(--esp-1);
  min-width: 0;
}
.vr7-doc-slot__rotulo {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--cor-texto-2);
}
.vr7-doc-slot__rotulo--novo {
  color: var(--cor-marca-bronze);
}
/* Documento de sócio na alteração: cards "Atual/Novo" contidos (não esticam a
   faixa toda dentro do item do sócio). */
.vr7-doc-socio {
  margin-block: var(--esp-2);
  max-width: 460px;
}

/* Diálogo de confirmação de ação de alto impacto (aprovar/recusar…) — <dialog>
   nativo (foco/Esc/backdrop). Componente do design system (§V). */
.vr7-confirmar {
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  background: var(--camada-cartao-fundo);
  color: var(--cor-texto);
  /* `--esp-5` não existe na escala (1,2,3,4,6,8): o padding era descartado como
     inválido e o diálogo ficava sem respiro interno. */
  padding: var(--esp-6);
  max-width: 30rem;
  width: calc(100% - 2rem);
  box-shadow: var(--sombra-modal);
}
.vr7-confirmar::backdrop {
  background: var(--veu-suave);
}
.vr7-confirmar__msg {
  margin: 0 0 var(--esp-4);
  font-size: 1rem;
  line-height: 1.5;
}
.vr7-confirmar__acoes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--esp-3);
  justify-content: flex-end;
}

/* Modal de step-up (spec 036) — confirmação do 2º fator numa ação sensível. */
.vr7-step-up-modal {
  border: 1px solid var(--cor-borda);
  border-radius: calc(var(--raio) * 2);
  background: var(--camada-cartao-fundo);
  color: var(--cor-texto);
  padding: var(--esp-6);
  max-width: 27rem;
  width: calc(100% - 2rem);
  box-shadow: var(--sombra-modal);
}
.vr7-step-up-modal::backdrop {
  background: var(--veu-medio);
}
.vr7-step-up-modal form {
  display: grid;
  gap: var(--esp-4);
  margin: 0;
}
.vr7-selo-seguranca {
  display: grid;
  place-items: center;
  justify-self: center;
  width: 3.25rem;
  height: 3.25rem;
  border-radius: 50%;
  background: var(--cor-superficie-2);
  border: 1px solid var(--cor-dourado);
  color: var(--cor-dourado);
}
.vr7-selo-seguranca .vr7-icone {
  width: 1.6rem;
  height: 1.6rem;
}
.vr7-step-up-modal__titulo {
  margin: 0;
  text-align: center;
  font-size: 1.35rem;
}
.vr7-step-up-modal__texto {
  margin: 0;
  color: var(--cor-texto-2);
  line-height: 1.55;
}
.vr7-step-up-modal .vr7-campo-grupo {
  display: grid;
  gap: var(--esp-2);
}
.vr7-step-up-modal .vr7-otp {
  justify-self: center;
  width: 100%;
  max-width: 13rem;
  min-height: 3rem;
  font-size: 1.5rem;
  padding-left: calc(0.35em + var(--esp-3));
}
.vr7-step-up-modal .vr7-otp::placeholder {
  color: var(--cor-texto-2);
  opacity: 0.45;
}
.vr7-step-up-modal__ajuda {
  margin: var(--esp-1) 0 0;
  font-size: 0.85rem;
  color: var(--cor-texto-2);
  text-align: center;
}
.vr7-step-up-modal__acoes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--esp-3);
  justify-content: flex-end;
  margin-top: var(--esp-2);
}

@media (prefers-reduced-motion: reduce) {
  .vr7-anexo__progresso-barra {
    animation: none;
    width: 100%;
    margin-left: 0;
  }
}

/* ── spec 033 — changelog e novidades ────────────────────────────────────────
   O changelog aparece em dois lugares com a MESMA marcação (`_lista.html`): a
   página `/changelog` (caminho sem JS) e o `<dialog>` que o `vr7.js` abre a
   partir da versão no rodapé. Por isso as classes não presumem o contêiner. */
.vr7-changelog__titulo {
  margin: 0 0 var(--esp-4);
  font-family: var(--fonte-display);
}
.vr7-changelog {
  list-style: none;
  margin: 0;
  padding: 0;
}
.vr7-changelog__item {
  padding: var(--esp-4) 0;
  border-top: 1px solid var(--cor-borda);
}
.vr7-changelog__item:first-child {
  border-top: 0;
  padding-top: 0;
}
.vr7-changelog__cabecalho {
  display: flex;
  align-items: baseline;
  gap: var(--esp-3);
  flex-wrap: wrap;
  margin: 0 0 var(--esp-2);
}
.vr7-changelog__versao {
  font-family: var(--fonte-display);
  font-size: 1.125rem;
  color: var(--cor-marca);
  font-variant-numeric: tabular-nums;
}
.vr7-changelog__data {
  color: var(--cor-texto-2);
  font-size: 0.875rem;
}
/* Dourado só na borda (acento); o texto fica em `--cor-texto` para manter AA
   nos dois temas — ver a nota em `.vr7-usuario-menu__papel`. */
.vr7-changelog__selo {
  padding: 0 var(--esp-2);
  border: 1px solid var(--cor-dourado);
  border-radius: var(--raio);
  color: var(--cor-texto);
  font-size: 0.75rem;
}
.vr7-changelog__atual .vr7-changelog__versao {
  color: var(--cor-texto);
  text-decoration: underline;
  text-decoration-color: var(--cor-dourado);
  text-underline-offset: 4px;
}
.vr7-changelog__resumo {
  margin: 0;
  color: var(--cor-texto);
}
.vr7-changelog__itens {
  margin: var(--esp-2) 0 0;
  padding-left: var(--esp-6);
  color: var(--cor-texto-2);
}
.vr7-changelog__itens li {
  margin-bottom: var(--esp-1);
}

/* O `<dialog>` do changelog: mesma casca do lightbox/confirmar (spec 029/032) */
.vr7-modal {
  max-width: min(46rem, 92vw);
  max-height: 86vh;
  overflow: auto;
  padding: var(--esp-6);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  background: var(--camada-cartao-fundo);
  color: var(--cor-texto);
}
.vr7-modal::backdrop {
  background: var(--veu-forte);
}
.vr7-modal__barra {
  display: flex;
  align-items: center;
  justify-content: flex-end;
}
.vr7-modal__fechar {
  min-height: var(--alvo-toque);
  min-width: var(--alvo-toque);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  background: transparent;
  color: var(--cor-marca);
  cursor: pointer;
}
.vr7-modal__fechar:focus-visible {
  outline: var(--foco-anel);
  outline-offset: 2px;
}
/* O corpo carrega o fragmento do servidor (changelog) ou a faixa de novidades
   movida do `<main>`; a faixa perde a moldura, que aqui o `<dialog>` já dá. */
.vr7-modal__corpo {
  margin-top: var(--esp-2);
}
.vr7-modal__corpo .vr7-novidades {
  margin: 0;
  border: 0;
  padding: 0;
  background: transparent;
}

/* Faixa de novidades — server-rendered no topo do conteúdo; com JS o vr7.js a
   promove a modal. A mesma marcação serve os dois caminhos, e o botão "marcar
   como visto" é um POST comum (funciona sem JS). */
.vr7-novidades {
  margin-bottom: var(--esp-6);
  padding: var(--esp-4);
  border: 1px solid var(--cor-dourado);
  border-radius: var(--raio);
  background: var(--cor-superficie);
}
.vr7-novidades__titulo {
  margin: 0 0 var(--esp-3);
  font-family: var(--fonte-display);
  font-size: 1.125rem;
}
.vr7-novidades__acoes {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--esp-4);
}

/* ── spec 034 — configuração de cobrança da loja ──────────────────────────── */
.vr7-config-cobranca {
  max-width: 48rem;
}

/* ── "Nova faixa de parcelas" (spec 065 · exceção de escopo do split) ───────
   Serve as duas telas de split — a da loja e a da categoria. É seção
   secundária dentro de uma tela que já tem a grade principal: fica delimitada
   para não ser confundida com a grade acima, sem pedir mais atenção que ela.
   Só tokens existentes (FR-052). */
.vr7-nova-faixa {
  max-width: 48rem;
  margin-top: var(--esp-6);
  /* `--esp-5` NÃO existe: a escala é 1·2·3·4·6·8·12·16. A declaração era
     inválida, o navegador a descartava, e esta faixa vinha com padding ZERO —
     em produção, desde que foi escrita, três linhas abaixo de um comentário que
     manda usar "só tokens existentes". Achado pelo guarda de token órfão da spec
     071, na primeira execução dele: `var(--nome)` sem declaração não é erro em
     CSS, é silêncio. `--esp-6` casa com o `margin-top` da mesma regra.
     Nenhuma tela fotografada usa esta faixa (só `regras_split_loja/lista.html` e
     `categorias_loja/form.html`), então o número esperado do SC-006 segue 4. */
  padding: var(--esp-6);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
}
.vr7-nova-faixa > h2 {
  margin-top: 0;
}
/* O aviso de que a faixa vale pelo total AGREGADO por forma é o que faz o
   formulário ser compreensível — ele explica por que "duas cobranças PIX à
   vista" precisam de uma faixa que cubra 2. Fica colado ao título, antes dos
   campos, e não perdido no fim. */
.vr7-nova-faixa > .vr7-ajuda {
  margin-bottom: var(--esp-4);
}
/* Seções com múltiplos controles ocupam a linha inteira da grade única (FR-203) —
   senão consumiriam uma célula e desalinhariam os pares rótulo|campo seguintes. */
.formulario-grade > .vr7-campo-secao {
  grid-column: 1 / -1;
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  padding: var(--esp-3) var(--esp-4);
  margin: 0;
}
.vr7-campo-secao legend {
  color: var(--cor-texto-2);
  padding: 0 var(--esp-2);
  font-size: 0.875rem;
}
.vr7-meios {
  display: flex;
  gap: var(--esp-6);
  flex-wrap: wrap;
}
.vr7-componente__on {
  display: inline-flex;
  align-items: center;
  gap: var(--esp-2);
}
.vr7-componente__campos {
  display: flex;
  gap: var(--esp-4);
  flex-wrap: wrap;
  margin-top: var(--esp-3);
}
.vr7-componente__campos label {
  display: grid;
  gap: var(--esp-1);
  font-size: 0.875rem;
}
.vr7-componente__campos .campo {
  max-width: 12rem;
}
.vr7-campo-secao .vr7-suave {
  margin: var(--esp-2) 0 0;
}

/* ── spec 037 — Conta VR7 (conta bancária da plataforma) ──────────────────── */

/* Barra de ações reutilizável: agrupa botões (e formulários de um botão só)
   numa linha, com respiro entre eles. Vive no design system, não inline, para
   qualquer tela nova herdar o mesmo espaçamento (§V). `form` entra no flex
   porque botão de POST precisa do próprio formulário — sem isto ele quebraria
   a linha e colaria no vizinho. */
.vr7-barra-acoes {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--esp-4);
  margin-top: var(--esp-4);
}
.vr7-barra-acoes form {
  margin: 0;
}
/* A barra que se SEPARA do que vem acima dela — a linha que a tela de venda tem
   sobre os botões (decisão do dono, 2026-08-13, ao comparar as telas).

   Modificador, e não regra na `.vr7-barra-acoes`: a barra crua é usada por
   `conta_vr7`, `lojas`, `usuarios` e pela ficha de pedidos, e pôr a linha na
   classe base desenharia um traço em sete telas que ninguém pediu. É a mesma
   armadilha do `.vr7-campo-bloco > .campo` alguns blocos acima — mexer na tela
   dos outros para acertar a sua.

   A linha vem do degrau `separação`, não escrita à mão: ela divide duas
   regiões, que é exatamente o papel dele (README §4). A `.vr7-acoes-pedido`,
   de onde este desenho veio, ainda a escreve como `1px solid var(--cor-borda)`
   — o valor é o mesmo, letra por letra, e ela é a metade da casa que ainda não
   fala pelo vocabulário. */
.vr7-barra-acoes--separada {
  padding-top: var(--esp-3);
  border-top: var(--camada-separacao-borda);
}

/* Cartão de conteúdo: agrupa um bloco (conta vigente, preparadas, histórico)
   com borda e respiro próprios, separando visualmente as três seções da tela. */
/* O CARTÃO DE CONTEÚDO — degrau `cartão` da escala (spec 071).

   Era uma caixa VAZADA: borda e raio simples, sem fundo próprio, em 21 usos. O
   cartão da tela de pedidos — a referência que o dono apontou como "mais
   profissional" — diferia dele em **duas declarações**: repousava sobre uma
   superfície e tinha o canto mais suave. Era só isso. Agora é este, e o nome
   específico de tela — o que citava a ficha de pedidos — deixou de existir:
   padrão com nome de uma tela não é adotável por outra, e quem tentasse teria de
   renomear ou copiar — copiar é como nascem seis direções onde devia haver uma.

   (O nome antigo não pode ser escrito aqui nem como exemplo: o guarda do
   `test_nome_generico.py` varre o comentário junto com a regra, de propósito.) */
/* ⚠️ **Cartão vazio não pinta caixa em volta do nada** (069.5, 2026-08-17).
   Relatado pelo dono: no formulário de cliente e de fornecedor, "Contas
   bancárias" e "Chaves PIX" mostravam um **quadrado branco vazio** quando não
   havia nenhum cadastro — o `.vr7-cartao` é renderizado sempre, porque é o
   ponto de inserção que o `grid_dados_bancarios.js` procura pelo
   `data-linhas-dados-bancarios`. Remover o `<div>` quando vazio quebraria o
   botão "+ conta".

   Por isso o conserto é **aqui e por `:empty`**, não no gabarito: ele se corrige
   sozinho quando o JS insere a primeira linha — o seletor deixa de casar no
   mesmo instante, sem ninguém precisar acrescentar ou remover classe. Um
   `{% if %}` no Jinja resolveria só o primeiro carregamento e deixaria a caixa
   sem pintura depois da inserção.

   ⚠️ O par disto vive no gabarito: os `{%-` que impedem o `for` vazio de deixar
   espaços dentro do `<div>`. Com espaço, `:empty` não casa e esta regra é
   inerte. As duas metades andam juntas. */
.vr7-cartao:empty {
  display: none;
}

.vr7-cartao {
  background: var(--camada-cartao-fundo);
  border: var(--camada-cartao-borda);
  border-radius: var(--camada-cartao-raio);
  /* `--esp-4`, não o `--esp-6` que o cartão vazado usava: o FR-009 manda adotar
     as propriedades da referência **inclusive o espaçamento**, e é o que mantém
     a ficha de pedidos estável (FR-011) — ela é a origem do padrão, não o alvo. */
  padding: var(--esp-4);
  margin-bottom: var(--esp-6);
  min-width: 0; /* item de grade nasce com `min-width: auto` e vaza no telefone */
}
.vr7-cartao > h2 {
  margin-top: 0;
}
.vr7-cartao .vr7-tabela {
  margin: var(--esp-3) 0;
}

/* OS DOIS DEGRAUS DE DENTRO DO CARTÃO — `aninhado` e `destaque` (spec 071).

   Nasceram sem classe: os tokens existiam em `vr7-tokens.css` e NENHUMA regra os
   consumia. O exercício do SC-005 (T028a) achou isso ao tentar montar uma tela
   ainda não repaginada só com este vocabulário — os três valores comparados de
   uma divergência de saldo pedem um bloco em destaque dentro do cartão, e não
   havia como pedi-lo pelo nome.

   Degrau sem classe não é vocabulário, é matéria-prima: obriga a próxima subspec
   a inventar o nome, a seguinte a inventar outro, e o §V (v3.8.0) acaba violado
   por duas specs que nunca se falaram. Não muda pixel de nada hoje — ninguém as
   usava, porque não existiam.

   A regra 3 da gramática está aqui em forma de código, e é o que impede a "caixa
   dentro de caixa": o `aninhado` tem BORDA e nenhum fundo próprio; o `destaque`
   tem FUNDO e nenhuma borda. Somar os dois é o defeito que a regra evita. */
.vr7-aninhado {
  border: var(--camada-aninhado-borda);
  border-radius: var(--camada-aninhado-raio);
  padding: var(--esp-4);
  min-width: 0;
}
.vr7-destaque {
  background: var(--camada-destaque-fundo);
  border-radius: var(--camada-destaque-raio);
  padding: var(--esp-4);
  min-width: 0;
}

/* Grid de LEITURA em blocos — nome próprio, NÃO `.vr7-dados`: aquela já existe
   (linha ~1084) com outra forma (rótulo|valor em duas colunas, max-width 40rem)
   e é usada por clientes finais, conta bancária da loja e detalhe de loja.
   Reescrevê-la aqui mudaria o layout daquelas telas sem ninguém pedir.

   Aqui cada par vira um bloco e os blocos se acomodam na largura disponível —
   os dados da conta ocupam a faixa inteira em vez de uma coluna estreita. */
.vr7-dados-conta {
  display: grid;
  gap: var(--esp-4);
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  margin: var(--esp-4) 0;
}
.vr7-dados-conta__item {
  display: grid;
  gap: var(--esp-1);
}
.vr7-dados-conta dt {
  color: var(--cor-texto-2);
  font-size: 0.8125rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.vr7-dados-conta dd {
  margin: 0;
  font-size: 1.0625rem;
}

/* As telas da Conta VR7 têm tabelas largas (extrato com 5 colunas) e um painel
   com muitos dados — usam uma faixa de conteúdo maior que o padrão de leitura,
   sem alterar `--largura-conteudo`, que rege o resto do sistema. */
body:has(.vr7-conta-vr7) .vr7-main,
body:has(.vr7-conta-vr7-extrato) .vr7-main {
  max-width: 90rem;
}

/* Saldo em destaque — é o número que a tela existe para mostrar. A nota da
   FR-021 vem junto, dentro do mesmo bloco: ler o valor sem ela é ler errado. */
.vr7-saldo {
  display: grid;
  gap: var(--esp-1);
  padding: var(--esp-4);
  margin: var(--esp-4) 0;
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  background: var(--cor-superficie-2);
}
.vr7-saldo__rotulo {
  color: var(--cor-texto-2);
  font-size: 0.8125rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.vr7-saldo__valor {
  font-size: 1.75rem;
  line-height: 1.2;
}
.vr7-saldo__nota {
  margin: var(--esp-2) 0 0;
  color: var(--cor-texto-2);
  font-size: 0.875rem;
}

/* Filtros do extrato: rótulo e campo lado a lado, quebrando em telas estreitas. */
.vr7-filtros {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--esp-3);
  margin: var(--esp-4) 0;
}
.vr7-filtros .campo {
  max-width: 12rem;
}

/* Linha de totais do extrato: saldo da conta e total do recorte lado a lado. */
.vr7-totais {
  display: flex;
  flex-wrap: wrap;
  gap: var(--esp-3);
  padding: var(--esp-3) var(--esp-4);
  margin: var(--esp-4) 0;
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  background: var(--cor-superficie-2);
}

/* Seção recolhível (`<details>` nativo — mesmo mecanismo do seletor de loja e do
   menu do usuário; funciona sem JS). A conta vigente abre expandida; preparadas
   e histórico começam recolhidos, porque o que importa ao abrir a tela é qual
   conta está recebendo agora. */
.vr7-cartao--recolhivel {
  padding: 0;
}
.vr7-secao__cabecalho {
  display: flex;
  align-items: center;
  gap: var(--esp-3);
  padding: var(--esp-4) var(--esp-6);
  cursor: pointer;
  list-style: none;
}
.vr7-secao__cabecalho::-webkit-details-marker {
  display: none;
}
.vr7-secao__cabecalho:hover {
  background: var(--cor-superficie-2);
}
.vr7-secao__cabecalho h2 {
  margin: 0;
  font-size: 1.125rem;
}
/* Seta que gira ao abrir — indica que a seção é recolhível sem precisar de texto. */
.vr7-secao__seta {
  margin-left: auto;
  color: var(--cor-texto-2);
  transition: transform 0.15s ease;
}
details[open] > .vr7-secao__cabecalho .vr7-secao__seta {
  transform: rotate(180deg);
}
.vr7-secao__corpo {
  padding: 0 var(--esp-6) var(--esp-6);
}

/* Indicador de situação da conta. `--ativa` é o único em cor de sucesso: numa
   tela onde várias contas convivem, é ele que responde "qual está recebendo?". */
.vr7-indicador {
  display: inline-flex;
  align-items: center;
  gap: var(--esp-1);
  padding: 0.125rem var(--esp-2);
  border: 1px solid var(--cor-borda);
  border-radius: 999px;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--cor-texto-2);
  white-space: nowrap;
}
.vr7-indicador::before {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: currentColor;
}
.vr7-indicador--ativa {
  /* ⚠️ **`--cor-sucesso-texto`, não `--cor-sucesso`** — são papéis diferentes do
     mesmo conceito (a nota está no token). Aqui o verde pinta um rótulo de 12px,
     e o de selo/borda dava 4,27:1 no tema claro. */
  color: var(--cor-sucesso-texto);
  border-color: var(--cor-sucesso);
}
.vr7-indicador--inativa {
  color: var(--cor-texto-2);
}
.vr7-indicador--alerta {
  color: var(--cor-erro);
  border-color: var(--cor-erro);
}
.vr7-secao__contagem {
  color: var(--cor-texto-2);
  font-size: 0.875rem;
}

/* ── spec 042.2 — superfície de Pedidos ─────────────────────────────────────
   Duas peças que não existiam no design system: a confirmação explícita do
   cancelamento (FR-039) e a trilha somente-leitura do histórico (FR-036). */

/* Cancelamento do pedido: bloco destacado, com a marca de confirmação ANTES do
   botão. A caixa é `required` no HTML — o realce existe para que o usuário veja
   que há um ato a praticar, não para substituí-lo. */
.vr7-cancelamento {
  display: grid;
  gap: var(--esp-3);
  justify-items: start;
  margin-top: var(--esp-4);
  padding: var(--esp-4);
  border: 1px solid var(--cor-erro);
  border-radius: var(--raio);
  background: color-mix(in srgb, var(--cor-erro) 6%, transparent);
}
/* `flex-start` e não `center`: o rótulo tem várias linhas, e centrar deixaria a
   caixa flutuando no meio do parágrafo, longe da primeira palavra. */
.vr7-cancelamento__confirmacao {
  display: flex;
  align-items: flex-start;
  gap: var(--esp-2);
  max-width: 46rem;
  line-height: 1.5;
}
.vr7-cancelamento__confirmacao input {
  margin-top: 0.25rem;
  flex: none;
}

/* Histórico do pedido — lista cronológica, sem controle nenhum: a trilha é
   append-only e a tela não oferece o que o banco não permite. */
.vr7-historico__lista {
  list-style: none;
  margin: var(--esp-4) 0 0;
  padding: 0;
  display: grid;
  gap: var(--esp-4);
}
.vr7-historico__item {
  padding-left: var(--esp-4);
  border-left: 2px solid var(--cor-borda);
}
.vr7-historico__cabecalho {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--esp-2);
  margin: 0 0 var(--esp-2);
}
.vr7-historico__subtitulo {
  margin: var(--esp-3) 0 var(--esp-1);
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--cor-texto-2);
}
.vr7-historico__linhas {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--esp-1);
  font-size: 0.9375rem;
}

/* A grade de composição em `<table>` (`.vr7-tabela--linhas-pagamento`) saiu no
   redesign de 2026-08-01: a composição virou CARTÃO de forma (`.vr7-forma`,
   mais abaixo), com as larguras resolvidas pelo `grid-template-columns` em vez
   de por `min-width` de célula. As regras foram removidas junto — CSS de peça
   que não existe mais é a próxima pessoa lendo estilo que nunca se aplica.

   O que a tabela ensinou continua valendo no cartão: largura de campo de
   dinheiro em `ch`, não em px, para acompanhar quem aumenta o zoom. */

/* Filtros da grade de Pedidos. `.vr7-filtros` é um flex-wrap simples, pensado
   para o par único "De/Até" do extrato; com os oito controles desta grade ele
   quebra a linha ENTRE o rótulo e o campo, e a tela passa a mostrar "Estado"
   numa linha e o seletor dele na seguinte — o usuário lê rótulo de um filtro
   junto do campo de outro. O par vira um bloco que não se parte. */
.vr7-filtros__campo {
  display: flex;
  align-items: center;
  gap: var(--esp-2);
  flex: 0 1 auto;
  min-width: 0;
}
.vr7-filtros__campo .rotulo {
  white-space: nowrap;
  padding-top: 0;
}

/* Cobranças da ficha: dinheiro e data não quebram no meio. Com a coluna de
   estado, a tabela ficou apertada e o navegador partia "R$ 250,00" em duas
   linhas — numa tela cujo assunto é quanto o lojista recebe, valor picado é
   ruído que se confunde com erro. O `.vr7-rolagem-x` em volta já garante que
   nada seja cortado: a tabela rola, o número não parte. */
/* ⚠️ `--cobrancas` FOI REMOVIDA (075 · T086, 2026-08-13) — ficou órfã quando
   `pedidos/ficha.html` deixou de ser tabela. Variante sem consumidor é a mesma
   armadilha do vocabulário sem consumidor, pelo avesso: ninguém a remove porque
   ninguém sabe que ela parou de ser usada.

   As outras três da lista original **ficam, com razão escrita**:

   - `--conta-bancaria` e `--chave-pix` — vivas em `clientes_finais/form.html` e
     `fornecedores/form.html`, que **ainda não migraram** (T083). Removê-las
     agora quebraria duas telas por antecipação de um trabalho que não foi
     feito. Saem junto da última tela que as consome, como a `--split` saiu.
   - `--matriz` — viva em `usuarios/matriz.html`. Ela **não é lista**: é uma
     grade de perfis × permissões, onde a coluna É o dado. O vocabulário desta
     spec não a serve, e forçá-la a caber seria pior que deixá-la fora. Exceção
     declarada (FR-016). */

/* `.vr7-dados` é uma grade `max-content 1fr`: o rótulo NUNCA quebra, o que é
   certo para "Cliente" e "Valor total", mas não para o retrato congelado, cujo
   maior rótulo é "Expiração do pagamento instantâneo (minutos)". No celular
   aquela coluna sozinha exigia 409px de página num viewport de 375, e a ficha
   ganhava rolagem horizontal. Aqui — e só aqui — o rótulo pode quebrar. */
.vr7-snapshot .vr7-dados {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
@media (min-width: 48rem) {
  .vr7-snapshot .vr7-dados {
    grid-template-columns: max-content 1fr;
  }
}

/* ── spec 065 — usabilidade da tela de venda ─────────────────────────────────
   A superfície da 042.2 está correta, e é por isso que cansa: cada garantia
   virou parágrafo permanente, clique deliberado ou campo a mais. Aqui a
   garantia não muda — muda onde e quando ela aparece. */

/* Restante a distribuir. Destaque discreto e não alarmante: não é erro, é
   informação de progresso. Quem recusa a composição é o servidor, com
   `soma_divergente`, e a mensagem dele já vem em cor de erro logo acima. */
.vr7-painel-calculo__restante {
  padding: var(--esp-2) var(--esp-3);
  border-left: 3px solid var(--cor-borda);
  background: var(--cor-superficie-2);
  border-radius: var(--raio);
}

/* Rótulo de campo de uma linha em composição — só para leitor de tela: quem
   enxerga lê o `.vr7-formas-cabecalho`, que nomeia a coluna uma vez para todas
   as linhas.

   ⚠️ O seletor era `.vr7-linhas-pagamento .vr7-linha__rotulo`, preso à tela de
   venda. O nome da classe sempre foi genérico ("rótulo de linha"), e prendê-lo
   a uma tela contradizia o motivo de ele ser genérico: a SEGUNDA tela a adotar
   a composição (o cadastro de regra de split, 2026-08-14) montava os mesmos
   `.vr7-forma` e via os três rótulos aparecerem duplicando o cabeçalho de
   colunas. Escopo solto em 2026-08-14; a classe só existe nas duas telas que a
   pedem. */
.vr7-linha__rotulo {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* A composição tem UMA apresentação — os cartões, com uma ou com várias
   linhas (decisão do dono do projeto, 2026-07-31/08-01). A guarda abaixo é a
   lição do defeito que o dono viu na tela: `[hidden]` tem especificidade
   baixíssima, e o `display: grid` do cartão o vence — a linha removida
   voltava a aparecer. Vale para o cartão novo e para qualquer resto tabular. */
.vr7-linhas-pagamento .vr7-forma[hidden],
.vr7-linhas-pagamento .vr7-forma__remover[hidden],
.vr7-linhas-pagamento tr[hidden],
.vr7-linhas-pagamento td[hidden] {
  display: none;
}

/* Campo com ação ao lado (065 · FR-034). O par vive num contêiner próprio para
   que o botão fique FORA do invólucro que o combobox cria em volta do
   `<select>` — dentro, ele seria absorvido e reapareceria em lugar imprevisto. */
.vr7-campo-com-acao {
  display: flex;
  align-items: center;
  gap: var(--esp-2);
}
.vr7-campo-com-acao > .campo,
.vr7-campo-com-acao > .vr7-combobox {
  flex: 1 1 auto;
  min-width: 0;
}
.vr7-btn--icone {
  flex: none;
  width: var(--alvo-toque);
  min-height: var(--alvo-toque);
  padding: 0;
  font-size: 1.25rem;
  line-height: 1;
}

/* Cadastro de cliente promovido a modal. Dentro do diálogo ele deixa de ser um
   recolhível — é o conteúdo —, então o resumo some e o corpo abre direto. */
.vr7-cliente-rapido--no-modal > summary {
  display: none;
}
.vr7-cliente-rapido--no-modal {
  border: 0;
  padding: 0;
  background: transparent;
}

/* ── spec 065 · redesign 2026-08-01 (template do dono do projeto) ────────────
   Duas colunas no desktop — formulário à esquerda, resumo FIXO à direita; no
   mobile, rolagem única com a barra de ação presa ao rodapé. Cartões, chip de
   distribuição, seletor de parcelas com valor por opção. Tokens da casa em
   todas as peças: nada de paleta paralela. */

/* Layout */
.vr7-grade-resumo {
  display: grid;
  /* `minmax(0, 1fr)` e não `1fr`: o segundo é `minmax(auto, 1fr)`, e o `auto`
     impede a coluna de encolher abaixo do conteúdo mínimo dos cartões. Num
     telefone de 390px isso faz a coluna ficar mais larga que a tela — os
     cartões vazam à direita e cada um parece ter uma largura diferente, que foi
     o que o dono viu (2026-08-05). Com `minmax(0, …)` a coluna cede, e quem
     rola é o conteúdo largo, no próprio contêiner. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--esp-4);
  align-items: start;
}
/* `min-width: 0` no contêiner NÃO basta, e foi o que faltou na primeira
   tentativa: ele deixa o próprio elemento encolher, mas os cartões dentro dele
   são itens de grade e nascem com `min-width: auto` — recusam-se a ficar
   menores que o próprio conteúdo mínimo e vazam para fora da coluna.

   Quem resolve é a COLUNA declarada com `minmax(0, …)`. O sinal que separou as
   duas camadas veio do dono, comparando: "a prévia de valores está da largura
   adequada" — mesma coluna, mesmo pai, e só o formulário estourando. Coluna
   certa, cartão errado: o problema estava um nível abaixo. */
.vr7-grade-resumo__principal {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--esp-4);
  min-width: 0;
}
.vr7-grade-resumo__lateral {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--esp-4);
  min-width: 0;
}
@media (min-width: 64rem) {
  .vr7-grade-resumo {
    grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  }
  /* O resumo acompanha a rolagem: o lojista altera as parcelas e vê o líquido
     mudar sem rolar — o ponto do redesign inteiro. */
  .vr7-grade-resumo__lateral {
    position: sticky;
    top: calc(var(--altura-cabecalho) + var(--esp-4));
  }
}

.vr7-cartao__titulo {
  display: flex;
  align-items: center;
  gap: var(--esp-2);
  font-size: 1rem;
  margin: 0 0 var(--esp-4);
}
.vr7-campo-bloco {
  margin-bottom: var(--esp-4);
}
.vr7-campo-bloco .rotulo {
  display: block;
  margin-bottom: var(--esp-1);
}
/* O campo ocupa o bloco — a lacuna que só apareceu quando a SEGUNDA tela adotou
   o padrão (incidentes, 2026-08-13).

   Até ali `.vr7-campo-bloco` só existia na tela de venda, e nas duas vezes o
   `.campo` estava dentro de outro invólucro que já resolvia a largura
   (`.vr7-campo-com-acao`, que é flex, e `.vr7-campo-moeda`). Um `.campo` DIRETO
   no bloco nunca tinha existido, e sem esta regra ele cai na largura intrínseca
   do controle: um `<textarea>` volta aos ~20 caracteres de `cols`, dentro de um
   cartão de coluna inteira.

   O seletor é `>` de propósito. Com descendente, a regra alcançaria os dois
   casos da tela de venda e mudaria a largura do campo de valor total — mexer na
   tela dos outros para consertar a sua é o defeito que a spec 071 catalogou. O
   filho direto é exatamente o caso que faltava, e só ele. */
.vr7-campo-bloco > .campo {
  width: 100%;
}

/* CONTAGEM rótulo:valor dentro de um cartão — a coluna lateral do padrão de
   duas colunas quando o que ela tem a dizer é contagem, não cálculo.

   ⚠️ O nome é `contagem`, e não `resumo`, por uma razão que custou um defeito:
   `.vr7-resumo-total`, `__valor` e `__rotulo` **já existiam** neste arquivo (o
   painel "Você recebe" da tela de venda, ~280 linhas abaixo). Declarados de
   novo aqui — e ANTES —, os dois blocos se misturaram: as propriedades que só
   um deles escrevia passaram a valer para os dois, e o `display: flex` daqui
   deitou numa linha o bloco empilhado de lá. Nada acusou: CSS não tem colisão
   de nome, tem cascata, e o resultado é uma tela alheia mudando em silêncio.
   `tests/test_selector_unico_css.py` passou a reprovar a redeclaração.

   ⚠️ NÃO é `.vr7-kpi`: o KPI traz fundo, borda e raio próprios — dentro de um
   cartão ele produz o "caixa dentro de caixa" que o `README.md` §3 nomeia. Aqui
   o degrau já é o do cartão que contém a lista; o que separa um item do outro é
   a linha de `separação`, que é o degrau feito para isso. */
.vr7-contagem-total {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--esp-2);
  margin: 0 0 var(--esp-4);
}
.vr7-contagem-total__valor {
  font-family: var(--fonte-display);
  font-size: 1.75rem;
  color: var(--cor-marca-forte);
  font-feature-settings: var(--num-tabular);
}
.vr7-contagem-total__rotulo {
  color: var(--cor-texto-2);
}
.vr7-contagem-lista {
  list-style: none;
  margin: 0;
  padding: 0;
}
.vr7-contagem-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--esp-3);
  padding: var(--esp-2) 0;
  border-top: var(--camada-separacao-borda);
}
.vr7-contagem-item__rotulo {
  color: var(--cor-texto-2);
}
/* Números tabulares: a coluna de contagens alinha algarismo com algarismo, e
   um 9 que vira 10 não empurra a linha inteira. */
.vr7-contagem-item__valor {
  font-feature-settings: var(--num-tabular);
}

/* Conversa entre duas partes dentro de um cartão (detalhe do incidente).
   Nome genérico: uma thread de comentários não é um conceito de incidente, e a
   próxima tela que tiver uma não deve precisar copiar isto.

   As classes existiam no template desde a spec 017 e **nenhuma tinha regra** —
   `.vr7-comentarios`, `.vr7-comentario` e os três filhos eram enfeite. Classe
   sem folha é pior que classe ausente: em revisão de código ela parece estilo,
   e ninguém vai conferir na tela o que o HTML já "declara".

   ⚠️ O DEGRAU é o mesmo para os dois lados da conversa — `.vr7-aninhado`,
   pedido no template. Distinguir quem fala por degraus diferentes seria usar a
   escala para dizer *categoria*, e ela diz *distância do que está atrás*
   (README §2). Quem fala se distingue pelo rótulo, abaixo. */
.vr7-comentarios {
  list-style: none;
  margin: 0 0 var(--esp-4);
  padding: 0;
  display: grid;
  gap: var(--esp-3);
}
.vr7-comentario {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: var(--esp-1) var(--esp-3);
}
.vr7-comentario__autor {
  font-weight: 600;
}
/* O único sinal visual de quem fala. `--cor-dourado-texto`, e não
   `--cor-dourado`: o segundo é acento por construção (bordas, foco, linhas) e o
   próprio token avisa "nunca texto pequeno" — foi assim que a 069.1 achou um
   3,60:1 em texto de etiqueta. Este par é o medido para texto. */
.vr7-comentario--suporte .vr7-comentario__autor {
  color: var(--cor-dourado-texto);
}
.vr7-comentario__data {
  color: var(--cor-texto-2);
  font-size: 0.875rem;
  justify-self: end;
}
.vr7-comentario__msg {
  grid-column: 1 / -1;
  margin: 0;
  /* A mensagem é texto de usuário: preserva as quebras que ele digitou e quebra
     palavra sem espaço (URL colada) em vez de esticar o cartão. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* Campo monetário com prefixo R$ dentro do campo */
.vr7-campo-moeda {
  position: relative;
}
.vr7-campo-moeda__prefixo {
  position: absolute;
  left: var(--esp-3);
  top: 50%;
  transform: translateY(-50%);
  color: var(--cor-texto-2);
  font-size: 0.8125rem;
  pointer-events: none;
}
.vr7-campo-moeda .campo {
  padding-left: 2.25rem;
  font-feature-settings: var(--num-tabular);
  max-width: 100%;
}
/* O valor total é o campo mais importante da tela — tamanho de destaque. */
.vr7-campo-destaque .campo {
  font-size: 1.5rem;
  min-height: 3.5rem;
}

/* Campos opcionais em acordeão */
.vr7-campos-extras {
  border-top: 1px solid var(--cor-borda);
  padding-top: var(--esp-1);
}
.vr7-campos-extras > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--esp-2);
  min-height: var(--alvo-toque);
  color: var(--cor-texto-2);
  font-size: 0.875rem;
}
.vr7-campos-extras > summary::-webkit-details-marker {
  display: none;
}
.vr7-campos-extras > summary::before {
  content: "+";
}
.vr7-campos-extras[open] > summary::before {
  content: "−";
}
.vr7-campos-extras__corpo {
  display: grid;
  gap: var(--esp-2);
  padding-top: var(--esp-2);
}

/* Chip de estado (distribuição) */
.vr7-chip {
  margin-left: auto;
  font-size: 0.6875rem;
  padding: 0.25rem 0.5625rem;
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  white-space: nowrap;
  color: var(--cor-texto-2);
}
.vr7-chip--ok {
  color: var(--cor-sucesso);
  border-color: var(--cor-sucesso);
}
/* ⚠️ **Texto pede `--cor-dourado-texto`; borda continua no acento** (076,
   2026-08-18). O chip usava `--cor-dourado-bri` no texto, e a régua de
   navegador mediu **3,27:1** no tema claro — contra os 4,5:1 da AA, em 11px e
   peso 700, que é o tamanho onde menos se pode perder contraste.

   ⚠️ **Nenhuma tela retratada renderizava este estado**, e é por isso que o
   defeito viveu tanto: a variante `--warn` só aparece quando a loja **não** tem
   regra de split, e todo cenário de retrato dava regra a ela. Apareceu quando o
   dono pediu para o retrato mostrar a pendência (2026-08-18) — o cenário mudou
   de lado e a régua acusou na primeira execução.

   `--cor-dourado-texto` existe exatamente para isto: nasceu quando a mesma
   medição pegou o dourado de acento servindo de texto de 12px na superfície
   pública. Escuro mantém o dourado da marca (8,76:1), claro desce ao bronze
   (5,52:1) — a identidade fica, a legibilidade também. */
.vr7-chip--warn {
  color: var(--cor-dourado-texto);
  border-color: var(--cor-dourado);
}
.vr7-chip--danger {
  color: var(--cor-erro);
  border-color: var(--cor-erro);
}

/* Formas de pagamento — cartões em grade */
.vr7-formas-cabecalho {
  display: none;
}
.vr7-forma {
  border: 1px solid var(--cor-borda);
  border-radius: calc(var(--raio) * 2);
  padding: var(--esp-3);
  margin-bottom: var(--esp-3);
  display: grid;
  /* Áreas NOMEADAS, e não auto-colocação: com células pulando de linha por
     conta própria, o campo de valor acabava sobre o de parcelas — visto na
     tela em 2026-08-01. Nomear é o que torna o posicionamento imune à ordem
     dos filhos. */
  grid-template-columns: minmax(0, 1fr) var(--alvo-toque);
  grid-template-areas:
    "metodo   remover"
    "valor    valor"
    "parcelas parcelas"
    "cond     cond";
  gap: var(--esp-3);
  align-items: center;
}
.vr7-forma > * {
  min-width: 0;
}
.vr7-forma__metodo {
  grid-area: metodo;
}
.vr7-forma__valor {
  grid-area: valor;
}
.vr7-forma__parcelas {
  grid-area: parcelas;
}
.vr7-forma__remover {
  grid-area: remover;
  justify-self: end;
}
.vr7-forma__cond {
  grid-area: cond;
}
.vr7-forma__remover {
  width: var(--alvo-toque);
  height: var(--alvo-toque);
  display: grid;
  place-items: center;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--cor-texto-2);
  font-size: 1.125rem;
  border-radius: var(--raio);
}
.vr7-forma__remover:hover {
  background: var(--cor-superficie-2);
  color: var(--cor-erro);
}
.vr7-forma__remover:focus-visible {
  outline: var(--foco-anel);
  outline-offset: 2px;
}
@media (min-width: 48rem) {
  .vr7-forma {
    grid-template-columns:
      minmax(7rem, 0.9fr) minmax(0, 1fr) minmax(0, 1.2fr)
      var(--alvo-toque);
    /* `metodo` NÃO atravessa para a 2ª linha: com o Remover escondido (linha
       única) o select de forma centrado em duas linhas fazia o conjunto
       parecer deslocado para cima. A 2ª linha começa vazia na 1ª coluna e as
       condições ocupam o resto. */
    grid-template-areas:
      "metodo valor parcelas remover"
      ".      cond  cond     cond";
  }
  .vr7-formas-cabecalho {
    display: grid;
    grid-template-columns:
      minmax(7rem, 0.9fr) minmax(0, 1fr) minmax(0, 1.2fr)
      var(--alvo-toque);
    gap: var(--esp-3);
    padding: 0 var(--esp-3) var(--esp-1);
    font-size: 0.6875rem;
    color: var(--cor-texto-2);
  }
}

/* ── Composição de UMA linha, sem a coluna do Remover ────────────────────────
   A tela de venda compõe N linhas e cada uma pode ser removida; o cadastro de
   regra de split compõe UMA por vez. A coluna de 44px do Remover ficava vazia —
   56px com o vão, num cartão que já disputa espaço dentro da coluna principal
   do padrão de duas colunas.

   E não é só o vão: as duas células do meio aqui são COMPOSTAS (seletor %/R$ +
   valor), não um input simples. `.vr7-componente` pede ~13,6rem no mínimo
   (dois segmentos de 3rem, o vão e um campo de 7rem) contra os ~7rem de um
   campo de moeda — e, sendo `inline-flex` com largura fixa no campo, ele **não
   cedia**: estourava a coluna e a Taxa cavalgava o FI. Visto na tela pelo dono
   em 2026-08-14.

   Os dois consertos andam juntos e nenhum sozinho basta: devolver os 56px sem
   deixar o valor encolher só adia o encontro para a largura seguinte. */
.vr7-forma--unica {
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "metodo"
    "valor"
    "parcelas"
    "cond";
}
@media (min-width: 48rem) {
  .vr7-formas-cabecalho--unica,
  .vr7-forma--unica {
    /* As duas células compostas ficam IGUAIS entre si: aqui elas carregam a
       mesma coisa (um componente de retenção), ao contrário de "Valor" e
       "Parcelas" da tela de venda, que pedem larguras diferentes. */
    grid-template-columns: minmax(7rem, 0.9fr) minmax(0, 1.05fr) minmax(0, 1.05fr);
  }
  .vr7-forma--unica {
    grid-template-areas:
      "metodo valor parcelas"
      ".      cond  cond";
  }
  /* As colunas das condições são as MESMAS que a área `cond` cobre — a faixa
     "da parcela" alinha com a Taxa e "até a parcela" com o FI, borda com borda.
     Sem isto vale a regra da tela de venda, que reserva uma 3ª faixa para a
     coluna do Remover que aqui não existe. */
  .vr7-forma--unica .vr7-forma__cond {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* O par %/R$ + valor OCUPA a célula da composição, em vez de transbordar dela.
   `.vr7-componente` nasceu para célula de tabela (a grade de split da loja),
   onde `inline-flex` + campo de 7rem é exatamente o certo; dentro de uma coluna
   de grade ele precisa ceder. O seletor é qualificado por `.vr7-forma` para não
   mexer na tela de onde ele veio — o mesmo cuidado do `.vr7-campo-bloco >
   .campo`. */
.vr7-forma .vr7-componente {
  display: flex;
  width: 100%;
  min-width: 0;
}
.vr7-forma .vr7-componente > .vr7-segmento {
  flex: 0 0 auto;
}
/* `min-width: 0` é o que faz a diferença: sem ele o input recusa ficar menor
   que a largura intrínseca do controle (~20 caracteres de `size`) e volta a
   empurrar a célula vizinha — a mesma armadilha que o `minmax(0, 1fr)` das
   colunas do padrão de duas colunas resolve um nível acima. */
.vr7-forma .vr7-componente > .campo {
  width: auto;
  flex: 1 1 0;
  min-width: 0;
}

/* Ações da composição */
.vr7-formas-acoes {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--esp-3);
}
.vr7-btn--tracejado {
  border-style: dashed;
  color: var(--cor-texto-2);
  width: 100%;
}
.vr7-btn--fantasma {
  border-color: transparent;
  color: var(--cor-texto-2);
}
.vr7-formas-acoes__minimo {
  margin-left: auto;
  font-size: 0.75rem;
}
@media (min-width: 48rem) {
  .vr7-btn--tracejado {
    width: auto;
  }
}

/* Resumo — "Você recebe" em destaque */
.vr7-resumo-total {
  background: var(--cor-superficie-2);
  border-radius: var(--raio);
  padding: var(--esp-4);
  margin-bottom: var(--esp-4);
}
.vr7-resumo-total__rotulo {
  margin: 0;
  font-size: 0.75rem;
  color: var(--cor-texto-2);
}
.vr7-resumo-total__valor {
  margin: 0.125rem 0 0;
  font-size: 1.75rem;
  font-weight: 600;
  font-feature-settings: var(--num-tabular);
}
.vr7-resumo-total__detalhe {
  margin: var(--esp-1) 0 0;
  font-size: 0.75rem;
  color: var(--cor-texto-2);
}

.vr7-resumo-forma {
  border-bottom: var(--camada-separacao-borda);
  padding-bottom: var(--esp-3);
  margin-bottom: var(--esp-3);
}
.vr7-resumo-forma__titulo {
  display: flex;
  align-items: baseline;
  gap: var(--esp-2);
  font-size: 0.875rem;
  font-weight: 600;
  margin: 0 0 var(--esp-2);
}
.vr7-resumo-forma__titulo .vr7-suave {
  font-weight: 400;
}
.vr7-resumo-forma__liquido {
  margin-left: auto;
  font-feature-settings: var(--num-tabular);
  white-space: nowrap;
}
.vr7-resumo-linha {
  display: flex;
  justify-content: space-between;
  gap: var(--esp-3);
  font-size: 0.8125rem;
  color: var(--cor-texto-2);
  padding: 0.125rem 0;
}
.vr7-resumo-linha span:last-child {
  font-feature-settings: var(--num-tabular);
  white-space: nowrap;
}
/* A linha do LÍQUIDO fecha a conta das três acima (bruto − taxa − FI), e por
   isso não pode ser lida como mais uma parcela dela: ganha o tom do texto
   principal, peso e um traço que a separa do que ela resume. Achado da
   convergência — a classe existia no template e não tinha regra nenhuma, então
   o resultado renderizava idêntico às suas próprias entradas. */
.vr7-resumo-linha--liquido {
  color: var(--cor-texto);
  font-weight: 600;
  margin-top: var(--esp-1);
  padding-top: var(--esp-2);
  border-top: 1px solid var(--cor-borda);
}
.vr7-previa-parcelas > summary {
  cursor: pointer;
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: var(--esp-2);
  color: var(--cor-texto-2);
  font-size: 0.8125rem;
  min-height: 2rem;
}
.vr7-previa-parcelas > summary::-webkit-details-marker {
  display: none;
}
.vr7-previa-parcelas > summary::before {
  content: "▸";
  transition: transform 0.15s ease;
}
.vr7-previa-parcelas[open] > summary::before {
  transform: rotate(90deg);
}
.vr7-previa-parcelas > summary:hover {
  color: var(--cor-texto);
}
/* Zebra por parcela dentro do detalhe. */
.vr7-previa-parcelas__lista .vr7-resumo-linha:nth-child(odd) {
  background: color-mix(in srgb, var(--cor-borda) 18%, transparent);
}

/* Barra de ações do resumo: fixa no rodapé no mobile, estática no desktop.

   O `bottom` é o token da barra de navegação, e não `0` (spec 068 / research
   D11): no telefone a barra inferior de atalhos ocupa exatamente esta
   coordenada, e as duas se cobririam — nesta tela, que é a de maior tráfego do
   produto. As ações do pedido ficam ACIMA da navegação: a ação da tela é mais
   urgente que a troca de tela.

   O valor mora aqui, na regra original, e não numa regra nova lá em cima: a
   regra de baixo é a que vence por ordem de arquivo, e o remendo distante
   parecia funcionar sem funcionar. */
.vr7-acoes-pedido {
  position: sticky;
  bottom: var(--altura-barra-inferior);
  z-index: 10;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--esp-3);
  padding: var(--esp-3) var(--esp-4)
    max(var(--esp-3), env(safe-area-inset-bottom));
  background: var(--cor-superficie);
  border-top: 1px solid var(--cor-borda);
  margin: 0 calc(-1 * var(--esp-4));
}
@media (min-width: 64rem) {
  .vr7-acoes-pedido {
    position: static;
    margin: 0;
    padding: var(--esp-3) 0 0;
    background: none;
  }
  .vr7-acoes-pedido .vr7-btn--primario {
    width: 100%;
    order: -1;
  }
}

/* Condições da linha — 1º vencimento e periodicidade VISÍVEIS lado a lado
   (decisão do dono do projeto, 2026-08-01, no lugar do bloco recolhido: são só
   dois campos e ocupam o mesmo espaço que o resumo ocupava). */
.vr7-forma__cond {
  /* `grid-area` repetida AQUI de propósito: este bloco fica longe do bloco do
     cartão, e uma reorganização futura do arquivo poderia deixá-lo sem a
     âncora — aí a auto-colocação levaria o 1º vencimento para a célula vazia
     do Remover escondido, na linha de cima. */
  grid-area: cond;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--esp-3);
  align-self: start;
}
@media (min-width: 48rem) {
  /* As colunas das condições são AS MESMAS da linha de cima (valor | parcelas
     | célula do remover): o 1º vencimento alinha com Valor e a Periodicidade
     com Parcelas, borda com borda. Uma divisão própria (o repeat(2) do mobile)
     partia o vão ao meio e a Periodicidade caía deslocada da coluna dela —
     apontado pelo dono na tela em 2026-08-01. */
  .vr7-forma__cond {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr) var(--alvo-toque);
  }
  .vr7-forma__cond > div:nth-child(1) {
    grid-column: 1;
  }
  .vr7-forma__cond > div:nth-child(2) {
    grid-column: 2;
  }
}
.vr7-forma__cond-rotulo {
  display: block;
  font-size: 0.75rem;
  color: var(--cor-texto-2);
  margin-bottom: 0.125rem;
  padding-top: 0;
}

/* Select de opção única, travado (regra GLOBAL do DS, 2026-08-01 — ver
   `travarSelectsDeOpcaoUnica` no vr7.js e a convenção no CLAUDE.md): uma opção
   só não é escolha, é informação. Interação fora, valor DENTRO da submissão —
   por isso nunca `disabled`. A seta some porque não há o que abrir. */
.vr7-select-travado {
  pointer-events: none;
  background-image: none;
  appearance: none;
  background-color: var(--cor-superficie-2);
  color: var(--cor-texto-2);
}

/* Campo de texto travado — o irmão do `.vr7-select-travado` para input, hoje o
   titular derivado da ficha (045.2 · FR-004a). Mesma regra e mesmo motivo:
   interação fora, valor DENTRO da submissão, e por isso `readonly` no HTML,
   NUNCA `disabled` — campo desabilitado não é submetido. Sem
   `pointer-events: none` de propósito: `readonly` já impede a edição, e um
   campo que decide para quem o dinheiro vai tem de continuar focável para ser
   lido e copiado. */
.vr7-campo-travado {
  background-color: var(--cor-superficie-2);
  color: var(--cor-texto-2);
}

/* Informação contextual (065 · US3). O ícone fica ao lado do que explica; o
   texto só aparece quando pedido. É `<details>` nativo — funciona sem script, e
   `ligarPopovers` acrescenta Esc, clique fora e exclusão mútua. */
.vr7-dica {
  display: inline-block;
  position: relative;
  vertical-align: middle;
}
.vr7-dica__gatilho {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.375rem;
  height: 1.375rem;
  margin-left: var(--esp-1);
  border: 1px solid var(--cor-borda);
  border-radius: 50%;
  color: var(--cor-texto-2);
  font-size: 0.8125rem;
  font-weight: 700;
  font-style: italic;
  line-height: 1;
  cursor: pointer;
  list-style: none;
}
.vr7-dica__gatilho::-webkit-details-marker {
  display: none;
}
.vr7-dica__gatilho:hover {
  color: var(--cor-texto);
  border-color: var(--cor-dourado);
}
.vr7-dica__gatilho:focus-visible {
  outline: var(--foco-anel);
  outline-offset: 2px;
}
.vr7-dica[open] > .vr7-dica__gatilho {
  color: var(--cor-texto);
  border-color: var(--cor-dourado);
}
.vr7-dica__balao {
  position: absolute;
  z-index: 30;
  top: calc(100% + var(--esp-2));
  left: 0;
  width: max-content;
  max-width: min(26rem, calc(80vw - 2 * var(--esp-4)));
  padding: var(--esp-3) var(--esp-4);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  background: var(--cor-superficie-2);
  box-shadow: var(--sombra-suspensa);
  font-size: 0.9375rem;
  font-weight: 400;
  font-style: normal;
  line-height: 1.55;
  white-space: normal;
  text-align: left;
}
.vr7-dica__titulo {
  display: block;
  margin-bottom: var(--esp-2);
}
.vr7-dica__balao p {
  margin: 0 0 var(--esp-2);
}
.vr7-dica__balao p:last-child {
  margin-bottom: 0;
}
.vr7-dica--fim .vr7-dica__balao {
  left: auto;
  right: 0;
}

/* Prévia desatualizada (065 · FR-029/FR-028d): recálculo em voo ou pausa por
   limite — números que já não correspondem aos campos nunca se apresentam como
   atuais. */
.vr7-painel-calculo.htmx-request,
.vr7-painel-calculo--atualizando,
.vr7-painel-calculo[data-desatualizado] {
  opacity: 0.55;
  transition: opacity 0.15s ease;
}
.vr7-painel-calculo.htmx-request::before,
.vr7-painel-calculo--atualizando::before,
.vr7-painel-calculo[data-desatualizado]::before {
  content: "Atualizando os valores…";
  display: block;
  margin-bottom: var(--esp-2);
  color: var(--cor-texto-2);
  font-size: 0.875rem;
  font-style: italic;
}
.vr7-painel-calculo[data-desatualizado]::before {
  content: "Atualização pausada — os valores abaixo podem estar desatualizados.";
}

/* Aviso do efeito do total agregado (065 · FR-032/FR-033) — no FLUXO, nunca
   atrás do ícone: o que se esconde é a explicação geral, jamais o fato. */
.vr7-painel-calculo__agregado {
  margin: var(--esp-3) 0;
  border-left: 3px solid var(--cor-dourado);
  background: color-mix(in srgb, var(--cor-dourado) 8%, transparent);
}

/* Visualização da loja: título colado no dado que ele nomeia.
   A tela herdava dois espaçamentos somados — a margem padrão do navegador em
   torno de cada `h1`/`h2` e o `gap: --esp-8` do `.vr7-painel`, que foi calibrado
   para a home (blocos independentes), não para um rótulo e a tabela dele. Dava
   ~3rem entre "Conta de repasse" e a conta, e o olho lia o título como sobrando
   no fim da seção anterior em vez de abrindo a seguinte. Aqui o respiro fica
   ENTRE as seções, não entre o nome e o conteúdo. */
.vr7-form-loja > h1 { margin: 0 0 var(--esp-3); }
.vr7-form-loja .vr7-situacao-loja { margin-bottom: var(--esp-4); }
.vr7-form-loja .vr7-situacao-loja p { margin: 0 0 var(--esp-1); }
.vr7-form-loja .vr7-painel { gap: var(--esp-2); margin-top: var(--esp-6); }
.vr7-form-loja .vr7-painel > h2 { margin: 0; }
.vr7-form-loja .vr7-painel > p { margin: 0; }

/* ── Revisão de cadastro em /aprovacoes (tela "Analisar") ────────────────── */

/* O `<form>` que carrega só o CSRF (abas, aprovar, rejeitar). Os botões dele
   moram FORA, ligados por `form=` — então ele não tem nada visível dentro, mas
   ocupava espaço duas vezes: pela margem padrão do navegador E, por ser filho
   direto do `.vr7-painel` (que é grid), como um ITEM DE GRID — uma linha inteira
   vazia, com o `gap: --esp-8` aplicado dos dois lados. Três desses na tela de
   análise davam faixas de nada entre as abas, o conteúdo e a decisão.
   `display: none` não afeta o envio: o `form=` associa o botão ao formulário
   pela árvore do documento, não pelo leiaute, e `input[hidden]` viaja igual. */
.vr7-form-token { display: none; }

/* A etapa em modo consulta é para LER: rótulo e valor um sob o outro, na
   vertical, sem o respiro de formulário que se preenche. A linha do grid do
   formulário herdaria o espaçamento de digitação (--esp-4). Vale só sob
   `.vr7-revisao` — o mesmo template serve o lojista preenchendo, e lá o
   espaçamento de formulário continua sendo o certo (FR-203).

   O `gap` desce de --esp-8 (do `.vr7-painel`) para --esp-4 porque em revisão os
   itens do painel são três e cada um já tem respiro próprio: o cabeçalho, as
   abas e a grade de duas colunas.

   ⚠️ As regras de margem por FILHO DIRETO que existiam aqui (`> h1`, `> h2`,
   `> p`) saíram com o layout de duas colunas de 2026-08-14: o `<h1>` passou ao
   cabeçalho da moldura e o resto do conteúdo mora dentro de `.vr7-cartao`, onde
   o fluxo normal é o certo. Seletor de filho direto que deixa de casar não faz
   barulho — some do estilo sem sumir do arquivo. */
.vr7-revisao { gap: var(--esp-4); }
.vr7-revisao .formulario-grade { row-gap: var(--esp-2); }
.vr7-revisao .vr7-abas { margin-bottom: 0; }

/* A marca de campo obrigatório não existe em CONSULTA (decisão do dono,
   2026-08-14): *"se já chegou neste estágio, os campos obrigatórios já foram
   preenchidos corretamente no cadastro"*. O asterisco pede uma ação de quem
   preenche; para quem revisa ele é ruído em 38 rótulos, e ruído repetido é o
   que faz a pessoa parar de ler os rótulos.

   Por regra na folha e não por `{% if %}` no template: são 38 ocorrências em
   seis arquivos, cada uma dentro de um `<label>` diferente. Uma condicional por
   ocorrência é uma chance de erro por ocorrência, e as seis telas servem
   também o lojista, onde o asterisco continua valendo. Aqui o modo é declarado
   uma vez — `.vr7-revisao`, na `<section>` — e a regra decorre dele. */
.vr7-revisao .vr7-obrigatorio {
  display: none;
}


/* ===========================================================================
   DOCUMENTO DE PAGAMENTO — a página pública do boleto/PIX (062.3 · 069.1)
   ---------------------------------------------------------------------------
   O fragmento era montado inteiro em `style=` inline, o que a §V proíbe e o que
   produzia o resultado que o dono chamou de "péssimo": sem hierarquia, sem
   respiro, tudo encostado na borda. Não era falta de capricho — era a ausência
   de vocabulário numa tela feita à mão.

   O que a repaginação decide, e por quê:

   - **o valor vem primeiro e grande.** É a pergunta de quem abre o documento, e
     vinha em terceiro lugar numa fileira de três campos do mesmo tamanho;
   - **cada forma de pagamento na sua faixa**, com título — linha digitável,
     código de barras e PIX eram três blocos soltos sem nome;
   - **a linha digitável antes do código de barras.** No telefone ela é o caminho
     principal (o dono: *"ninguém vai ler um código de barras no celular"*), e
     estava embaixo, em cinza de rodapé.
   =========================================================================== */
.vr7-boleto {
  max-width: 44rem;
  margin: 0 auto;
  background: var(--camada-cartao-fundo);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  padding: var(--esp-8);
}

.vr7-boleto__topo {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--esp-4);
  padding-bottom: var(--esp-4);
  border-bottom: var(--camada-separacao-borda);
}
.vr7-boleto__rotulo {
  margin: 0;
  font-size: var(--texto-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cor-texto-2);
}
.vr7-boleto__beneficiario {
  margin: var(--esp-1) 0 0;
  font-family: var(--fonte-display);
  font-size: var(--texto-md);
  font-weight: 600;
}
.vr7-boleto__banco {
  font-size: var(--texto-sm);
  color: var(--cor-texto-2);
  text-align: right;
}

/* O bloco do valor. `--cor-superficie-2` o separa do corpo sem uma borda a mais:
   a hierarquia vem da superfície, não de mais um traço. */
.vr7-boleto__destaque {
  margin: var(--esp-6) 0;
  padding: var(--esp-6);
  background: var(--cor-superficie-2);
  border-radius: var(--raio);
}
.vr7-boleto__valor {
  margin: var(--esp-1) 0 0;
  font-family: var(--fonte-display);
  font-size: clamp(1.8rem, 6vw, 2.4rem);
  font-weight: 700;
  line-height: 1.1;
  font-feature-settings: var(--num-tabular);
}
.vr7-boleto__vencimento {
  margin: var(--esp-2) 0 0;
  font-size: var(--texto-sm);
  color: var(--cor-texto-2);
}
.vr7-boleto__vencimento strong {
  color: var(--cor-texto);
  font-feature-settings: var(--num-tabular);
}

.vr7-boleto__dados {
  margin: 0 0 var(--esp-6);
}
.vr7-boleto__dados dt {
  font-size: var(--texto-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cor-texto-2);
}
.vr7-boleto__dados dd {
  margin: var(--esp-1) 0 0;
  font-weight: 600;
}

.vr7-boleto__forma + .vr7-boleto__forma {
  margin-top: var(--esp-6);
  padding-top: var(--esp-6);
  border-top: 1px solid var(--cor-borda);
}
.vr7-boleto__forma-titulo {
  margin: 0 0 var(--esp-3);
  font-size: var(--texto-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--cor-texto-2);
}

/* A linha digitável é para COPIAR: monoespaçada, selecionável e quebrando em
   qualquer ponto — 47 dígitos não cabem em 390px, e sem quebra a página inteira
   rolava de lado, com o número fugindo da vista de quem veio pagar. */
.vr7-boleto__linha {
  margin: 0;
  font-family: ui-monospace, "Cascadia Mono", "Segoe UI Mono", Consolas, monospace;
  font-size: var(--texto-base);
  letter-spacing: 0.02em;
  word-break: break-all;
  user-select: all; /* um toque seleciona a linha inteira */
}
.vr7-boleto__linha--crua {
  /* Menor que `--texto-xs` de propósito: com 0,72rem o último dígito da linha de
     44 caracteres caía sozinho na linha de baixo em 390px — um número órfão que
     parece erro de leitura. 0,66rem cabe inteiro, e este texto é referência
     visual (quem copia usa o botão acima), não leitura. */
  font-size: 0.66rem;
  letter-spacing: 0;
  color: var(--cor-texto-2);
  /* Centrada como a caixa acima: alinhada à esquerda ela ficava desencontrada
     do código que descreve. */
  text-align: center;
  margin-top: var(--esp-2);
}

/* O código de barras nasce com a largura NATURAL (405px para 44 dígitos) e em
   tela de 390px fazia a PÁGINA rolar de lado. `overflow-x` no contêiner e
   `width:100%` não contiveram — algum ancestral é dimensionado pelo conteúdo —,
   então a regra limita o PRÓPRIO SVG e não depende do ancestral.

   Encolher é a resposta certa, e a decisão é do dono (2026-08-07): *"ninguém vai
   ler um código de barras no celular"*. Barra estreita perde precisão de leitura
   ótica, mas ninguém aponta leitor para tela de telefone — quem paga ali usa a
   linha digitável ou o PIX. O código de barras serve ao caixa, em papel
   impresso, onde a largura volta a ser a natural. */
.vr7-boleto-barras {
  overflow-x: auto;
  /* `fit-content` + `margin auto`: a caixa encolhe até o código e fica
     CENTRADA, com o mesmo respiro dos quatro lados.

     Antes ela era um bloco: ocupava a largura inteira do cartão (44rem) com um
     código de 405px encostado à esquerda, e sobrava uma área branca vazia à
     direita. No tema escuro isso lia como **código de barras cortado** — o olho
     interpreta o branco que sobra como parte do código que faltou imprimir. Num
     documento de pagamento, parecer incompleto é o pior defeito possível. */
  width: fit-content;
  max-width: 100%;
  margin: 0 auto;
  /* Fundo claro **nos dois temas** — é a exceção que o dono declarou em
     2026-08-07. Leitor ótico lê barra ESCURA sobre fundo CLARO; no tema escuro,
     barra preta sobre preto simplesmente não existe para o leitor. O mesmo vale
     para o QR logo abaixo. */
  background: var(--cor-leitura-optica);
  padding: var(--esp-3);
  border-radius: var(--raio);
}
.vr7-boleto-barras svg {
  display: block;
  max-width: 100%;
  height: auto;
}

.vr7-boleto__qr {
  display: block;
  width: 11rem;
  height: 11rem;
  margin: 0 0 var(--esp-3);
  padding: var(--esp-2);
  background: var(--cor-leitura-optica);
  border-radius: var(--raio);
  image-rendering: pixelated;
}

@media (max-width: 30rem) {
  .vr7-boleto {
    padding: var(--esp-4);
    border: none;
  }
}

/* Impressão: o documento é o que vai para o papel — o resto da página, não. */
@media print {
  .vr7-boleto {
    border: none;
    padding: 0;
    max-width: none;
  }
  .vr7-boleto__destaque {
    background: none;
    padding: 0;
  }
}

/* O invólucro da página pública de pagamento. Era `style="padding:1.5rem"` no
   `<body>`, e o respiro não sobrevivia à impressão nem acompanhava a largura. */
.vr7-pagina-pagamento {
  padding: var(--esp-8) var(--esp-4);
  background: var(--cor-fundo);
  min-height: 100vh;
}
.vr7-pagina-pagamento__acoes {
  max-width: 44rem;
  margin: var(--esp-6) auto 0;
  text-align: center;
}
@media print {
  .vr7-pagina-pagamento {
    padding: 0;
    background: none;
    min-height: 0;
  }
  /* O botão de imprimir não vai para o papel — é o gesto, não o conteúdo. */
  .vr7-pagina-pagamento__acoes {
    display: none;
  }
}

/* Botão de copiar (069.1) — nasce escondido e o `copiar.js` o revela. */
.vr7-copiar {
  /* NÃO sobrescreve o `min-height` do `.vr7-btn`. A primeira versão pôs
     `min-height: auto` para o botão não parecer grande ao lado de um título
     pequeno — e a régua mediu **27px**, contra os 44 do FR-201. Numa tela de
     pagamento, no telefone, o alvo é o que decide se a pessoa copia o número ou
     erra o toque três vezes. Quem cede é o texto, não o alvo. */
  padding: var(--esp-2) var(--esp-4);
  font-size: var(--texto-sm);
  flex: none;
}
.vr7-copiar--feito {
  border-color: var(--cor-sucesso);
  color: var(--cor-sucesso);
}
.vr7-boleto__forma-topo {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--esp-3);
  margin-bottom: var(--esp-3);
}
.vr7-boleto__forma-topo .vr7-boleto__forma-titulo {
  margin-bottom: 0;
}
@media print {
  .vr7-copiar {
    display: none;
  }
}

/* ── Extrato da conta interna: as bordas do saldo e a projeção (spec 073.3) ───

   Três classes, e **nenhuma** delas escreve cor, borda, raio ou sombra: o degrau
   de profundidade é pedido pela classe do vocabulário (`.vr7-aninhado`), como o
   `app/static/css/README.md` manda. O que mora aqui é só layout e tipografia.

   Isso não é economia — é a regra da casa aplicada: a distinção entre o que já
   aconteceu e o que vai acontecer **não pode** depender de cor (FR-032a). Ela é
   carregada pela estrutura (seções, degrau, cabeçalho) e pelo **texto** do
   estado, que vem escrito do service. Se a cor sumir, a informação fica. */
.vr7-extrato-bordas {
  display: grid;
  gap: var(--esp-4);
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  margin: var(--esp-4) 0;
}
.vr7-extrato-bordas__item {
  display: grid;
  gap: var(--esp-1);
}
.vr7-extrato-bordas dt {
  color: var(--cor-texto-2);
  font-size: var(--texto-sm);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.vr7-extrato-bordas dd {
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 700;
}

.vr7-projecao {
  margin: var(--esp-4) 0;
}
.vr7-projecao > h3 {
  margin-top: 0;
}

/* O cabeçalho do dia dentro do corpo da tabela. `<th>` de linha, alinhado à
   esquerda: a separação visual já vem do `--camada-separacao-borda` que a
   `.vr7-tabela` aplica em toda célula — repetir a borda aqui seria o segundo
   degrau que a gramática do design system proíbe. */
.vr7-tabela__grupo > th {
  text-align: left;
  font-weight: 700;
  letter-spacing: 0.02em;
}

/* ===========================================================================
   spec 075 — O REGISTRO: a tabela ganha vocabulário e cabe no telefone
   ---------------------------------------------------------------------------
   Duas famílias, medidas antes de decidir (research · D-001):

   - **lista de trabalho** (58 tabelas) — só texto e link. Continua TABELA no
     computador, porque densidade é o trabalho do operador interno: ~17 registros
     por tela contra ~7 em cartão, e a coluna alinhada é o que deixa varrer
     valores. Vira REGISTRO no telefone.
   - **conjunto com controle** (7 tabelas) — tem campo dentro do corpo. Deixa de
     ser tabela nas TRÊS faixas: um campo dentro de célula não tem para onde
     encolher, e em 390px ou transborda ou some. Foi assim que o campo de anexo
     da etapa de sócios ficou invisível — o defeito que originou esta spec.

   **Não são dois componentes.** É um, com duas densidades, alimentado por UMA
   declaração de papéis (`app/papeis_de_coluna.py`). Mudar o papel de uma coluna
   muda os dois arranjos juntos, porque só existe um lugar a mudar — é isso que
   impede a divergência que produziu as 5 variantes ad-hoc.

   ⚠️ **A faixa de 768 segue o telefone POR ORA** (I7 · FR-003). A decisão é de
   medição e acontece na Fase 3, com a razão escrita no `NOTAS.md`; ela cai na
   fronteira, porque o rail come 60px e sobram ~700 úteis. Enquanto não houver
   medida, o padrão é o lado seguro: registro não transborda, tabela larga sim.

   Escrito DO TELEFONE PARA CIMA, como o resto do arquivo (068 · FR-011a): o
   estado base é o registro, e `64rem` traz a tabela. Um par
   `max-width`/`min-width` fracionário erra em zoom e em arredondamento.
   =========================================================================== */

/* ── O registro: uma LINHA da grade, não um cartão ────────────────────────

   ⚠️ **Ele já foi cartão, e o retrato reprovou** (2026-08-11, decisão do dono
   olhando a imagem gerada). Um cartão por forma produzia caixa dentro de caixa,
   desperdiçava a largura no computador e — o pior — **perdia o alinhamento
   entre formas** sem repor nada no lugar. A tabela era feia e deixava varrer a
   coluna; a primeira versão do registro não fazia nem uma coisa nem outra.

   Agora o registro é **linha**, separada por borda, dentro de UM cartão. É a
   regra 3 da gramática de superfície (071) aplicada: quem não ganha fundo
   próprio se delimita por borda. E o alinhamento volta — no computador as
   colunas se correspondem entre todas as linhas.

   O degrau vem de `--camada-separacao-borda`, não do cartão: o cartão é o
   contêiner, e é ele que já empresta fundo, borda e raio ao conjunto. */
.vr7-registro {
  display: grid;
  /* `--esp-3` e não `--esp-2`: o espaço ENTRE campos comparados precisa ser
     visivelmente maior que o de dentro de um (`--esp-1`), senão o apoio de um
     encosta no controle do seguinte. Medido em 390px — é a mesma armadilha de
     agrupamento por proximidade, dois níveis acima. */
  gap: var(--esp-3);
  padding: var(--esp-3) 0;
  border-bottom: var(--camada-separacao-borda);
  min-width: 0; /* item de grade nasce com `min-width: auto` e vaza no telefone */
}
.vr7-registro:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}
.vr7-registro:first-child {
  padding-top: 0;
}
.vr7-registro__identificador {
  margin: 0;
  font-weight: 700;
}

/* Todo controle dentro do registro é tocável (FR-013 · FR-201). Alvo é ÁREA:
   um dedo erra na horizontal tanto quanto na vertical — foi o achado do sino
   na 069.4, que media 38×44 e passava por alto o bastante. */
.vr7-registro .campo,
.vr7-registro select,
.vr7-registro textarea,
.vr7-registro button {
  min-height: var(--alvo-toque);
}

/* ── O rótulo viaja na célula (lista de trabalho) ─────────────────────────
   No telefone não existe cabeçalho de coluna, e é o `data-rotulo` que nomeia o
   valor. É o que permite UM DOM só: sem ele, a tela teria de emitir a tabela E
   os cartões em paralelo — peso dobrado, leitura de tela dobrada e, na família
   com controle, campo submetido duas vezes. */
.vr7-registro td::before {
  content: attr(data-rotulo);
  display: block;
  color: var(--cor-texto-2);
  font-size: var(--texto-xs);
}
.vr7-registro > [data-papel="detalhe"] {
  display: none; /* `detalhe` nunca aparece no resumo, em nenhuma faixa (I2) */
}

/* ── A expansão ───────────────────────────────────────────────────────────
   `<details>` nativo: abre sem JavaScript (FR-010 · I8), o mesmo padrão do
   painel da 068. Informação que só existe com script é informação que alguém
   não terá. */
.vr7-registro__expansao > summary {
  min-height: var(--alvo-toque);
  display: flex;
  align-items: center;
  cursor: pointer;
  color: var(--cor-texto-2);
  font-size: var(--texto-sm);
}

/* ── O campo comparado ────────────────────────────────────────────────────
   Rótulo à esquerda, controle à direita, e o valor anterior como APOIO abaixo
   — nunca como um segundo termo de igual peso.

   ⚠️ **Quando não há valor anterior, o apoio não existe** — nem "de —", nem
   "Variação —". O primeiro desenho imprimia o andaime da comparação com um dos
   lados vazio, e o resultado foi quatro linhas de travessão que não diziam
   nada. **Estado vazio é estado, não comparação com um lado faltando**; quem
   decide isso é o template, e é por isso que o CSS aqui não tem nada a dizer
   sobre ausência. */
.vr7-comparacao {
  display: grid;
  /* `6rem` cabe o rótulo mais longo do vocabulário hoje ("Documento", na etapa
     de sócios). Largura fixa e não `auto`: `auto` alinharia cada linha por si e
     o registro voltaria a ter rótulos em posições diferentes — que é metade do
     que a tabela dava de bom e a primeira versão do registro perdeu. */
  grid-template-columns: 6rem 1fr;
  gap: var(--esp-2);
  align-items: center;
}
.vr7-comparacao__rotulo {
  color: var(--cor-texto-2);
  font-size: var(--texto-sm);
}
.vr7-comparacao__controle {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: var(--esp-2);
  /* O apoio pertence a ESTE controle, não ao de baixo: dentro do par, o menor
     espaço da escala; entre pares, o espaçamento do registro, que é maior.
     Proximidade é o que agrupa fora da tabela — na tabela era a coluna. */
  row-gap: var(--esp-1);
}
.vr7-comparacao__apoio {
  flex-basis: 100%;
  color: var(--cor-texto-2);
  font-size: var(--texto-sm);
}

/* ── A lista de trabalho: TABELA nas três faixas, com menos colunas ───────

   ⚠️ **Esta é a segunda decisão do dono que a MEDIÇÃO derrubou** (2026-08-13).
   A spec dizia: *"lista pura continua tabela no computador e vira cartão no
   telefone"*. A maquete mediu quatro modelos em 390px e o cartão foi o pior:

   | modelo                    | registros por tela |
   | ------------------------- | ------------------ |
   | tabela com rolagem        | ~4, com coluna cortada |
   | **registro empilhado**    | **~2**             |
   | lista de duas linhas      | ~5, sem coluna para varrer |
   | **tabela por prioridade** | **~5 fechados, ~10 sem apoio** |

   A tabela **não vira outra coisa**: ela mostra menos colunas. O que se varre —
   identificador, valor, estado — fica sempre visível e **alinhado entre as
   linhas**, que é a única coisa que a tabela fazia bem e o cartão jogava fora.

   **A prioridade não é declarada: ela sai do PAPEL** (`app/papeis_de_coluna.py`).
   `secundario` e `detalhe` descem; o resto fica. A tela continua declarando o
   que cada coluna É, nunca como ela se arruma — se precisasse declarar
   prioridade, o vocabulário teria dois lugares dizendo a mesma coisa, e é assim
   que eles divergem.

   O que sai de cena vira **linha de apoio** no resumo e **expansão** na célula
   do identificador — `<details>` nativo, sem script, sem sair da lista, sem
   segundo DOM. */
.vr7-lista td[data-papel="secundario"],
.vr7-lista th[data-papel="secundario"],
.vr7-lista td[data-papel="detalhe"],
.vr7-lista th[data-papel="detalhe"],
/* `expansao` sai de cena aqui como os outros dois — a diferença dela é logo
   abaixo, na faixa larga, onde ela é a ÚNICA que não volta. */
.vr7-lista td[data-papel="expansao"],
.vr7-lista th[data-papel="expansao"] {
  display: none;
}
/* ⚠️ **Na faixa estreita a lista tem largura FIXA, e isso é uma garantia, não um
   ajuste** (075, 2026-08-14).

   Tabela de layout automático nunca fica menor que a soma dos `min-content` das
   colunas — então "cabe em 390px" passa a depender do **conteúdo**, e conteúdo
   muda: um selo mais longo, um nome de fornecedor maior, e a tabela volta a
   passar da janela. Foi o que aconteceu duas vezes seguidas nesta branch, em
   telas diferentes, com consertos pontuais entre elas.

   `table-layout: fixed` com `width: 100%` troca a promessa: as colunas dividem o
   espaço **que existe**, e o conteúdo se acomoda dentro. A tabela deixa de poder
   transbordar por construção, e não por sorte de medição.

   **No computador ela volta a ser automática** (`64rem`, mais abaixo): lá sobra
   largura, e o dimensionamento por conteúdo é melhor — é ele que mantém a
   densidade que o SC-005 exige. */
/* ══ NA FAIXA ESTREITA A LISTA É UMA GRADE, NÃO UMA TABELA ═══════════════
   Decisão do dono, 2026-08-14, com as três saídas na frente
   (`specs/075-design-system-tabela/maquetes/linha-aberta-tres-saidas.html`).

   ⚠️ **Isto reescreve a frase "a tabela não vira outra coisa" — e entrega o que
   ela queria.** Aquela decisão (2026-08-13) escolheu a tabela por prioridade
   contra o cartão por um motivo só: *"o que se varre fica alinhado ENTRE AS
   LINHAS, que é a única coisa que a tabela fazia bem e o cartão jogava fora"*.

   A tabela HTML cumpre isso nas linhas fechadas e **quebra na aberta**: ao virar
   `display: grid` para empilhar, a `<tr>` sai do contexto de tabela e o
   navegador a embrulha numa célula anônima de **uma coluna**. Medido na carteira
   em 390px: tabela 358px, linha fechada 358px, **linha aberta 230px** — e dentro
   dela "Imobiliária/Central", "R$/2.400,00", "Em/aberto" quebravam no meio.
   Testados e descartados: `width: 100%`, `display: contents` na linha,
   `display: block`. Atravessar coluna só com `colspan`, que é HTML — e que
   obrigaria a **duplicar o dado**, o que esta spec recusa desde o começo.

   A grade entrega as duas coisas: colunas que se correspondem entre as linhas
   **e** a linha aberta atravessando de ponta a ponta, sem segundo DOM.

   ## O arranjo é por PAPEL, não por ordem no HTML

   Cada célula é posta na coluna do seu papel (`grid-column`), e não onde calhou
   de estar no `<tr>`. É o mesmo princípio que a spec já declara — *a tela nomeia
   o que a coluna é; o design system decide como isso se arruma* — e resolve de
   graça as listas cujo identificador não é a primeira célula (`fila_saida`,
   `incidentes`).

   Colunas sem nenhuma célula colapsam para zero: a mesma grade de quatro serve a
   lista de duas e a de quatro. */
.vr7-lista {
  display: grid;
  /* identificador · valor · estado · ação. O `1fr` do identificador é o que
     sobra depois das três; `minmax(0, …)` porque item de grade nasce com
     `min-width: auto` e um texto longo empurraria a grade inteira. */
  grid-template-columns: minmax(5rem, 1fr) repeat(3, minmax(0, auto));
  column-gap: var(--esp-3);
  width: 100%;
}
/* `display: contents` faz o `<thead>` e o `<tbody>` sumirem como caixa. A
   semântica de tabela **permanece** para leitor de tela (Chromium/Firefox/Safari
   preservam os papéis implícitos desde 2022); a régua de acessibilidade roda em
   toda tela registrada e é ela quem vigia isso. */
.vr7-lista > thead,
.vr7-lista > tbody,
.vr7-lista > tfoot {
  display: contents;
}
/* ⚠️ **As três peças que só o extrato tinha** (2026-08-16, achadas pela régua de
   rolagem em `/loja/minha-conta` e `/lojas/<id>/saldo-interno`).

   As 18 primeiras listas eram `thead` + `tbody` e nada mais, então a grade nunca
   precisou saber o que fazer com legenda, rodapé e cabeçalho de grupo. O extrato
   tem os três — é um razão, não um cadastro: a legenda explica o recorte, o
   cabeçalho de grupo abre cada dia, e o rodapé fecha o total previsto.

   Sem estas regras eles viram **itens soltos da grade**, cada um caindo numa
   coluna qualquer: a legenda espremida em `minmax(5rem, 1fr)`, o dia do grupo na
   coluna do valor. Todos atravessam de ponta a ponta — que é o que já faziam com
   `colspan` na tabela de verdade. A grade **não lê `colspan`**, e é por isso que
   a mesma intenção precisa ser dita duas vezes, em duas linguagens. */
.vr7-lista > caption,
.vr7-lista > tbody > tr.vr7-tabela__grupo > th {
  grid-column: 1 / -1;
}
/* ⚠️ **A LINHA é uma grade própria, e é isso que a mantém uma linha.**
   O `<tr>` também era `display: contents` — as células iam direto para a grade
   da tabela. Funcionava enquanto todas cabiam numa faixa, e **desmontava na
   linha aberta**: a célula revelada atravessa as quatro colunas
   (`grid-column: 1 / -1`), e o empacotamento esparso do CSS nunca volta atrás.
   Bastava uma célula escondida vir **antes** do identificador no HTML para o
   cursor já ter passado da coluna 1 — o identificador era jogado para a linha
   seguinte e o resto descia atrás dele.

   O sintoma, achado pelo dono no cadastro de bancos: o nome do banco aparecendo
   **depois** do ISPB, e o cabeçalho partido em duas alturas. Não era daquela
   tela — eram **10** (`bancos`, os três extratos, `fila_saida`, `naturezas`,
   as duas de notificação, `conferencia_bancaria`, `repasses/ficha`), todas as
   que declaram uma coluna secundária antes do identificador.

   `subgrid` é o que resolve sem tocar em template nenhum: a linha herda as
   **mesmas trilhas** da tabela — então as colunas continuam alinhadas de uma
   linha para a outra, que é a razão de a lista ser grade — e o empacotamento
   passa a ser assunto interno dela. Aí `dense` fica seguro: ele preenche o vão
   que a célula transversal deixou para trás **dentro da própria linha**, sem
   poder puxar célula da linha de baixo. Com `contents` isto era impossível:
   `dense` na tabela inteira faria a ação de uma linha subir para o vão da
   anterior.

   Alternativa descartada: reordenar as células no HTML para o identificador vir
   primeiro. Conserta o empacotamento e **estraga o computador** — em extrato,
   a data vem antes da descrição porque é assim que se lê um extrato. */
.vr7-lista > thead > tr,
.vr7-lista > tbody > tr,
.vr7-lista > tfoot > tr {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 1 / -1;
  grid-auto-flow: row dense;
}
/* ⚠️ **O identificador é o único com posto FIXO — linha 1, coluna 1.** A coluna
   é o arranjo; a **linha** é o que garante que ele venha primeiro mesmo quando
   vem por último no HTML.

   Sem `grid-row`, o `dense` não salva a linha aberta: ali as células reveladas
   atravessam a grade **antes** de o identificador ser colocado (a colocação
   segue a ordem do HTML), e ele só achava vaga na coluna 1 de uma linha ainda
   livre — a terceira. O nome do banco saía debaixo do ISPB.

   Com o posto fixo ele é colocado na **primeira etapa** do algoritmo, antes de
   qualquer célula transversal existir; as reveladas então se acomodam nas
   linhas 2, 3, 4. As outras três NÃO ganham posto: elas precisam mesmo descer
   quando a linha abre. */
.vr7-lista > thead > tr > [data-papel="identificador"],
.vr7-lista > tbody > tr > [data-papel="identificador"],
.vr7-lista > tfoot > tr > [data-papel="identificador"] {
  grid-column: 1;
  grid-row: 1;
}
.vr7-lista > thead > tr > [data-papel="valor"],
.vr7-lista > tbody > tr > [data-papel="valor"],
.vr7-lista > tfoot > tr > [data-papel="valor"] {
  grid-column: 2;
}
.vr7-lista > thead > tr > [data-papel="estado"],
.vr7-lista > tbody > tr > [data-papel="estado"],
.vr7-lista > tfoot > tr > [data-papel="estado"] {
  grid-column: 3;
}
.vr7-lista > thead > tr > [data-papel="acao"],
.vr7-lista > tbody > tr > [data-papel="acao"],
.vr7-lista > tfoot > tr > [data-papel="acao"] {
  grid-column: 4;
}
/* ⚠️ **Não há regra de borda aqui, e a ausência é a correção.** Chegou a haver
   uma — "a linha deixou de ser caixa, então a borda mora nas células" —, escrita
   sem conferir que `.vr7-tabela th, td` **já** traz `border-bottom` desde a 071.
   Ela era redundante na faixa estreita e, pior, veio acompanhada de um
   `border-bottom: 0` no computador para "desfazê-la": esse zero apagou o
   separador **original** de toda lista em 1440.

   O sintoma não foi erro nenhum — foi espaçamento, e quem acusou foi a
   comparação de imagem. Terceira vez nesta branch que uma regra de faixa
   estreita vaza para o computador. */
/* ⚠️ **`min-width: 0` em toda célula, e sem isto o texto VAZA por cima da
   coluna vizinha.** Item de grade nasce com `min-width: auto`, que é o
   min-content dele — ou seja, ele se recusa a ficar menor que a palavra mais
   longa e transborda em silêncio. Medido: o cabeçalho saía "VencimentoValor"
   colado, e a data "15/03/2027" invadia a coluna do dinheiro.

   Com `0`, a célula cede e o texto **quebra**, que é o comportamento que a
   faixa estreita pede. */
.vr7-lista > thead > tr > th,
.vr7-lista > tbody > tr > td {
  min-width: 0;
  /* ⚠️ **O recuo lateral sai, e é ele que faz as quatro colunas caberem.**
     `.vr7-tabela th/td` traz `padding: --esp-2 --esp-3`; numa grade o recuo
     horizontal é redundante — quem separa as colunas é o `column-gap`. Medido
     em 390px: com ele, as quatro colunas pediam **387px** de 358 disponíveis, e
     o cabeçalho saía "VencimentoValor" colado, com a data invadindo a coluna do
     dinheiro. Sem ele sobram 96px, que é o que falta.

     O recuo **vertical fica**: é ele que dá altura de toque à linha. */
  padding-inline: 0;
}

/* ⚠️ **E a célula CEDE** — a outra metade do mesmo problema (achado pelo dono
   olhando o retrato).

   Uma tabela de layout automático nunca fica menor que a soma dos **min-content**
   das colunas. `.vr7-estado` é `white-space: nowrap` por decisão (o selo não
   deve partir no meio), então o min-content dele é o selo inteiro: "Enviado ao
   banco" pedia **147px** dos 358 úteis em 390px. Com quatro colunas visíveis, a
   conta dava 418px — e a de **Ações** era a cortada, que é a única com que se
   age na fila.

   O `overflow-x: clip` do `.vr7-main` escondia o estrago: a página não rolava, o
   canto era cortado em silêncio. É por isso que a régua passou a medir a borda.

   Aqui — e só dentro de `.vr7-lista` — o selo pode quebrar em duas linhas. Ele
   continua `nowrap` em toda outra superfície: ficha, painel, formulário. A
   alternativa seria esconder mais uma coluna no telefone, e a situação é
   justamente o que se varre. */
.vr7-lista td .vr7-estado {
  white-space: normal;
}
/* ⚠️ **As colunas que NÃO são o identificador têm largura declarada**, e é isso
   que faz o `table-layout: fixed` acima produzir uma tabela legível em vez de
   uma tabela que apenas cabe.

   Sem elas, o layout fixo divide o espaço **em partes iguais** — quatro colunas
   de ~89px em 390px. Medido no retrato: o selo "Enviado ao banco" virava um oval
   de três linhas e o apoio quebrava no meio das palavras ("reembols/o",
   "insuficien/te"). Cabia, e estava feio o bastante para não servir.

   Aqui o dinheiro, o estado e a ação recebem o que pedem, e **o identificador
   fica com o resto** — que é a ordem de importância que a declaração de papéis
   já expressa. Não é ajuste por tela: é o mesmo recorte para toda lista.

   ⚠️ **`overflow-wrap: anywhere` foi REMOVIDO daqui.** Ele existia como segunda
   rede contra um identificador sem espaço, e com o layout fixo ele passou a agir
   onde não devia: partia palavra comum no meio. A tabela não transborda mais por
   construção, então a rede perdeu a função e ficou só o efeito colateral.

   ⚠️ **E a premissa tinha um buraco: TOKEN DE MÁQUINA** (076, ao integrar a
   `main` — 2026-08-19). O console de eventos da auditoria mostra chaves como
   `webhook:inter:203.0.113.11:assinatura_invalida` — 403px de conteúdo contra
   358 disponíveis, medido no contêiner. Sem espaço não há onde quebrar, e a
   tabela voltou a rolar de lado.

   A rede volta **só onde a prosa não vive**: `<code>`, logo abaixo. Devolvê-la à
   célula inteira traria de volta o efeito colateral que a removeu daqui. */
/* Token de máquina quebra; palavra não. `<code>` marca exatamente a fronteira —
   chave de agregação, payload, identificador de provedor —, e nada dentro dele é
   texto que alguém leia por sílaba. */
.vr7-lista code,
.vr7-tabela code {
  overflow-wrap: anywhere;
}
/* ⚠️ **Não há largura declarada aqui, e a ausência é a decisão.** Houve —
   `6rem`/`6rem`/`3rem` — enquanto a lista era `table-layout: fixed`, para
   impedir a divisão em quatro partes iguais. Com a grade, cada coluna `auto` se
   dimensiona pelo conteúdo e o identificador fica com o `1fr` restante, que é
   exatamente o que se queria.

   Mantê-las seria pior que inútil: como item de grade, `width` **comprime** a
   coluna. Medido: o identificador caía para 50px dos 358 e o vencimento
   quebrava em duas linhas. */
.vr7-lista__resumo > summary {
  /* `list-style: none` + o marcador do WebKit: o triângulo nativo brigaria com
     o rótulo textual, que diz mais ("ver tudo" / "ocultar"). */
  list-style: none;
  cursor: pointer;
  min-height: var(--alvo-toque);
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.vr7-lista__resumo > summary::-webkit-details-marker {
  display: none;
}
.vr7-lista__resumo > summary::after {
  content: "ver tudo";
  color: var(--cor-texto-2);
  font-size: var(--texto-xs);
  text-decoration: underline;
}
.vr7-lista__resumo[open] > summary::after {
  content: "ocultar";
}
/* ⚠️ **A meta do identificador NÃO é o apoio** (decisão do dono, 2026-08-13),
   e a diferença é o que decide se ela some no computador.

   `.vr7-lista__apoio` carrega o que SAIU DE CENA no telefone — ISPB, data de
   criação, contato — e por isso ele desaparece em 64rem, onde as colunas voltam
   e diriam a mesma coisa duas vezes.

   Isto aqui é outra coisa: um fato sobre o registro que **não tem coluna
   nenhuma** para voltar. A contagem de anexos do sócio (FR-012) é o caso: ela
   responde de longe *quem ainda não mandou documento?*, e some no computador só
   porque foi escrita com a classe errada. Fica nas três faixas. */
.vr7-registro__meta {
  color: var(--cor-texto-2);
  font-size: var(--texto-sm);
  font-weight: 400;
}
/* ⚠️ **O apoio SOME quando a linha abre** — achado do dono olhando o cadastro de
   bancos, 2026-08-15: *"o nome e o ISPB ficaram duplicados na linha aberta"*.

   A regra já estava escrita logo acima, e eu a apliquei pela metade: o apoio
   *"desaparece em 64rem, onde as colunas voltam e diriam a mesma coisa duas
   vezes"*. A linha aberta é o **outro** caso em que as colunas voltam — e ali o
   apoio continuava dizendo "ISPB 001" com a coluna *ISPB* revelada logo abaixo.

   Não é regra nova: é a mesma, aplicada onde faltava. O critério é um só — o
   apoio existe enquanto a coluna está fora de cena, e nem um instante além. */
.vr7-lista tr:has(.vr7-lista__resumo[open]) .vr7-lista__apoio {
  display: none;
}
.vr7-lista__apoio {
  color: var(--cor-texto-2);
  font-size: var(--texto-sm);
}
/* ⚠️ **A expansão revela as PRÓPRIAS células, não uma cópia delas.**

   A primeira versão punha um `<dl>` dentro do resumo repetindo os valores que
   as colunas escondidas já continham. Funcionava — e era **duplicação de DOM**,
   exatamente o que este design system recusou quando decidiu não emitir tabela
   e cartões em paralelo. Dois lugares com o mesmo valor divergem no dia em que
   alguém edita um.

   `:has()` resolve sem script: quando o `<details>` daquela linha está aberto,
   as células de baixa prioridade **daquela linha** voltam a aparecer, empilhadas
   com o rótulo que já viaja nelas. Um valor, um lugar.

   Suporte: Chromium 105+, Firefox 121+, Safari 15.4+. Onde não houver, a linha
   simplesmente não expande — o dado continua alcançável no computador, e o
   `<details>` continua abrindo (só não revela nada). Degradação silenciosa é
   aceitável **aqui** porque nada de exclusivo mora atrás dela: as colunas
   existem, só não cabem. */
/* ⚠️ **A LINHA ABERTA DEIXA DE SER LINHA DE TABELA** — e isso é o conserto de
   um defeito que só a imagem pegou (2026-08-13).

   A primeira versão punha `display: grid` nas **células** escondidas. Não
   funciona: uma `<td>` em grid continua sendo célula da linha, então as
   reveladas ficam **lado a lado** com as outras, espremem tudo e empurram a
   página na horizontal — a lista de bancos aberta transbordava, com o botão
   "Buscar" cortado. Nenhum teste acusou; o retrato acusou.

   Aqui é a **linha** que muda de papel: aberta, ela vira grade de uma coluna, e
   as células empilham. As demais linhas continuam `table-row` — misturar os dois
   dentro do mesmo `<tbody>` é legal, o navegador monta caixas anônimas.

   Continua sem duplicar valor: o que aparece é a **própria** célula. */
.vr7-lista tr:has(.vr7-lista__resumo[open]) {
  /* ⚠️ **A linha aberta NÃO vira grade própria** — ela continua
     `display: contents`, e são as **células reveladas** que atravessam a grade
     da tabela (`grid-column: 1 / -1`, logo abaixo).

     Era aqui que morava o defeito: `display: grid` na `<tr>` a tirava do
     contexto de tabela e o navegador a embrulhava numa célula anônima de uma
     coluna — 230 de 358px. Com a lista já sendo grade, não há o que embrulhar.

     O que sobra desta regra é só o espaçamento vertical do bloco aberto. */
  row-gap: var(--esp-1);
}
/* **Toda** célula revelada ganha rótulo, e não só as de baixa prioridade.

   ⚠️ Antes, só `secundario` e `detalhe` recebiam o `::before`. O resultado, na
   linha aberta, era um empilhado meio rotulado: "Fornecedor: Imobiliária
   Central" e "Meio: Chave PIX" ao lado de um "R$ 2.400,00" e um "Em aberto"
   soltos, sem dizer o que são. Na linha FECHADA eles se explicam pela coluna em
   que estão; aberta, a coluna não existe mais — e o rótulo é a única coisa que
   os nomeia.

   O identificador fica de fora de propósito: ele é o resumo da linha, o que
   nomeia o registro. Rotulá-lo seria escrever "Nº" em cima do próprio número. */
.vr7-lista tr:has(.vr7-lista__resumo[open]) > td:not([data-papel="identificador"]) {
  /* ⚠️ **`1 / -1` é a peça que resolve o problema todo.** A célula revelada
     atravessa as quatro colunas da grade — 358 de 358px —, e é isso que faz o
     rótulo e o valor caberem lado a lado em vez de quebrarem no meio.
     Não há duplicação: o que atravessa é a **própria** célula. */
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 6rem minmax(0, 1fr);
  gap: var(--esp-2);
  align-items: baseline;
}
.vr7-lista tr:has(.vr7-lista__resumo[open]) > td:not([data-papel="identificador"])::before {
  content: attr(data-rotulo);
  color: var(--cor-texto-2);
  font-size: var(--texto-sm);
}
/* Coluna de rótulo vazio existe (a de ações, em algumas telas, tem cabeçalho em
   branco). Reservar 6rem para nada seria um recuo que ninguém explica. */
.vr7-lista tr:has(.vr7-lista__resumo[open]) > td[data-rotulo=""] {
  grid-template-columns: 1fr;
}
.vr7-lista tr:has(.vr7-lista__resumo[open]) > td[data-rotulo=""]::before {
  content: none;
}

/* ═══ O COMPUTADOR: as colunas voltam a se corresponder ═══════════════════ */
@media (min-width: 64rem) {
  /* ⚠️ **A lista volta a ser TABELA de verdade.** A grade é o arranjo da faixa
     estreita; aqui sobra largura, e o dimensionamento por conteúdo do layout
     automático é o que mantém a densidade que o SC-005 mediu — altura de linha
     idêntica à de antes da spec.

     Desfazer é obrigatório e tem de ser **completo**: `display`, o template de
     colunas, o `display: contents` das três camadas e a colocação por papel.
     Um esquecido aqui deixa a tela do computador num arranjo híbrido, e o
     sintoma é discreto — foi assim que a versão anterior desta seção perdeu o
     `<thead>` em 1440 sem nenhum teste acusar. */
  .vr7-lista {
    display: table;
    grid-template-columns: none;
    column-gap: normal;
  }
  .vr7-lista > thead {
    display: table-header-group;
  }
  .vr7-lista > tbody {
    display: table-row-group;
  }
  /* ⚠️ **O rodapé e o cabeçalho de grupo também se desfazem — e esquecê-los
     custou uma geração inteira** (2026-08-16). O aviso acima diz que o desfazer
     tem de ser **completo**, e ele foi escrito por causa do `<thead>` perdido em
     1440. Ao ensinar a grade a lidar com legenda, rodapé e grupo — que só o
     extrato tem —, os três entraram no arranjo estreito e **nenhum** saiu aqui.

     O sintoma foi exatamente o previsto, e discreto: em 1440 o *"Total previsto
     a sair"* quebrou em três linhas espremidas, com o valor ao lado, em vez de
     atravessar até a coluna do número. Quem pegou foi a **imagem**; nenhuma
     régua reprova um rodapé feio.

     A legenda não precisa de `display` (ela nunca foi `contents`), só do
     `grid-column`. O cabeçalho de grupo volta a atravessar por `colspan`, que é
     o que ele sempre teve. */
  .vr7-lista > tfoot {
    display: table-footer-group;
  }
  .vr7-lista > thead > tr,
  .vr7-lista > tbody > tr,
  .vr7-lista > tfoot > tr {
    display: table-row;
  }
  .vr7-lista > caption,
  .vr7-lista > tbody > tr.vr7-tabela__grupo > th {
    grid-column: auto;
  }
  .vr7-lista > thead > tr > th,
  .vr7-lista > tbody > tr > td,
  .vr7-lista > tfoot > tr > th,
  .vr7-lista > tfoot > tr > td {
    grid-column: auto;
    /* ⚠️ **Estes dois desfazeres são os que faltavam, e o sintoma era mudo.**
       `min-width: 0` e `padding-inline: 0` existem para a grade — o primeiro
       deixa a célula ceder, o segundo devolve os 96px que o `column-gap` já
       supre. Sem reverter, eles vazavam para o computador e mudavam o
       espaçamento de **toda** lista em 1440, onde a spec promete — e o SC-005
       mediu — que nada muda. Foi a comparação de imagem que acusou. */
    min-width: auto;
    padding-inline: var(--esp-3);
  }
  .vr7-lista tr:has(.vr7-lista__resumo[open]) > td:not([data-papel="identificador"]) {
    display: table-cell;
    grid-column: auto;
  }
  /* ⚠️ **E o rótulo da célula revelada SOME**, que é o desfazer que faltava.
     Na faixa estreita a coluna não existe, e o `::before` é a única coisa que
     nomeia o valor; aqui a coluna voltou e o cabeçalho já o nomeia. Sem isto a
     linha aberta saía com tudo em dobro no computador — "Devolvido R$ 120,00"
     dentro da coluna *Devolvido*.

     O sintoma foi 8,6% dos pixels em `estornos_operacao` e 10,3% em
     `estornos_lojista_povoado`, e só apareceu quando a regra dos dois estados
     passou a abrir uma linha no retrato: antes, nenhuma imagem tinha linha
     aberta para revelar o defeito. */
  .vr7-lista tr:has(.vr7-lista__resumo[open]) > td:not([data-papel="identificador"])::before {
    content: none;
  }
  /* A grade contínua (decisão do dono, 2026-08-11): identificador à esquerda e
     os campos em colunas que se alinham entre TODAS as linhas. É o que a tabela
     dava de bom — varrer uma coluna — sem a tabela e sem a rolagem horizontal. */
  /* As colunas de baixa prioridade VOLTAM — e com elas some tudo que existia
     para compensar a ausência delas. Não fica um "ver tudo" inútil ao lado de
     colunas que já mostram tudo.

     ⚠️ Aqui não há `display: block` a desfazer: a tabela **nunca deixou de ser
     tabela**. A versão anterior desmontava e remontava os cinco papéis
     (`table`, `table-header-group`, `table-row-group`, `table-row`,
     `table-cell`) — e eu esqueci quatro deles, o que deixou a lista **sem
     cabeçalho** em 1440 sem nenhum teste acusar. Não desmontar é mais simples
     e não tem esse erro possível. */
  /* A largura volta a ser automática: aqui sobra espaço, e dimensionar por
     conteúdo é o que mantém a densidade da lista (SC-005). A trava de
     `table-layout: fixed` é da faixa estreita, onde ela impede a tabela de
     transbordar por construção. */
  .vr7-lista {
    table-layout: auto;
  }
  /* ⚠️ **Quem absorve a sobra da linha é DECLARADO** (2026-08-18, pedido do dono
     na listagem de naturezas: *"aumentar o tamanho do campo nome, para que o
     nome fique mais visível"*).

     No layout automático a sobra se reparte pelo conteúdo, e quem tem etiqueta
     larga a toma antes: em naturezas, a coluna "Usos" (dois selos) espremia o
     nome em duas linhas — "Natureza / 001" — com espaço vazio à direita da
     tabela. `width: 100%` numa célula de tabela automática é o pedido "leve toda
     a sobra"; as outras colunas encolhem para o conteúdo delas.

     **Modificador, e não regra da `.vr7-lista`**, por duas razões que andam
     juntas: nem toda lista quer isso — extrato e carteira preferem a densidade
     que o dimensionamento por conteúdo dá (SC-005) —, e ligá-lo para todo mundo
     mudaria as 24 listas da casa a partir de uma tela, que é o "mexer na tela
     dos outros" que a 071 catalogou. O nome é do PAPEL, não da tela: a próxima
     lista que precisar disso pede a mesma classe.

     Só existe nesta faixa: na estreita, a grade já dá `minmax(5rem, 1fr)` ao
     identificador — ele já leva a sobra lá por construção. */
  .vr7-lista--nome-largo > thead > tr > [data-papel="identificador"],
  .vr7-lista--nome-largo > tbody > tr > [data-papel="identificador"] {
    width: 100%;
  }
  /* ⚠️ **O par do `width: 100%` acima, e ele não é opcional** (2026-08-25, dono
     na listagem de clientes: *"as informações estão ultrapassando a coluna na
     tela de notebook"*).

     "As outras colunas encolhem para o conteúdo delas" quer dizer **min-content**
     — e min-content quebra onde o texto PODE quebrar. Um documento formatado
     tem hifens, então o navegador parte "700.000.000-01" em "700.000.000-" e
     "01" e considera a coluna satisfeita. Medido: sem esta regra, tirar a coluna
     "Contato" **e** ligar o `--nome-largo` desentortava o nome e continuava
     quebrando o documento — o modificador tinha trocado um sintoma pelo outro.

     `valor` é "o número que a pessoa procura" (CPF/CNPJ, dinheiro, documento):
     número partido no meio deixa de ser procurável. A regra vive **sob o
     modificador**, não na `.vr7-lista` crua, porque é ele quem cria o aperto —
     e assim ela não alcança as 24 listas da casa a partir desta tela. Hoje só
     `clientes` combina as duas coisas: a de naturezas usa o modificador e não
     tem coluna `valor`. */
  .vr7-lista--nome-largo > tbody > tr > [data-papel="valor"],
  .vr7-lista--nome-largo > thead > tr > [data-papel="valor"] {
    white-space: nowrap;
  }
  /* ⚠️ **As duas concessões da faixa estreita são DESFEITAS aqui**, e não é
     zelo: sem isto elas vazam para o computador e mudam a distribuição das
     colunas — o `min-content` de uma célula muda quando o conteúdo dela pode
     quebrar, e o layout automático se rearranja em volta disso.

     Medido: com elas soltas, `estornos_operacao` e `contas_a_pagar_lista`
     acusaram diferença de imagem em **1440**, onde a spec promete — e o SC-005
     mediu — que **nada muda**. Regra de faixa estreita que não é desfeita não é
     regra de faixa estreita. */
  .vr7-lista td .vr7-estado {
    white-space: nowrap;
  }
  /* ⚠️ **`expansao` está AUSENTE desta lista de propósito, e a ausência é a
     regra inteira** (2026-08-25). `secundario` e `detalhe` descem no telefone e
     **voltam** aqui; `expansao` não volta — ela é o papel do dado que só existe
     quando alguém pede para ver.

     Nasceu da moldura de duas colunas da 071: ela estreita a lista (o cartão da
     coluna principal é bem menor que a tela), e seis colunas deixaram de caber.
     Na listagem de clientes, "Contato" ocupando coluna fazia o nome quebrar em
     duas linhas em 1440 — decisão do dono: *"contato só no celular"*.

     ⚠️ Quem acrescentar `expansao` aqui **desfaz o papel**: ele vira um
     `detalhe` com outro nome, e a tela que o declarou volta a apertar. Se a
     coluna precisa aparecer no computador, o papel dela é `detalhe`. */
  .vr7-lista td[data-papel="secundario"],
  .vr7-lista th[data-papel="secundario"],
  .vr7-lista td[data-papel="detalhe"],
  .vr7-lista th[data-papel="detalhe"] {
    display: table-cell;
  }
  /* ⚠️ **O `<details>` SAI INTEIRO, e não só o que ele mostra.** Enquanto o
     identificador morava dentro do `<summary>`, esconder só o apoio e o "ver
     tudo" bastava — o resto do elemento ainda continha o nome da linha, que
     precisa aparecer. Com o nome fora dele (o link aninhado era
     `nested-interactive`), o que sobra aqui é um elemento **vazio** que continua
     ocupando altura: as linhas da grade de categoria cresceram ~1 px cada e a
     página inteira desceu, com **2,07% dos pixels** diferentes em 1440.

     Escondê-lo por completo é o certo por outro motivo também: no computador as
     colunas voltam, então não há nada a expandir — o gatilho não é só invisível,
     ele não tem função. */
  .vr7-lista__resumo {
    display: none;
  }
  /* O rótulo volta para o `<thead>`: repeti-lo na célula seria dizer duas vezes
     a mesma coisa, e leitor de tela leria as duas. */
  .vr7-lista .vr7-registro td::before {
    content: none;
  }
  /* No computador `detalhe` PODE ter coluna própria — é o que enxuga as tabelas
     de 9 a 17 colunas (I5 · FR-008). *Pode*, não *deve*. */
  .vr7-lista .vr7-registro > [data-papel="detalhe"] {
    display: table-cell;
  }
}

/* ── A barra de lista ─────────────────────────────────────────────────────
   Ordenação, filtro e seleção em lote moram AQUI, e não no cabeçalho da tabela
   (FR-008a). O motivo é estrutural: quando a lista vira registro, o `<thead>`
   some — e todo controle que morava nele sumiria junto, em silêncio.

   Ela existe nas três faixas, com os mesmos controles (I12 · FR-008c):
   reduzir função por largura é a decisão que a 068 já reprovou. */
.vr7-barra-lista {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--esp-2);
  margin-bottom: var(--esp-3);
}
/* O seletor de ordenação existe **só no telefone**: no computador os `<th>`
   voltam e fazem o trabalho, e oferecer os dois seria dois caminhos para a
   mesma coisa — um deles envelheceria. */
/* ⚠️ **Linha própria, e colunas em vez de `flex-wrap`** (2026-08-18, o dono
   olhando o telefone: *"não está com uma boa usabilidade e visibilidade na
   parte superior"*).

   Com `flex-wrap`, os três controles se acomodavam onde coubesse: em 390px
   sobrava o rótulo + o primeiro seletor numa linha e o segundo seletor + o botão
   na seguinte, com o "Decrescente" pendurado à esquerda de um "Aplicar". Quatro
   linhas de controle antes do primeiro registro, e nenhuma delas com começo e
   fim óbvios.

   A grade dá o que a quebra automática não dá: os dois seletores **sempre**
   dividem a largura em partes iguais, e o bloco **sempre** ocupa uma linha só —
   `width: 100%` porque ele é item do flex da `.vr7-barra-lista`, e sem isso
   voltaria a disputar a linha da busca.

   `minmax(0, 1fr)`, e não `1fr`: o seletor traz "Decrescente" dentro, e `1fr` é
   `minmax(auto, 1fr)` — ele se recusaria a ficar menor que a opção mais longa e
   empurraria o vizinho para fora em 390px.

   ⚠️ **Em 390 o rótulo leva a linha inteira, e isso foi MEDIDO, não estimado.**
   Com ele na mesma linha dos dois seletores sobravam ~114px para cada um, e o
   segundo saía escrito "Crescent" — o texto cortado no meio da palavra, com a
   seta comendo o resto. Cortar o valor de um controle é pior do que gastar uma
   linha com o rótulo dele: a pessoa deixa de saber o que está escolhendo.
   Acima de 48rem os três cabem lado a lado, e aí o rótulo volta para a linha. */
.vr7-barra-lista__ordenar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: var(--esp-2);
  width: 100%;
}
.vr7-barra-lista__ordenar > .rotulo {
  grid-column: 1 / -1;
}
@media (min-width: 48rem) {
  .vr7-barra-lista__ordenar {
    grid-template-columns: auto minmax(0, 1fr) minmax(0, 1fr);
  }
  .vr7-barra-lista__ordenar > .rotulo {
    grid-column: auto;
  }
}
/* O botão só existe sem script (`<noscript>` no macro). Quando existe, ele leva
   a linha inteira: espremido numa quarta coluna, ele encolhe os seletores para
   caber — e a degradação sem script passaria a ser um leiaute pior, não só um
   clique a mais. */
.vr7-barra-lista__ordenar noscript {
  grid-column: 1 / -1;
}
@media (min-width: 64rem) {
  .vr7-barra-lista__ordenar {
    display: none;
  }
}
/* ⚠️ **A POSIÇÃO deste bloco no arquivo é requisito, não arrumação.**
   `.vr7-registro--grade` e `.vr7-registro` têm a MESMA especificidade (uma
   classe), então quem vem depois vence. `.vr7-registro` declara
   `gap: var(--esp-3)`; esta grade quer `--esp-4`. Escrito antes dele, o `gap`
   daqui é silenciosamente descartado — medido em 2026-08-17: as faixas do
   editor de categoria foram de `439px` para `443px`, um retrato congelado
   mudando 8px por causa de ordem de linha. Mantenha o bloco DEPOIS da media
   query de 64rem, que é de onde ele saiu. */
/* ── A grade se mede pelo CARTÃO, não pela janela (076, 2026-08-17) ──────────
   As três colunas moravam em `@media (min-width: 64rem)`, e a pergunta que essa
   regra faz é *"a JANELA é larga?"*. A que ela precisava fazer é *"o CARTÃO é
   largo?"*. Nas duas telas que existiam quando ela nasceu as duas perguntas
   tinham a mesma resposta — a grade ocupava a largura inteira.

   ⚠️ **A etapa de split da análise (076) quebrou a coincidência.** Ela vive na
   moldura de duas colunas, e o cartão dela mede **593px numa janela de 1440**.
   `1fr` é `minmax(auto, 1fr)`, então a faixa **não encolhe** abaixo do próprio
   `min-content`: o navegador resolveu `160px 322px 322px` dentro de 559px e a
   última coluna saiu **260px para fora do cartão**, por cima da coluna de
   decisão.

   ⚠️ **As seis réguas passaram verdes** — contraste, foco, tabulação e
   componente estavam impecáveis do lado de fora do cartão. Quem viu foi o
   retrato. É a fronteira escrita em `e2e/reguas/__init__.py`: régua mede
   qualidade do que está lá; ela não pergunta se está no lugar certo.

   O limiar sai da conta do próprio arranjo — `10rem + 2 × 322px + 2 × gap` ≈
   836px — com folga para os dois lados: o editor de categoria mede **1104px** de
   cartão e mantém as três colunas; a etapa da análise volta a empilhar, que é o
   mesmo arranjo do telefone e já estava provado. */
.vr7-registros {
  container-type: inline-size;
}
/* ── O rótulo encolhe antes de o campo sair da caixa (076, 2026-08-18) ───────
   Segundo transbordo da mesma família, um nível abaixo do primeiro: aqui quem
   não encolhe é o `1fr` de `.vr7-comparacao` (rótulo + controle), e o campo de
   valor saía **32px** para fora do invólucro em 390px.

   ⚠️ **Empilhar rótulo sobre campo estava fora de questão** — o FR-012a da 075
   proíbe, e há teste de navegador cobrando (`test_o_rotulo_e_o_campo_ficam_na
   _mesma_linha`): *"aí o formulário vira uma coluna de rótulos soltos"*. O que
   cede é a **largura do rótulo**, não a linha.

   Os `6rem` do padrão foram medidos para "Documento", o rótulo mais longo do
   vocabulário. Aqui os rótulos são "Taxa" e "FI", e `3.5rem` os acomoda com
   folga: 56 + 8 de intervalo + 218 do editor = 282, dentro dos 290 disponíveis.

   ⚠️ **O limiar é a própria conta, não um número escolhido**: lado a lado, a
   linha precisa de `96 (rótulo) + 8 (intervalo) + 218 (editor) = 322px`. Abaixo
   disso não cabe, e o rótulo cede. `20.125rem` **é** 322px.

   ⚠️ **E ele se compara à CAIXA DE CONTEÚDO, que não é a que se mede na tela.**
   `@container` consulta o content box; `getBoundingClientRect` devolve o border
   box. A primeira versão desta regra usou `21rem` porque eu medi o editor de
   categoria em **358px** — mas o que o `@container` vê ali é **324px** (menos
   `2 × 16` de padding e `2 × 1` de borda). O resultado: a regra pegou uma tela
   que não era para pegar, e o retrato congelado da 075 mudou **2,42% dos
   pixels** em 390px. Quem acusou foi a comparação de imagem.

   Com 322px, os dois lados ficam certos pela mesma conta: a etapa da análise
   expõe **290px** de conteúdo e encolhe; o editor de categoria expõe **324px** e
   fica como estava — porque ali ainda cabe. */
@container (max-width: 20.125rem) {
  .vr7-registro--grade .vr7-comparacao {
    grid-template-columns: 3.5rem 1fr;
  }
}

@container (min-width: 53rem) {
  .vr7-registro--grade {
    /* Duas colunas, escrito à mão e não por token. Havia aqui um
       `var(--registro-colunas, 2)` — um botão de ajuste que NINGUÉM aperta:
       nenhuma tela declara outro número, e o guarda de token órfão o pegou.
       É a mesma lição que a `.vr7-lista` deu nesta branch: vocabulário sem
       consumidor não é vocabulário, é matéria-prima. O dia em que uma tela
       comparar três valores, o token volta COM ela. */
    grid-template-columns: 10rem repeat(2, 1fr);
    align-items: start;
    gap: var(--esp-4);
  }
  /* A expansão atravessa a grade: ela é do REGISTRO inteiro, não de uma
     coluna. Sem isto ela vira uma quarta coluna estreita e o detalhe fica
     ilegível na largura de um campo. */
  .vr7-registro--grade > .vr7-registro__expansao {
    grid-column: 1 / -1;
  }
}

.vr7-barra-lista:empty {
  /* Não aparece vazia (I13 · FR-008d) — barra sem controle é uma faixa de
     espaço que a pessoa aprende a ignorar. `:empty` e não `display:none` numa
     classe à parte: a tela não precisa saber declarar ausência. */
  margin-bottom: 0;
}

