/*
  Skirret — Tema "Pedra Bruta": COMPONENTES do produto
  ===================================================
  Todas as cores vivem em variáveis CSS (custom properties), nunca em
  valores fixos espalhados pelas regras abaixo. Isso é proposital: um
  Tema publicado (`docs/37` §5) sobrescreve tokens e a interface inteira
  se adapta, sem tocar em nenhum componente individual.

  **O bloco `:root` NÃO mora mais aqui** — saiu para `tokens.css` em
  26/08/2026 (Prompt #201, R11 do `docs/59`). Este arquivo traz os
  COMPONENTES DO PRODUTO: as telas autenticadas, com tema, escala de
  fonte e densidade. O site público tem os seus em `landing.css`, e os
  dois consomem os MESMOS tokens.

  A separação é de contexto, não de estilo: o visitante não está
  autenticado, não tem tema nem preferência, e a cadeia de herança de
  quatro níveis do `docs/37` é peso morto para ele.

  Ver `docs/Brand_Guidelines_Skirret.md` para o simbolismo de cada cor.
*/

/* BASE TIPOGRÁFICA: 20px, não 16px (Prompt #147 — decisão de Wagner de
   17/08/2026).

   O zoom de 125% era o tamanho em que Wagner validava todas as telas, e
   passou a ser o padrão. A conta é direta: 16px × 125% = **20px**. Com a
   base em 20px, a escala de 100% renderiza exatamente o que hoje se
   obtém em 125%.

   O QUE NÃO SE FEZ, e o motivo. A saída fácil seria renomear o rótulo —
   fazer o controle exibir "100%" enquanto aplica o fator 125. Isso
   quebraria a semântica de tudo o que depende do número: o valor gravado
   em `Usuario.escala_fonte` deixaria de significar o que diz, e a próxima
   pessoa a ler `--sk-escala-fonte: 100` no DOM concluiria que está no
   tamanho de fábrica. Aqui a escala continua sendo multiplicação sobre a
   base; **só a base cresceu**.

   POR QUE O ESPAÇAMENTO EM px NÃO FOI CONVERTIDO JUNTO. Padding, gap,
   margin e bordas continuam em px de propósito. A referência visual é o
   125% de HOJE, onde esses mesmos px já convivem com texto de 20px — a
   base nova reproduz aquele rendering pixel por pixel. Converter o
   espaçamento para `rem` agora mudaria justamente o que foi validado.
   Bordas de 0,5–1px, então, nunca escalam: uma linha de 1,25px é uma
   linha borrada.

   EMENDA DO PROMPT #177 — a base virou TOKEN. O `20px` fixo saiu daqui e
   virou `--sk-base-fonte`, parametrizável por Tema na MASTER-08. O
   mecanismo já era este desde o #147; o que mudou foi a constante virar
   variável. **O fallback `20px` fica de propósito:** um Tema antigo que
   não traga o token continua renderizando exatamente como hoje, em vez
   de colapsar para o `font-size` do navegador.

   E a composição é de UM eixo só: o Tema dá a base, o usuário multiplica
   por cima dela. Os dois nunca se multiplicam entre si — `docs/37` §8.1
   registra o que foi recusado e por quê. */
html {
  font-size: calc(var(--sk-base-fonte, 20px) * var(--sk-escala-fonte) / 100);
}

* {
  box-sizing: border-box;
}

/* Cabeçalho/rodapé fixos, rolagem só em .sk-main (docs/14, Prompt #60) —
   flexbox em coluna, com só o miolo (.sk-main) rolável, é o único jeito de
   garantir as duas exigências ao mesmo tempo: cabeçalho/rodapé nunca se
   movem (nem "sticky", que só gruda ao rolar — aqui eles não fazem parte
   de nenhuma área rolável pra começo de conversa) E o rodapé permanece no
   fim da viewport mesmo com conteúdo mais curto que a tela (um `position:
   sticky`/`fixed` isolado não resolveria esse segundo caso sem o mesmo
   truque de flex/grid por baixo). Revertido em @media print, abaixo — um
   `.sk-artigo-para-imprimir` mais alto que 100vh não pode ficar cortado
   pelo `overflow: hidden` daqui. */
body {
  background: var(--sk-bg);
  color: var(--sk-fg);
  font-family: var(--sk-font-body);
  font-size: 0.875rem;
  line-height: 1.6;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
}

a {
  color: var(--sk-gold);
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}

h1, h2, h3 {
  font-family: var(--sk-font-display);
  font-weight: 500;
  margin: 0 0 0.5em;
}

/* Componentes nativos do navegador (<select>/<option>) — o navegador nem
   sempre herda o tema da página ao renderizar a lista de opções aberta,
   caindo de volta no padrão do SO (geralmente claro), o que quebra a
   legibilidade sobre o tema escuro. Regra global (não pontual a nenhuma
   tela) e com cor sólida (nunca `rgba` translúcido, que não compõe
   corretamente com o popup nativo) — docs/14, princípio 10. */
select {
  background-color: var(--sk-surface-solid);
  color: var(--sk-fg);
}
select option {
  background-color: var(--sk-surface-solid);
  color: var(--sk-fg);
}

/* ---------- Layout ---------- */

/* Cabeçalho em 2 colunas (docs/14, Prompt #60) — coluna 1 (hambúrguer +
   logomarca) e coluna 2 (a partir da metade do cabeçalho: avisos/alertas
   + "Sair" na extrema direita) via grid 1fr/1fr, nunca flex+space-between
   puro — isso garante que a coluna 2 sempre começa exatamente na metade
   do cabeçalho, mesmo quando a coluna 1 é bem mais estreita que ela. */
.sk-header {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: var(--e4);
  padding: var(--e4) var(--e6);
  border-bottom: 0.5px solid var(--sk-border);
  flex: 0 0 auto;
}

.sk-logo {
  height: 40px;
  width: auto;
  display: block;
}

.sk-logo-link {
  display: block;
  line-height: 0;
}
.sk-logo-link:focus-visible {
  outline: 2px solid var(--sk-gold);
  outline-offset: 2px;
}

.sk-header-esquerda {
  display: flex;
  align-items: center;
  gap: var(--e4);
}

/* Coluna 2 — avisos/alertas (à esquerda desta coluna, crescendo) + "Sair"
   sempre colado na extrema direita do cabeçalho. */
.sk-header-direita {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e4);
}

.sk-header-avisos {
  display: flex;
  flex-direction: column;
  gap: var(--e1);
  flex: 1 1 auto;
  min-width: 0;
}
.sk-header-aviso {
  display: flex;
  align-items: center;
  gap: var(--e2);
  font-size: 0.75rem;
  padding: var(--e2) var(--e3);
  border-radius: var(--sk-radius-sm);
  background: rgba(99, 153, 34, 0.1);
  border: 0.5px solid rgba(99, 153, 34, 0.3);
  color: var(--sk-success-fg);
  opacity: 1;
  /* Restauração RÁPIDA (esta) x esmaecimento LENTO (na classe abaixo):
     quem aponta para a mensagem quer lê-la agora, então voltar à nitidez
     é quase imediato; o desaparecer continua suave para não piscar
     (Prompt #112). */
  transition: opacity 150ms ease;
}
.sk-header-aviso-texto {
  flex: 1 1 auto;
  min-width: 0;
}
.sk-header-aviso--esmaecido {
  opacity: 0.35;
  transition: opacity 1s ease;
}
/* Apontar ou focar restaura a nitidez plena (Prompt #112). O JS também
   remove a classe e congela o cronômetro — estas regras são a defesa em
   profundidade: sem elas, um segundo de transição pendente ainda deixaria
   a mensagem apagada sob o cursor. `:focus-within` cobre a navegação por
   teclado, inclusive quando o foco está no botão de fechar. */
.sk-header-aviso--esmaecido:hover,
.sk-header-aviso--esmaecido:focus-within {
  opacity: 1;
  transition: opacity 150ms ease;
}
/* Renomeadas de ".error"/".warning" (achado do Catálogo Vivo, MASTER-24,
   Prompt #185/#186): a mesma classe bare ".error" também é o seletor de
   erro de validação do abas-form.js (SELETORES_DE_ERRO, docs/14
   Princípio 19) — um aviso de topo com essa classe, se algum dia caísse
   dentro de um `.sk-aba-form-conteudo`, seria lido como erro de
   formulário sem ter nada a ver com validação. Investigado (Prompt #186):
   nenhuma tela real combina as duas coisas hoje (o aviso de cabeçalho
   vive sempre no <header>, fora de qualquer `.sk-aba-form-conteudo`) —
   defeito latente, não em produção. Corrigido pelo lado de MENOR risco:
   o mecanismo de validação (abas-form.js) é código compartilhado,
   testado e com histórico (Prompt #126); o aviso de cabeçalho é decoração
   de uma faixa, e já tinha precedente de nome namespaced no mesmo
   componente (`sk-header-aviso--esmaecido`, algumas linhas acima) — só
   faltava estender o mesmo padrão a estes dois. */
.sk-header-aviso--error {
  background: rgba(192, 57, 43, 0.1);
  border-color: rgba(192, 57, 43, 0.3);
  color: var(--sk-danger-fg);
}
.sk-header-aviso--warning {
  background: rgba(201, 168, 76, 0.12);
  border-color: rgba(201, 168, 76, 0.35);
  color: var(--sk-warning-fg);
}
/* Fechar manual — erro/aviso não esmaecem sozinhos (decisão de
   13/08/2026, Prompt #60), então precisam de uma saída explícita. Mesmo
   glyph "×" do botão de fechar do modal de Help (`sk-help-modal-fechar`). */
.sk-header-aviso-fechar {
  flex: 0 0 auto;
  background: none;
  border: none;
  color: inherit;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  padding: 0 2px;
}
.sk-header-aviso-fechar:hover {
  opacity: 0.7;
}

.sk-header-sair-form {
  flex: 0 0 auto;
}
.sk-header-sair-btn {
  background: none;
  border: none;
  color: var(--sk-fg);
  font-size: 0.8125rem;
  cursor: pointer;
  padding: var(--e1) var(--e2);
}
.sk-header-sair-btn:hover {
  color: var(--sk-gold);
}

/* LINHA DE SUBTÍTULO DE TELA (maquete de 06/09, Prompt #333)

   Um degrau abaixo do `<h1>`, na cor de texto secundário: ela carrega o
   ESTADO da tela (o exercício, a contagem), não a identidade dela. Mesma
   razão do Princípio 43 para rótulo de campo - quem manda na hierarquia é
   o que a informação É, não o quanto ela é útil. */
.sk-page-subtitulo {
  margin: calc(-1 * var(--e2)) 0 var(--e4) 0; /* sk-excecao: aproxima do <h1> — a escala não tem passo negativo */
  color: var(--sk-muted);
  font-size: 0.875rem;
}

/* BOTÃO EXPANDIR (docs/14, Princípio 47, Prompt #333)

   Botão de CROMO: mesma natureza do menu e do seletor de tema, e por isso
   FORA da hierarquia de botões (níveis 1 a 4). Os quatro níveis
   classificam ações sobre o CONTEÚDO da tela; expandir não toca em dado
   nenhum. Encaixá-lo na hierarquia faria uma ação sem efeito sobre o
   registro disputar destaque com salvar e excluir.

   Herda a forma do `.sk-header-sair-btn` vizinho de propósito — os dois
   são cromo do cabeçalho, e um botão de cromo com desenho próprio ao lado
   do outro anunciaria uma diferença de importância que não existe. */
.sk-header-cromo-btn {
  background: none;
  border: none;
  color: var(--sk-fg);
  font-size: 0.8125rem;
  cursor: pointer;
  padding: var(--e1) var(--e2);
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
}
.sk-header-cromo-btn:hover {
  color: var(--sk-gold);
}
.sk-header-cromo-btn:focus-visible {
  outline: 2px solid var(--sk-gold);
  outline-offset: 2px;
}

/* MODO EXPANDIDO — o caminho (b) do Prompt #333, para quando a Fullscreen
   API não existe ou é RECUSADA (iframe sem `allow="fullscreen"`).

   🔴 **O CABEÇALHO NÃO SOME INTEIRO, e essa é a parte que não se pode
   "simplificar".** O Princípio 47 recusa o cromo escondido por CSS como
   MECANISMO principal, e diz por quê: *"sair depende de o usuário achar
   de novo um botão que o próprio efeito acabou de esconder"* — é a
   diferença entre ampliar e prender.

   Aqui o cromo sai, **menos o botão que desfaz**: o cabeçalho encolhe a
   uma faixa com o Expandir/Restaurar, o menu e o rodapé somem, e a área
   útil toma o resto. É o que o `§1` do #333 chama de *"o botão nunca
   some - ele degrada"*.

   Quem esconder o `.sk-header` inteiro aqui reintroduz exatamente o
   defeito que o princípio nomeia, e a tela não dará erro nenhum: o
   usuário é que fica sem saída. */
.sk-expandido .sk-header {
  padding-top: 0;
  padding-bottom: 0;
  min-height: 0;
}
.sk-expandido .sk-header-esquerda,
.sk-expandido .sk-header-avisos,
.sk-expandido .sk-header-sair-form {
  display: none;
}
.sk-expandido .sk-footer {
  display: none;
}
/* 🔴 O `.sk-main` NÃO mexe no `padding-top` aqui, e a razão é um
   ACOPLAMENTO DECLARADO que uma guarda vigia.

   A primeira versão deste bloco apertava o respiro vertical
   (`padding-top: var(--e2)`), e o `test_o_sticky_compensa_o_padding_do_
   scrollport_da_pagina` reprovou com `'e2' != 'e4'` — **com razão, e não
   por formalismo**: o `top` do cabeçalho fixo é
   `calc(-1 * var(--e4))`, calculado a partir DESSE padding (docs/38
   §2.16). Mudar um degrau sem o outro reabre a faixa por onde o conteúdo
   rola acima do cabeçalho — e é falha silenciosa, que foi exatamente o
   que a §2.16 previu ao chamar o acoplamento de "real e deliberado".

   O ganho vertical do modo expandido vem de esconder o rodapé e encolher
   o cabeçalho, que basta. Apertar o respiro custaria reabrir um defeito
   já resolvido em troca de alguns pixels. */

/* Painel do sino de Notificações (docs/60 §6.1, Prompt #196). Nenhum
   primitivo de painel flutuante/popover existia no docs/59 no momento
   desta tarefa (só o menu do Combobox, que é um caso mais estreito) —
   construído LOCAL de propósito, sem virar primitivo novo. */
.sk-notificacoes {
  position: relative;
  flex: 0 0 auto;
}
.sk-notificacoes-sino {
  position: relative;
  background: none;
  border: none;
  color: var(--sk-fg);
  font-size: 1.125rem;
  line-height: 1;
  cursor: pointer;
  padding: var(--e1) var(--e2);
}
.sk-notificacoes-sino:hover {
  color: var(--sk-gold);
}
.sk-notificacoes-contador {
  position: absolute;
  top: -2px;
  right: -2px;
  min-width: 16px;
  height: 16px;
  padding: 0 var(--e1);
  border-radius: 999px;
  background: var(--sk-orange);
  color: var(--sk-fg);
  font-size: 0.625rem;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
}

/* Ancorado no sino (docs/60 §6.1) — nunca gaveta nem tela cheia. O
   `overflow-y` fica no CORPO, não neste invólucro: a lição do #183 foi
   que `overflow` num ancestral mata `position: sticky`/comportamento
   fixo do que está dentro — aqui não há `sticky`, mas o cabeçalho do
   painel precisa continuar fixo enquanto só o corpo rola, e isso exige a
   mesma separação de responsabilidade entre invólucro e área que rola. */
.sk-notificacoes-painel {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 30;
  margin-top: var(--e2);
  width: 340px;
  max-width: calc(100vw - 24px);
  max-height: 420px;
  display: flex;
  flex-direction: column;
  background: var(--sk-surface-solid);
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
}
/* `display: flex` acima bate em especificidade com o `[hidden] {
   display: none }` do UA stylesheet (mesma especificidade, autor vence)
   — sem esta regra, o atributo `hidden` do JS não escondia nada. */
.sk-notificacoes-painel[hidden] {
  display: none;
}
.sk-notificacoes-painel-cabecalho {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e2);
  padding: var(--e3) var(--e4);
  border-bottom: 0.5px solid var(--sk-border);
}
.sk-notificacoes-painel-titulo {
  font-family: var(--sk-font-display);
  font-size: 0.9375rem;
  color: var(--sk-fg);
}
.sk-notificacoes-marcar-todas-form {
  flex: 0 0 auto;
}
.sk-notificacoes-marcar-todas-btn {
  background: none;
  border: none;
  color: var(--sk-gold);
  font-size: 0.6875rem;
  cursor: pointer;
  padding: 2px;
}
.sk-notificacoes-marcar-todas-btn:hover {
  text-decoration: underline;
}

/* A lista rola DENTRO do painel — o `overflow-y` vive só aqui, nunca no
   `.sk-notificacoes-painel` acima, para o cabeçalho ficar fixo. */
.sk-notificacoes-painel-corpo {
  flex: 1 1 auto;
  overflow-y: auto;
}
.sk-notificacoes-grupo-titulo {
  padding: var(--e3) var(--e4) var(--e1);
  font-size: 0.625rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--sk-muted);
}
.sk-notificacao {
  display: flex;
  align-items: flex-start;
  gap: var(--e2);
  padding: var(--e2) var(--e4);
  text-decoration: none;
  color: var(--sk-fg);
}
.sk-notificacao:hover {
  background: color-mix(in srgb, var(--sk-fg) 6%, var(--sk-bg));
}
/* Duas pistas para o mesmo estado (docs/60 §6.1) — cor sozinha falha
   para quem não a distingue. A bolinha é a primeira pista; o negrito do
   título (abaixo) é a segunda. */
.sk-notificacoes-bolinha {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  margin-top: var(--e1);
  border-radius: 50%;
  background: var(--sk-orange);
}
.sk-notificacao-corpo {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.sk-notificacao-titulo {
  font-size: 0.8125rem;
  color: var(--sk-fg);
}
.sk-notificacao--nao-lida .sk-notificacao-titulo {
  font-weight: 700;
}
.sk-notificacao-texto {
  font-size: 0.75rem;
  color: var(--sk-muted);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.sk-notificacao-tempo {
  flex: 0 0 auto;
  font-size: 0.625rem;
  color: var(--sk-muted);
  white-space: nowrap;
}

.sk-notificacoes-vazio {
  padding: var(--e6) var(--e5);
  text-align: center;
}
.sk-notificacoes-vazio-titulo {
  margin: 0 0 var(--e1);
  color: var(--sk-fg);
  font-size: 0.8125rem;
}
.sk-notificacoes-vazio-prazo {
  margin: 0;
  color: var(--sk-muted);
  font-size: 0.6875rem;
}

/* Prévia de alcance da tela de envio (docs/60 §6.2 R9, Prompt #197) — "é
   o único freio entre um clique e a tela de todo mundo", e por isso em
   destaque visual, nunca como texto corrido junto dos outros campos.
   Classe própria, não `.sk-faixa-estado`: aquela é o link de bloqueio de
   Loja (posição fixa entre header/main, semântica de aviso) — reaproveitá-
   la aqui seria o mesmo erro que construir primitivo novo sem precisar. */
.sk-notificacoes-prevista {
  margin: var(--e4) 0;
  padding: var(--e4) var(--e5);
  border: 0.5px solid var(--sk-gold);
  border-radius: var(--sk-radius);
  background: color-mix(in srgb, var(--sk-gold) 10%, var(--sk-bg));
  color: var(--sk-fg);
}
.sk-notificacoes-prevista p {
  margin: var(--e1) 0 0;
  color: var(--sk-muted);
  font-size: 0.8125rem;
}

/* Ícone hambúrguer do Menu Principal (docs/20, seção 7). */
.sk-menu-hamburguer {
  background: none;
  border: none;
  color: var(--sk-fg);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  padding: var(--e1) var(--e2);
}
.sk-menu-hamburguer:hover {
  color: var(--sk-gold);
}

/* Painel do Menu Principal (docs/20) — <dialog> ancorado à esquerda, altura
   cheia, ao invés de centralizado na viewport (esse é o comportamento do
   componente de Diálogo/MessageBox, docs/18 — um uso diferente do mesmo
   elemento nativo <dialog>, não o mesmo componente). */
.sk-menu-principal {
  position: fixed;
  inset: 0 auto 0 0;
  margin: 0;
  height: 100%;
  max-height: 100%;
  width: 380px;
  max-width: 92vw;
  padding: 0;
  border: none;
  border-right: 0.5px solid var(--sk-border-strong);
  border-radius: 0;
  background: var(--sk-bg);
  color: var(--sk-fg);
}
.sk-menu-principal[open] {
  display: flex;
  flex-direction: column;
}
.sk-menu-principal::backdrop {
  background: rgba(0, 0, 0, 0.6);
}
.sk-menu-principal-corpo {
  overflow-y: auto;
  padding: var(--e4) var(--e5) var(--e6);
}
.sk-menu-principal-cabecalho {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--e2);
}
.sk-menu-principal-titulo {
  font-family: var(--sk-font-display);
  font-size: 1.125rem;
}
.sk-menu-principal-logo {
  height: 28px;
  width: auto;
  display: block;
}
.sk-menu-principal-categoria {
  border-top: 0.5px solid var(--sk-border);
  padding: var(--e3) 0;
}
.sk-menu-principal-categoria-titulo {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--sk-muted);
  cursor: pointer;
}
.sk-menu-principal-itens {
  list-style: none;
  margin: var(--e2) 0 0;
  padding: 0;
}
.sk-menu-principal-itens a {
  display: block;
  color: var(--sk-fg);
  font-size: 0.875rem;
}

/* Realce por FUNDO no menu (docs/20 §7.1, Prompt #107) — substitui o
   sublinhado/troca de cor do hover pelo padrão de menu de aplicação: a
   linha inteira acende. Vale igualmente para a categoria-mãe (primeiro
   nível), para os itens no flyout do desktop e para os mesmos itens no
   acordeão do touch — a regra é uma só, os três contextos compartilham
   estes seletores.

   O tom vem de color-mix sobre os tokens do Tema (Prompt #106), nunca de
   cor fixa: em Tema escuro clareia, em Tema claro escurece, sempre no
   mesmo passo relativo. Três intensidades, para que hover, foco e item
   ativo permaneçam distinguíveis entre si:
     hover  8%  ·  foco 8% + outline  ·  ativo 14% + peso 600           */
.sk-menu-principal-categoria-titulo,
.sk-menu-principal-itens a {
  padding: var(--e2) var(--e3);
  border-radius: var(--sk-radius-sm);
  transition: background-color 120ms ease;
}
.sk-menu-principal-categoria-titulo:hover,
.sk-menu-principal-itens a:hover {
  background: color-mix(in srgb, var(--sk-fg) 8%, var(--sk-bg));
  /* Sem sublinhado: o fundo é o afordance agora (a regra global de
     `a:hover` continua valendo para links de texto corrido). */
  text-decoration: none;
}
/* Foco de teclado (docs/14, princípio 4 — paridade entre apontar e
   navegar): mesmo realce de fundo do hover, MAIS o indicador de foco
   próprio. O outline nunca é removido em favor do fundo — quem navega
   por Tab precisa distinguir "onde estou" de "onde o mouse passou". */
.sk-menu-principal-categoria-titulo:focus-visible,
.sk-menu-principal-itens a:focus-visible {
  background: color-mix(in srgb, var(--sk-fg) 8%, var(--sk-bg));
  outline: 2px solid var(--sk-gold);
  outline-offset: -2px;
  text-decoration: none;
}
/* Localização (docs/20 §7.1, Prompt #101) — categoria da tela ativa
   marcada (nunca auto-expandida) e item ativo destacado no flyout. O
   ativo é PERMANENTE, então precisa se distinguir do hover, que é
   transitório: fundo mais intenso + peso, além da cor de destaque. */
.sk-menu-categoria-ativa > .sk-menu-principal-categoria-titulo {
  color: var(--sk-gold);
  border-left: 2px solid var(--sk-gold);
  background: color-mix(in srgb, var(--sk-fg) 14%, var(--sk-bg));
}
.sk-menu-principal-itens a.sk-menu-item-ativo {
  color: var(--sk-gold);
  font-weight: 600;
  background: color-mix(in srgb, var(--sk-fg) 14%, var(--sk-bg));
}
/* Passar o mouse sobre o item já ativo não pode ESCURECER o realce (o
   hover é mais fraco que o ativo) — sobe mais um passo. */
.sk-menu-categoria-ativa > .sk-menu-principal-categoria-titulo:hover,
.sk-menu-principal-itens a.sk-menu-item-ativo:hover {
  background: color-mix(in srgb, var(--sk-fg) 20%, var(--sk-bg));
}

/* Flyout de categorias no desktop (docs/20 §7.1, Prompt #101) — só com
   ponteiro fino e hover de verdade (media query, nunca user-agent): as
   filhas da categoria aberta saem do fluxo e flutuam à direita do
   painel. No touch, nada disso se aplica — o <details> nativo expande
   como acordeão abaixo da categoria. */
@media (hover: hover) and (pointer: fine) {
  /* O flyout precisa escapar da caixa do drawer — sem isso, o
     overflow-y:auto do corpo o cortaria na borda direita. A rolagem
     vertical fica no próprio <dialog> (altura cheia; conteúdo do menu é
     curto no desktop). */
  .sk-menu-principal {
    overflow: visible;
  }
  .sk-menu-principal-corpo {
    overflow: visible;
  }
  .sk-menu-principal-categoria {
    position: relative;
  }
  .sk-menu-principal-categoria > .sk-menu-principal-itens {
    position: absolute;
    left: 100%;
    top: 0;
    min-width: 240px;
    margin: 0;
    /* Padding lateral enxuto (era 16px) desde o Prompt #107: o realce de
       fundo de cada item já tem respiro próprio, e um recuo grande no
       container deixaria a faixa acesa estreita demais dentro do
       flyout. */
    padding: var(--e3) var(--e2);
    background: var(--sk-bg);
    border: 0.5px solid var(--sk-border-strong);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
    z-index: 5;
  }
  /* O "›" da categoria acompanha o padding do título (Prompt #107) —
     sem isso ele encostaria na borda do realce de fundo. */
  .sk-menu-principal-categoria-titulo::after {
    margin-right: 2px;
  }
  .sk-menu-principal-categoria-titulo::after {
    content: "›";
    float: right;
    color: var(--sk-muted);
  }
  /* O marcador nativo do <details> (triângulo) perde o sentido no modo
     flyout — o "›" acima assume o papel de affordance. */
  .sk-menu-principal-categoria-titulo {
    list-style: none;
  }
  .sk-menu-principal-categoria-titulo::-webkit-details-marker {
    display: none;
  }
}

@media (max-width: 640px) {
  .sk-menu-principal {
    width: 100vw;
    max-width: 100vw;
  }

  /* Cabeçalho em 2 colunas (Prompt #60) empilha em telas estreitas — meia
     largura cada não sobra espaço para avisos + "Sair" ao lado de
     hambúrguer + logomarca. */
  .sk-header {
    grid-template-columns: 1fr;
    gap: var(--e2);
  }
}

.sk-nav {
  display: flex;
  gap: 0;
  padding: 0 var(--e6);
  border-bottom: 0.5px solid var(--sk-border);
  overflow-x: auto;
}
.sk-nav a {
  padding: var(--e4) var(--e5);
  font-size: 0.8125rem;
  color: var(--sk-muted);
  white-space: nowrap;
}
.sk-nav a.active {
  color: var(--sk-orange);
  border-bottom: 2px solid var(--sk-orange);
}

.sk-main {
  padding: var(--e6);
  /* Prompt #226 — o padding-TOP some do bloco uniforme e vira --e4: o
     título logo abaixo já ganhou entrelinhamento apertado (1.15) e
     margem menor (--e3, ver .sk-page-header h1) — manter --e6 aqui em
     cima duplicaria o respiro que as outras duas mudanças já deram. Os
     outros três lados continuam --e6, intocados. */
  padding-top: var(--e4);
  /* docs/14, Princípio 27 — 80% da viewport até um teto de 1440px, nunca
     esticando além disso em telas muito largas; nunca formalizado antes
     (960px era valor implícito herdado do início do projeto). */
  width: min(80%, 1440px);
  margin: 0 auto;
  flex: 1 1 auto;
  /* min-height:0 é necessário num item de flex-column para ele aceitar
     encolher abaixo do seu conteúdo e realmente rolar internamente
     (docs/14, Prompt #60) — sem isso, o item cresce para caber todo o
     conteúdo e quem rola acaba sendo a página inteira de novo. */
  min-height: 0;
  overflow-y: auto;
}
.sk-main--narrow {
  max-width: 480px;
}

/* ---------------------------------------------------------------------
   FAIXA PERSISTENTE DE ESTADO (docs/14, Princípio 35 — Prompt #141)

   Terceira categoria de comunicação: o Aviso Urgente interrompe e é
   dispensável; a mensagem comum passa; esta é ESTADO — fica enquanto o
   estado durar.

   `flex: 0 0 auto` e não `position: fixed`: o `body` já é uma coluna flex
   em que só o `.sk-main` rola (Prompt #135), então um irmão entre header
   e main é permanentemente visível PARTICIPANDO do layout, sem flutuar
   por cima nem cobrir a primeira linha da tela.

   Cor de aviso, não de erro: o bloqueio é um estado deliberado — alguém o
   ligou de propósito —, não uma falha. `--sk-warning-fg` sobre a
   superfície de aviso é o par já calibrado por tema desde o #106, então a
   faixa nasce legível nos três temas sem calibragem própria. */
.sk-faixa-estado {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--e3);
  padding: var(--e3) var(--e6);
  background: color-mix(in srgb, var(--sk-warning) 18%, var(--sk-bg));
  border-bottom: 0.5px solid var(--sk-warning);
  color: var(--sk-warning-fg);
  font-size: 0.8125rem;
  text-decoration: none;
}
.sk-faixa-estado:hover {
  background: color-mix(in srgb, var(--sk-warning) 28%, var(--sk-bg));
  text-decoration: none;
}
/* O foco precisa aparecer: a faixa é um link e alguém chega nela por
   teclado antes de chegar ao conteúdo da página. */
.sk-faixa-estado:focus-visible {
  outline: 2px solid var(--sk-warning-fg);
  outline-offset: -2px;
}
.sk-faixa-estado-texto strong {
  font-weight: 700;
}
/* A ação é convite, não botão: a faixa informa e OFERECE — não é
   dispensável nem obrigatória, então nada aqui pode se parecer com algo
   que precise ser resolvido antes de seguir. */
.sk-faixa-estado-acao {
  white-space: nowrap;
  text-decoration: underline;
}

.sk-footer {
  border-top: 0.5px solid var(--sk-border);
  padding: var(--e4) var(--e6);
  display: flex;
  align-items: center;
  gap: var(--e5);
  font-size: 0.6875rem;
  color: var(--sk-gray);
  flex: 0 0 auto;
  flex-wrap: wrap;
}

/* Abas da tela de Tabelas Gerais (docs/19) — mesma linguagem visual de
   `.sk-nav` (ativa = laranja com borda inferior), mas com `flex-wrap` porque
   são 11 abas, não cabendo em uma única linha em telas estreitas. */
.sk-abas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e1);
  border-bottom: 0.5px solid var(--sk-border);
  margin-bottom: var(--e4);
}
.sk-abas a {
  padding: var(--e3) var(--e4);
  font-size: 0.8125rem;
  color: var(--sk-muted);
  white-space: nowrap;
}
.sk-abas a.active {
  color: var(--sk-orange);
  border-bottom: 2px solid var(--sk-orange);
}

/* Ações de linha de tabela (ex: Editar + Excluir lado a lado em Tabelas
   Gerais, docs/19) — `<form>` é um elemento de bloco por padrão, quebrando a
   linha se não for colocado num container flex junto com o link/botão
   irmão. */
.sk-acoes-inline {
  display: flex;
  gap: var(--e2);
  align-items: center;
}

/* O bloco de resumo do Extrato (`D7`, Prompt #414) - "abre respondendo, e
   só depois detalha". Uma linha de grandezas que quebra em telas estreitas,
   com o gap saindo da escala (docs/59 §3), nunca de valor literal. */
.sk-resumo-extrato {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e6);
  margin: 0;
}
.sk-resumo-extrato dt {
  color: var(--sk-muted);
  font-size: 0.8125rem;
}
.sk-resumo-extrato dd {
  margin: 0;
  font-size: 1.25rem;
  font-weight: 600;
}
/* 🔴 Classe PRÓPRIA para a nota do bloco, e o nome importa: a classe de
   dica de campo é fixada pelo Princípio 45 (itálico, recuo, 0.6875rem) e a
   guarda `test_padroes_visuais` casa a PRIMEIRA regra cujo seletor a
   contenha - inclusive dentro de um `:not(...)`. Medido no Prompt #414:
   com a dica redefinida aqui, a guarda passou a ler ESTA regra e reprovou,
   corretamente. Aqui a nota é de um bloco de leitura, não de um campo de
   formulário; ela toma o tamanho e a cor do princípio sem disputar o nome.

   🔴 **E SEM `font-style: italic`, por medição:** a inversão companheira
   daquela guarda apaga a PRIMEIRA ocorrência dessa declaração no arquivo e
   espera que a guarda reprove. Com um itálico novo acima do dela, a
   inversão passava a apagar o meu e ficava verde - eu teria enfraquecido
   um teste alheio sem tocar nele.

   🔴 **E O SELETOR GANHOU UM GRAU EM 22/09/2026** (Prompt #439, `PSPO-052`),
   sem que nenhuma declaração mudasse. Com `.sk-resumo-extrato-nota` sozinho
   (0,1,0), a regra PERDIA para `.sk-resumo-extrato dd` (0,1,1) logo acima, e
   as duas notas do bloco - o "Desde ..." e o "Parte do total a pagar que ainda
   não foi cobrada." - saíam em 1.25rem e peso 600, a letra de VALOR. Assim
   desde o #414, quando a classe nasceu: a regra existia e nunca valeu.

   O grau vem do ancestral, nao de `!important` nem de elemento: `!important`
   venceria tambem o que vier depois, e prender a `dd` amarraria a nota a um
   elemento que ela não precisa ser. */
.sk-resumo-extrato .sk-resumo-extrato-nota {
  font-size: 0.6875rem;
  font-weight: 400;
  color: var(--sk-muted);
}

/* O MEMO NA LINHA DO EXTRATO (LOJA-20, Prompt #429) — decisão de Wagner de
   16/09/2026, letra `n`: o texto que o Tesoureiro digita aparece para o
   obreiro, como linha SECUNDÁRIA abaixo da categoria.

   `display: block` é o que faz dela linha secundária e não continuação da
   categoria na mesma linha - sem ele, "Confraternização" e o memo colam um
   no outro e o obreiro lê os dois como um nome só.

   Toma o mesmo tamanho e a mesma cor da nota do bloco de resumo acima: as
   duas são texto de apoio a um dado principal, e a hierarquia visual é a
   mesma. 🔴 E SEM `font-style: italic`, pela mesma medição registrada ali
   em cima - a inversão companheira daquela guarda apaga a PRIMEIRA
   ocorrência dessa declaração no arquivo. */
.sk-linha-memo {
  display: block;
  font-size: 0.6875rem;
  font-weight: 400;
  color: var(--sk-muted);
}

/* A GRADE DO PAINEL DE WIDGETS (docs/59 §3.1, Prompt #407) — `LOJA-01` e o
   painel do Master da Plataforma. As três regras da spec, nesta ordem:

   1. NO MÁXIMO TRÊS COLUNAS. O `max(...)` com `(100% - 2*gap) / 3` é o que
      impede a quarta: nenhuma coluna pode ser menor que um terço da linha,
      então não cabem quatro. Sem cravar largura de coluna em lugar nenhum.
   2. NUNCA ABAIXO DA LARGURA MÍNIMA. O outro lado do `max(...)`: quando um
      terço da linha fica menor que `--sk-widget-largura-min`, a grade PERDE
      COLUNA em vez de espremer - três viram duas, duas viram uma.
   4. O `min(100%, ...)` é o CHÃO, e ele é obrigatório: numa tela mais
      estreita que a largura mínima, sem ele a COLUNA transborda a grade e
      a página inteira ganha rolagem horizontal (medido a 390px, Prompt
      #407: coluna de 300px numa área de 271px). Com ele, a coluna vira a
      largura da tela e o que não couber rola DENTRO do widget - que é o
      que a §3.1 manda.
   3. MESMA ALTURA NA MESMA LINHA. É o `stretch` padrão do grid, declarado
      aqui porque é regra, não acaso.

   O `gap` é passo da escala (§3), nunca valor literal. */
.sk-painel-widgets {
  /* A largura mínima de uma coluna. Vive AQUI, e não no `:root` do
     `tokens.css`, de propósito: `:root` é o catálogo de Temas (docs/37
     §2.2 - toda variável de lá é ponto de variação por Loja, curado em
     `TokenDesign`), e esta é calibração do componente, mesma natureza da
     `--sk-densidade-grade`. Medido no Prompt #407: posta no `:root`, a
     guarda de integridade do catálogo reprova - e catalogá-la convidaria
     a recalibrar a grade por Tema.

     Em `rem`, nunca px: acompanha a escala de fonte do obreiro. O valor
     vem da medição dos onze widgets (Prompt #404); a REGRA escrita é
     "uma coluna nunca fica abaixo da largura mínima", nunca o número. */
  --sk-widget-largura-min: 15rem;
  display: grid;
  gap: var(--e4);
  grid-template-columns: repeat(
    auto-fit,
    minmax(min(100%, max(var(--sk-widget-largura-min), (100% - var(--e4) * 2) / 3)), 1fr)
  );
  align-items: stretch;
}
/* `min-width: 0` porque item de grid não encolhe abaixo do conteúdo por
   padrão, e uma tabela larga empurraria a coluna inteira. Com ele mais o
   `overflow-x`, o que não couber ROLA DENTRO DO WIDGET — nunca a página
   (docs/59 §3.1, "Tela estreita": medido a 390px, a área útil fica abaixo
   da largura mínima do widget mais exigente). */
.sk-painel-widgets > * {
  min-width: 0;
  overflow-x: auto;
}

/* ---------- Componentes ---------- */

.sk-card {
  background: var(--sk-surface);
  border: 0.5px solid var(--sk-border);
  border-radius: 10px;
  padding: var(--e4);
}
.sk-card + .sk-card {
  margin-top: var(--e4);
}

/* NA GRADE DO PAINEL, O ESPACAMENTO E' O `gap` (Prompt #434, 21/09/2026).

   A regra de irmao acima e' certa em fluxo vertical - dois cartoes
   empilhados precisam de ar entre eles. Dentro de `.sk-painel-widgets` ela
   e' um segundo espacamento sobre o `gap`, e ele NAO e' simetrico: pega do
   SEGUNDO widget em diante, entao o primeiro de cada linha comeca no topo e
   os outros comecam mais baixo. Com `align-items: stretch`, os que comecam
   mais baixo terminam na mesma altura - e ficam mais CURTOS.

   Foi o que Wagner reprovou na validacao de 21/09 (`PSPO-041`, item 1): o
   bloco da esquerda comecando mais alto que o da direita, na mesma linha. A
   `docs/59` §3.1 manda o contrario, com todas as letras: "MESMA ALTURA NA
   MESMA LINHA".

   🔴 A ESPECIFICIDADE E' O PONTO, e por isso o seletor e' este e nao
   `.sk-painel-widgets > *`: aquele vale (0,1,0) e PERDE para
   `.sk-card + .sk-card` (0,2,0), alem de vir antes no arquivo. Este vale
   (0,3,0) e vem depois - ganha pelos dois caminhos.

   Alcanca TODO widget porque toda raiz de widget e' `.sk-card` - medido nos
   onze em 21/09 -, e vale em toda aba, inclusive a "Minha Jornada", que e' a
   do obreiro comum, e no painel do Master da Plataforma, que usa a mesma
   grade. Guardado por `apps/dashboards/tests/test_grade_do_painel.py`. */
.sk-painel-widgets > .sk-card + .sk-card {
  margin-top: 0;
}

/* O RESPIRO ENTRE A LINHA DAS ABAS E A GRADE (Prompt #439, `PSPO-048`).

   A linha das abas do painel (`.sk-acoes-inline`) e a grade não têm margem
   nenhuma, e por isso encostavam. **O primeiro widget SEMPRE encostou** - só
   não se via, porque os vizinhos começavam mais baixo pela regra de irmão. O
   Prompt #434 zerou aquela margem dentro da grade, e estava certo: o efeito
   foi a primeira linha inteira ficar rente às abas, que e' o que Wagner viu.

   O seletor de irmão adjacente é o que limita o espaço ao caso real - grade
   que vem LOGO DEPOIS da linha das abas. O painel do Master da Plataforma não
   tem abas (a grade é o primeiro filho do cartão) e não é alcançado.

   O valor é passo da escala, e e' o MESMO `gap` da grade: o respiro entre a
   linha e a primeira fila fica igual ao respiro entre as filas. */
.sk-acoes-inline + .sk-painel-widgets {
  margin-top: var(--e4);
}
/* O CAMINHO INVERSO (Prompt #450, `PSPO-074`): bloco de ações logo DEPOIS
   de um card. Mesmo valor da escala (docs/59 §3) e mesmo molde da regra
   acima - vizinhança, nunca margem na própria `.sk-acoes-inline`, que é
   usada mais de sessenta vezes e quase sempre DENTRO de um painel, onde
   margem superior seria espaço a mais.

   🔴 São DOIS vizinhos, e o segundo não é invencionice: a `LOJA-19` e a
   `LOJA-18` põem o cartão de edição num `<dialog class="sk-modal-edicao">`,
   que fica no DOM como irmão anterior do bloco de ações mesmo depois de
   fechado (`display:none`, fora do fluxo). Sem ele, o botão perderia o
   respiro **justamente ao cancelar a edição** - e a `detalhe_subconta.html`
   já tinha essa forma antes deste prompt, medida na varredura da `F2`. */
.sk-card + .sk-acoes-inline,
.sk-modal-edicao + .sk-acoes-inline {
  margin-top: var(--e4);
}

.sk-btn {
  /* ALTURA UNIFICADA (Prompt #270, relato B.01) — antes desta regra,
     `.sk-btn` achava a altura pelo padding vertical (`--e3`), que
     escala com densidade/fonte; o Datagrid cravava `--sk-btn-height`
     nos SEUS próprios botões desde a maquete de 25/08 (comentário de
     `.sk-datagrid-painel-filtros-botao` abaixo), e o resto do sistema
     nunca acompanhou — "duas escolas" convivendo. Mesmo padrão que a
     maquete já usava: `inline-flex` + `align-items: center` para
     centralizar o conteúdo dentro de uma altura FIXA, em vez de a
     altura nascer do conteúdo. Padding vertical sai (a altura não vem
     mais dele); padding horizontal (`--e5`) continua igual. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--sk-font-body);
  font-size: 0.8125rem;
  font-weight: 500;
  /* ALTURA DERIVADA, NÃO CRAVADA (Prompt #291). `min-height` em vez de
     `height`: o token vira PISO de alvo de toque, e a altura real passa a
     ser a maior entre ele e o que o conteúdo pede. Com `height`, o rótulo
     saía da caixa a partir de 150% de zoom - a varredura do #287 mediu 29
     transbordos vindos daqui. O padding vertical volta em `em` (relativo a
     fonte DESTE elemento), para o texto ter respiro quando a caixa cresce;
     em 100% ele não muda nada, porque o piso ainda domina. */
  min-height: var(--sk-btn-height);
  padding: 0.25em var(--e5);
  border-radius: var(--sk-radius-sm);
  border: none;
  cursor: pointer;
  text-decoration: none;
  /* Um <button> mantém "appearance: auto" por padrão — o navegador pinta
     o widget nativo do SO (chrome/hover/estado pressionado) por cima do
     CSS, numa camada que getComputedStyle nem expõe. Um <a> já é
     "appearance: none" nativamente. Sem isto, um <button class="sk-btn">
     e um <a class="sk-btn"> ficam com text-decoration/color/background
     computados idênticos e, mesmo assim, visivelmente diferentes no hover
     (confirmado inspecionando os dois no Chrome) — a causa raiz não era
     "text-decoration", era o widget nativo do <button> desenhando por
     cima. "appearance: none" remove essa camada nativa, garantindo que
     ambos os elementos sejam pintados só pelas regras deste arquivo, em
     todos os estados (docs/14, princípio 4). */
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
}
.sk-btn-primary {
  background: var(--sk-orange);
  color: var(--sk-btn-primary-fg);
}
.sk-btn-secondary {
  background: transparent;
  color: var(--sk-gold);
  border: 0.5px solid var(--sk-gold);
}
.sk-btn-tertiary {
  background: transparent;
  color: var(--sk-gray);
  border: 0.5px solid var(--sk-gray);
}
/* Botão só-ícone (mesmo padrão de .sk-help-imprimir-btn) — usado quando o
   rótulo textual do botão dá lugar a um ícone com title/aria-label
   equivalente (ex: engrenagem "Personalizar" no Dashboard, LOJA-01). */
.sk-btn-icone {
  display: inline-block;
  flex: none;
  background: none;
  border: 0.5px solid var(--sk-gray);
  color: var(--sk-gray);
  border-radius: var(--sk-radius-sm);
  /* CAIXA ELÁSTICA, não fixa (Prompt #147). O `em` é relativo ao
     `font-size` DESTE elemento, então a caixa acompanha o próprio glifo:
     no padrão (100% = base 20px) o resultado é exatamente o mesmo px de
     antes, e nos extremos da faixa nova a caixa cresce junto em vez de
     cortar o conteúdo.

     Por que era preciso: a base subiu para 20px e o teto da escala
     continua em 150%, ou seja 30px de raiz — terreno que nunca existiu
     (o máximo antigo eram 24px). Com a caixa cravada em px, o glifo
     passava a caixa lá em cima e o botão ficava com o símbolo
     transbordando. Nada muda no tamanho que Wagner valida. */
  width: 1.6em;
  height: 1.6em;
  line-height: 1.48;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  font-size: 0.8125rem;
  padding: 0;
}
.sk-btn-icone:hover {
  color: var(--sk-gold);
  border-color: var(--sk-gold);
  text-decoration: none;
}
/* Nível 4 (docs/14, princípio 1) — ações destrutivas/irreversíveis, ex:
   "Excluir versão". Sólido (não contornado, ao contrário do Nível 3) para
   pesar visualmente tanto quanto o Nível 1 — a seriedade da ação exige
   destaque, não discrição. Sempre acompanhado de confirmação explícita
   antes de executar (ver .sk-btn-danger[data-confirmar] em base.html). */
.sk-btn-danger {
  background: var(--sk-danger);
  color: #fff;
}
/* Paridade de comportamento entre opções de mesmo nível (docs/14, princípio
   4): sem esta regra, um <a class="sk-btn"> herda o sublinhado de "a:hover"
   (regra global mais abaixo) enquanto um <button class="sk-btn"> não tem
   como herdá-lo — duas opções visualmente idênticas passavam a divergir só
   no hover, dependendo do elemento HTML por trás. Especificidade de
   ".sk-btn:hover" (0,2,0) já vence "a:hover" (0,1,1) independente da ordem
   das regras no arquivo. */
.sk-btn:hover {
  text-decoration: none;
  /* Retorno visual de hover (docs/14, princípio 1/4 — antes desta regra
     .sk-btn:hover só mudava text-decoration, sem NENHUM realce de estado).
     Fundo sutil para os níveis com fundo transparente (Secundário,
     Terciário): mesmo `color-mix` do hover do menu principal (Prompt #106,
     acima) — sobre --sk-fg/--sk-bg do Tema ativo, nunca cor fixa, então
     clareia em Tema escuro e escurece em Tema claro, sempre no mesmo passo
     relativo. Níveis 1 e 4 (fundo sólido) SOBRESCREVEM este valor logo
     abaixo — clarear o elemento mais luminoso da tela reduziria o
     contraste com o texto claro por cima; eles escurecem, não usam este
     mecanismo. */
  background: color-mix(in srgb, var(--sk-fg) 8%, var(--sk-bg));
}
/* Nível 1 e 4 (docs/14, princípio 1) — achado do #184: misturar com
   `black` parte do pressuposto de que o fundo do botão é sempre escuro,
   e no Pavimento Mosaico não é — --sk-orange lá já é #0A0A0A (quase
   preto), então "escurecer mais" pede um passo a partir de um valor já
   no piso, e fica imperceptível. Não é limite do color-mix, é direção
   errada.

   A direção certa é RELATIVA AO FUNDO DO TEMA, não a um extremo fixo:
   misturar com var(--sk-bg), não com black. --sk-bg é o oposto da ação
   principal por construção — é dele que o botão tira contraste —, então
   misturar com ele sempre move na direção que tem margem: escurece no
   Pedra Bruta e na Abóbada Celeste (--sk-bg escuro nos dois), clareia no
   Pavimento Mosaico (--sk-bg claro).

   90% É CALIBRADO, NÃO PALPITE (Prompt #185): a mistura desloca o fundo
   em direção ao texto do próprio botão (--sk-btn-primary-fg / branco em
   .sk-btn-danger) — misturar demais fecha esse contraste, de menos não
   sai do piso em Pavimento Mosaico. Varrido 60–94% checando as 3
   combinações de fundo × os 2 textos nos 3 Temas de fábrica (6
   pontos): 90% é o menor passo em que os 6 ainda passam WCAG AA
   (>= 4,5:1) — o ponto de menor movimento visual que ainda garante
   contraste em todos, não o de maior efeito. Um único número serve aos
   três Temas; não precisou de token por Tema. Confirmado também por
   captura real (Playwright, hover de mouse) nos três. */
.sk-btn-primary:hover {
  background: color-mix(in srgb, var(--sk-orange) 90%, var(--sk-bg));
}
.sk-btn-danger:hover {
  background: color-mix(in srgb, var(--sk-danger) 90%, var(--sk-bg));
}
/* Fora do escopo desta correção (docs/14, princípio 1) — .sk-btn-icone já
   tinha hover próprio (cor + borda) antes de .sk-btn ganhar fundo; sem
   esta linha, os dois casos que combinam "sk-btn sk-btn-tertiary
   sk-btn-icone" (configuracao_visual.html) herdariam o fundo novo por
   acidente de especificidade igual + ordem no arquivo. */
.sk-btn-icone:hover {
  background: none;
}
/* Botão desabilitado (docs/14, princípio 1/4 — achado do Catálogo Vivo,
   MASTER-24, Prompt #185): até aqui `.sk-btn:disabled` não tinha estilo
   PRÓPRIO nenhum — indistinguível de um botão ativo. Isso não era só
   estética: a decisão de trocar a paginação do Datagrid por chevrons
   (docs/59, P3) foi sustentada pelo argumento "com seta, o estado
   desabilitado se lê de imediato", que pressupõe um desabilitado
   legível — e ele não existia.

   `opacity`/`cursor`, nunca cor literal — funcionam sobre QUALQUER fundo
   de Tema sem token novo, mesmo raciocínio de abertura do arquivo. `.4`
   verificado nos três Temas de fábrica (Prompt #186): serviu aos três,
   inclusive o Pavimento Mosaico (fundo claro), sem precisar de ajuste —
   ver nota de calibração no relato do Prompt #186. */
.sk-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
/* Sem esta regra, o hover que o #185 deu a .sk-btn (linha ~793) continua
   respondendo em cima de um elemento desabilitado — o cursor do
   navegador já indicaria "não clicável", mas o FUNDO mudaria como se
   fosse, contradizendo o próprio cursor.

   `transparent` é certo para os níveis de fundo transparente (Secundário/
   Terciário, cujo `.sk-btn:hover` genérico é a origem do tingimento) —
   mas ERRADO para os de fundo sólido: `.sk-btn:disabled:hover` (0,3,0)
   tem especificidade MAIOR que `.sk-btn-primary`/`.sk-btn-danger`
   (0,1,0), então sem as duas regras abaixo ele apagaria o preenchimento
   inteiro do botão ao passar o mouse — não só cancelaria o hover, como
   faria o botão desabilitado desaparecer visualmente (achado ao
   verificar nos três Temas, Prompt #186). Cada nível sólido recupera o
   PRÓPRIO fundo de repouso, nunca `transparent`. */
.sk-btn:disabled:hover {
  background: transparent;
}
.sk-btn-primary:disabled:hover {
  background: var(--sk-orange);
}
.sk-btn-danger:disabled:hover {
  background: var(--sk-danger);
}

.sk-field {
  margin-bottom: var(--e4);
}

/* ---------------------------------------------------------------------
   GRID DE FORMULÁRIO (docs/14, Princípio 33 — Prompt #139)

   Até aqui TODO campo do sistema ocupava a linha inteira, em toda tela. A
   LOJA-03 tem 27 campos; MASTER-11 tem 21, CONTA-02 24, LOJA-25 20 e
   CAD-03 19 — e o Prompt #124, ao devolver ao rótulo a tipografia do
   corpo (Princípio 29), tornou esses formulários ainda mais altos. Um
   campo de 3 caracteres ("Ramal", "Número") gastando 100% da largura não
   é decisão de layout, é ausência dela.

   DOZE COLUNAS, e não uma classe por combinação (`--7-3`, `--4-4-4`…):
   com 12 colunas qualquer proporção útil se expressa somando inteiros, e
   o utilitário não precisa crescer a cada tela nova. É o mesmo motivo de
   `tipo_exibicao` no Datagrid ser um vocabulário fechado em vez de CSS
   por tela.

   O `gap` substitui a margem do `.sk-field` DENTRO da linha — mantê-la
   somaria com o `row-gap` e a linha ficaria mais espaçada que as demais.
   Fora da linha, `.sk-field` segue exatamente como antes: uma tela que
   não usa o utilitário renderiza o mesmo HTML e o mesmo CSS de sempre. */
.sk-form-linha {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--e4) var(--e3);
  margin-bottom: var(--e4);
  /* Campos de uma mesma linha alinham pelo TOPO, nunca esticando: um
     campo com dica ou erro embaixo é mais alto que o vizinho, e
     `stretch` (padrão do grid) deformaria o input do menor para
     acompanhá-lo. */
  align-items: start;
}
.sk-form-linha > .sk-field {
  margin-bottom: 0;
  /* `min-width: 0` porque um item de grid tem `min-width: auto` por
     padrão e se recusa a encolher abaixo do próprio conteúdo — um input
     com `size` grande estouraria a coluna em vez de caber nela. */
  min-width: 0;
}
.sk-col-1 { grid-column: span 1; }
.sk-col-2 { grid-column: span 2; }
.sk-col-3 { grid-column: span 3; }
.sk-col-4 { grid-column: span 4; }
.sk-col-5 { grid-column: span 5; }
.sk-col-6 { grid-column: span 6; }
.sk-col-7 { grid-column: span 7; }
.sk-col-8 { grid-column: span 8; }
.sk-col-9 { grid-column: span 9; }
.sk-col-10 { grid-column: span 10; }
.sk-col-11 { grid-column: span 11; }
.sk-col-12 { grid-column: span 12; }

/* Espaçador que ocupa a altura de um rótulo, para alinhar com os vizinhos
   um item que não tem rótulo próprio — o caso real é o botão "Consultar
   CEP" ao lado do campo CEP.

   É um `<label>` VAZIO, não uma margem em pixels: assim ele herda a mesma
   regra tipográfica dos rótulos reais e a altura casa POR CONSTRUÇÃO,
   inclusive se o tamanho do rótulo mudar de novo (mudou no Prompt #124).

   A alternativa óbvia — `align-self: end` na célula do botão — resolve o
   caso feliz e quebra o real: basta o campo vizinho ter um erro de
   validação para a célula dele crescer para baixo, e o botão colado na
   base desce junto, desencontrado do input. */
.sk-rotulo-espacador {
  user-select: none;
}

/* Empilhamento automático (Princípio 33). O Skirret é de uso
   predominantemente desktop e isto NÃO é esforço de design mobile: é a
   media query que o grid dá de graça, para o formulário não entregar três
   campos espremidos numa tela de 6 polegadas. 700px é o mesmo breakpoint
   já usado no arquivo, não um valor novo. */
@media (max-width: 700px) {
  .sk-form-linha {
    grid-template-columns: 1fr;
  }
  .sk-form-linha > [class*="sk-col-"] {
    grid-column: span 1;
  }
  /* Empilhado, o espaçador só criaria uma linha em branco entre os
     campos — não há mais vizinho lateral com quem alinhar. */
  .sk-rotulo-espacador {
    display: none;
  }
}
/* Rótulo de campo usa a tipografia do CORPO, não a de legenda (docs/14,
   princípio 29 — Prompts #108 e #124).

   O seletor cobre as DUAS formas de renderizar campo que o sistema usa:
   `.sk-field` (widgets próprios) e o `<p>` que `form.as_p` gera dentro de
   um `sk-card`. Antes eram duas regras separadas, e a segunda usava `>`
   (filho DIRETO de form) — rótulos aninhados numa `<div>` de aba
   escapavam dela e herdavam o corpo por acidente. Era exatamente esse
   acidente que o #108 identificou ao investigar por que os rótulos de
   CAD-03 pareciam melhores que os do resto do sistema.

   Agora o seletor é DESCENDENTE, então o princípio vale por construção:
   não existe aninhamento que escape.

   ---------------------------------------------------------------------
   PRINCÍPIO 43 (Prompt #293) — a TIPOGRAFIA mudou; o SELETOR, não.

   Wagner julgou três colunas de formulário sobre maquete em 05/09/2026 e
   escolheu o rótulo um degrau ABAIXO do conteúdo: `0.6875rem`, peso 400,
   `--sk-muted`. É o padrão anterior ao #124, e a volta é deliberada.

   **A recusa de 16/08 (Princípio 29) estava contaminada**, e o próprio
   texto do 29 registra por quê: naquela época os rótulos reduzidos
   escapavam do seletor de filho direto e perdiam junto o
   `display:block; margin-bottom` — rótulo e campo renderizavam na MESMA
   LINHA. A auditoria do #108 diz que *"o 'padrão bom' e o layout
   quebrado eram literalmente o mesmo acidente"*. Em 16/08 Wagner
   comparou rótulo pequeno com layout ERRADO contra rótulo grande com
   layout CERTO; em 05/09 comparou três layouts corretos. É a primeira
   vez que estes valores foram julgados limpos.

   **A TRAVA:** recupera-se a tipografia, NUNCA o seletor. Voltar ao
   filho direto traz de volta o layout quebrado, que nunca esteve em
   julgamento. Guardado por `Principio43RotulosTestCase`, que lê este
   arquivo e recusa a forma de FILHO DIRETO do seletor - a lista literal
   das formas proibidas mora no teste, e não aqui, porque a guarda é uma
   busca de texto neste arquivo: escrever aqui a string que ela procura
   faria a guarda acusar o próprio comentário que a explica. (Aconteceu
   na primeira execução do Prompt #293, e está no `§3` dele.)

   **`font-weight: 400` é DECLARADO**, não herdado — a regra passa a
   dizer o que quer em vez de depender de quem a envolve.

   **O que este comentário dizia antes e deixou de ser verdade:** que
   *"`.sk-field-dica` continua menor e esmaecida de propósito — a
   distinção entre rótulo e dica é essa"*. **Não é mais.** Com o 43, o
   rótulo tem exatamente a tipografia da dica (`0.6875rem`/`--sk-muted`),
   e o que os distingue passa a ser só a POSIÇÃO. Medido e relatado no
   `§2` do Prompt #293; a decisão sobre isso é de Wagner, sobre maquete,
   e não foi tomada aqui. */
.sk-field label:not(.sk-btn),
.sk-card form p label:not(.sk-btn),
.sk-card fieldset p label:not(.sk-btn) {
  display: block;
  font-size: 0.6875rem;
  font-weight: 400;
  color: var(--sk-muted);
  margin-bottom: var(--e2);
}
/* `:not(.sk-btn)` acima não é firula: os widgets de upload usam
   `<label class="sk-btn sk-btn-tertiary">` como gatilho de arquivo
   (`sk_logomarca_input.html`, `sk_foto_obreiro_input.html`), e a regra de
   rótulo — mais específica (0,1,1) que `.sk-btn` (0,1,0) — vencia,
   degradando aqueles BOTÕES a texto de rótulo com `display:block`. O
   defeito já existia antes do princípio 29; a unificação só o tornaria
   mais visível, então foi corrigido junto.

   `.helptext` é a dica que `form.as_p` gera (56 usos no projeto) e nunca
   teve regra nenhuma: herdava o corpo e ficava MAIOR e mais contrastada
   que o rótulo ao lado — o inverso exato do que o princípio 29 pede. Ela
   recebe aqui o mesmo tratamento de `.sk-field-dica`, que é o seu
   equivalente nos formulários de widget próprio. */
.helptext {
  display: block;
  font-size: 0.6875rem;
  color: var(--sk-muted);
  margin-top: var(--e1);
  /* Princípio 45 (docs/14, decidido por Wagner em 06/09/2026 sobre maquete
     de cinco tratamentos combináveis). ITÁLICO e RECUO, e só.

     **Tamanho, cor e peso NÃO mudam** - continuam idênticos aos do rótulo,
     que é o Princípio 43 e fica intacto.

     O 45 nasce de uma CONSEQUÊNCIA do 43, não de um defeito: ao descer o
     rótulo para `0.6875rem`/`--sk-muted`, o 43 o deixou com a tipografia
     exata da dica, e o que os separava passou a ser só a posição.

     **A colisão real não é dentro do campo - é entre campos.** Rótulo e dica
     do mesmo campo nunca se tocam: entre os dois está o input. O que colide é
     a dica de um campo contra o rótulo do SEGUINTE, e só um formulário
     corrido mostra isso; a maquete que julgou um campo isolado não podia ver.

     **O recuo reforça um agrupamento que já existia**, em vez de disputar com
     ele: a dica está a `--e1` do campo dela e a `--e4` do próximo rótulo,
     quatro para um. Recuar diz "isto pertence ao que está acima", que é a
     relação verdadeira. Encolher ou apagar diria "isto é menos importante", e
     a dica não é menos importante que o rótulo: é SUBORDINADA a ele. */
  font-style: italic;
  padding-left: var(--e3);
}
.sk-field input,
.sk-field select,
.sk-field textarea {
  width: 100%;
  box-sizing: border-box;
  background: color-mix(in srgb, var(--sk-fg) 4%, var(--sk-bg));
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius-sm);
  padding: var(--e2) var(--e3);
  color: var(--sk-fg);
  font-size: 0.8125rem;
  font-family: var(--sk-font-body);
}
.sk-field input[type="checkbox"] {
  width: auto;
}
.sk-field input:disabled,
.sk-field select:disabled,
.sk-field textarea:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
/* Checkbox sempre adjacente ao rótulo, imediatamente à direita dele
   (docs/14, princípio 12) — sobrepõe o "display: block" padrão de
   .sk-field label, que empurraria o checkbox para a linha de baixo. */
.sk-field-checkbox {
  display: flex;
  align-items: center;
  gap: var(--e2);
}
.sk-field-checkbox label {
  display: inline;
  margin-bottom: 0;
}
/* Checkbox dentro do GRID de formulário (Prompt #187, docs/59 §6 fase 3,
   primitivo "bloco de campo") — alinhado com a BASE dos campos vizinhos,
   nunca com o topo. Os vizinhos têm rótulo (uma linha, ~19px + 6px de
   margem) ACIMA do widget; o checkbox não tem rótulo acima, então sem
   este respiro ele nasceria mais alto na célula, desalinhado com a caixa
   dos inputs ao lado. Valor calibrado por medição real (Playwright,
   `bounding_box()` do input vs. do checkbox numa página fixa carregando
   este CSS) até os dois centros verticais coincidirem — não é herdado de
   `.sk-field-valor` (aquele usa `padding: 9px 0`, problema geométrico
   diferente). */
.sk-form-linha > .sk-field-checkbox {
  padding-top: 41px; /* sk-excecao: constante geométrica calibrada por medição (comentário acima), não espaçamento de escala */
}
/* Obrigatório — asterisco no rótulo, nunca a palavra (Prompt #187,
   docs/59 §6 fase 3). Cor de AÇÃO (--sk-orange), não de erro: obrigatório
   não é problema, é informação neutra sobre o que preencher. */
.sk-obrigatorio {
  color: var(--sk-orange);
  margin-left: 2px;
}
/* Campo com erro — borda de perigo no widget (Prompt #187). Decidido pela
   presença de `campo.errors` NO TEMPLATE (classe `.sk-field--erro`),
   nunca por `:has()` — o projeto evita depender dele (ver nota de
   `.sk-loja-cabecalho`, Prompt #171/#186). */
.sk-field--erro input,
.sk-field--erro select,
.sk-field--erro textarea {
  border-color: var(--sk-danger);
}
/* Ícone ao lado da mensagem de erro (Prompt #187) — erro que depende só
   de cor falha para quem não a distingue (docs/59 §6). `::before` em vez
   de mexer no HTML que o Django gera: `.errorlist li` já existe e já
   carrega a cor/peso corretos (`--sk-danger-fg`, Prompt #123) — só falta
   o símbolo. SVG embutido (não glifo de texto) para nitidez em qualquer
   escala de fonte, com `currentColor` herdando a cor do próprio erro.

   `display:flex` etc. entram na regra ORIGINAL do item de lista de erro
   (adiante neste arquivo), não aqui — uma segunda regra para o mesmo
   seletor já produziu um achado real (Prompt #187: um teste de guarda
   procurava a cor do texto pela primeira ocorrência LITERAL do seletor
   no arquivo, e achava esta cópia, sem a cor — corrigido consolidando
   numa regra só). Só o `::before` mora aqui. */
.errorlist li::before {
  content: "";
  flex: none;
  width: 11px;
  height: 11px;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round'><circle cx='8' cy='8' r='6.4'/><path d='M8 4.8v3.6'/><path d='M8 11.2v.01'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round'><circle cx='8' cy='8' r='6.4'/><path d='M8 4.8v3.6'/><path d='M8 11.2v.01'/></svg>") center / contain no-repeat;
}
/* Área de texto — altura mínima de dois campos, redimensionável só na
   vertical (Prompt #187). `--sk-btn-height` já é a altura de referência
   de UM campo de texto (docs/14, princípio 23) — dobrada aqui, não um
   valor novo. */
.sk-field textarea {
  min-height: calc(var(--sk-btn-height) * 2);
  resize: vertical;
}
/* Combobox — chevron indicando lista (Prompt #187, docs/59 §6 fase 3).
   `.sk-combobox` é o invólucro que combobox.js cria em volta do
   `<input>` (Prompt #111) — o chevron entra nele, nunca no input, para
   não competir com o texto digitado. `padding-right` no input abre
   espaço para o glifo não sobrepor o fim do texto. */
.sk-combobox input {
  padding-right: var(--e6);
}
.sk-combobox::after {
  content: "▾";
  position: absolute;
  right: 11px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 0.7rem;
  color: var(--sk-muted);
  pointer-events: none;
}
/* Seleção de Widgets (LOJA-12/LOJA-13, SelecaoWidgetsForm, campo
   "widgets") — Django renderiza CheckboxSelectMultiple como
   <label><input>Nome</label> sem nenhum alinhamento; mesmo tratamento de
   .sk-field-checkbox acima, mas por id (form.as_p não passa por nenhuma
   classe própria do app). Mesmo id nas duas telas, então o mesmo fix
   corrige as duas de uma vez. */
#id_widgets label {
  display: flex;
  align-items: center;
  gap: var(--e2);
}
/* .sk-card form input (acima) dá width:100% a QUALQUER <input>, sem
   excluir checkbox (diferente de .sk-field input[type="checkbox"], que já
   tem essa exclusão) — dentro do label em flex acima, isso infla a
   "flex-basis" do checkbox pro tamanho da linha toda (item de flex fica
   enorme, mesmo o desenho nativo do checkbox continuando pequeno),
   empurrando o texto do nome do widget pra fora do alinhamento. */
#id_widgets input[type="checkbox"] {
  width: auto;
  flex: none;
}
/* LISTA DE CAIXAS DE MARCACAO, no sistema inteiro (docs/14 Principio 50,
   emenda de 24/09/2026 - Prompt #459, Wagner na validacao local do #457:
   "os controles nas posicoes adequadas - a esquerda do texto").

   O CheckboxSelectMultiple do Django desenha <label><input> Nome</label> - a
   caixa DENTRO do rotulo. As regras de rotulo de formulario acima (Principio
   29/43) fazem todo <label> virar BLOCO, e a caixa ficava solta numa linha,
   com o nome embaixo. Esta regra generaliza o conserto que o `#id_widgets`
   acima fazia so' para uma tela, por id.

   SO GEOMETRIA: tamanho, cor e peso da fonte continuam os dos Principios 43 e
   45. E NAO reordena nada - a caixa fica a esquerda porque o Django a escreve
   antes do texto. A caixa solitaria do padrao .sk-field-checkbox (rotulo IRMAO
   da caixa, nao pai) nao casa com :has(> input), e fica de fora: a ordem dela
   e' a tensao 12 x 39 do docs/14, ainda aberta para esse caso.

   :not(.sk-btn) repete o das regras de rotulo, e deixa esta mais especifica do
   que elas - senao o display:block delas venceria. */
.sk-card form label:not(.sk-btn):has(> input[type="checkbox"]),
.sk-field label:not(.sk-btn):has(> input[type="checkbox"]) {
  display: flex;
  align-items: center;
  gap: var(--e2);
}
/* O `.sk-card form input` da width:100% a todo <input>, sem excluir a caixa -
   dentro do rotulo em flex ela esticaria e empurraria o texto (mesma razao do
   `#id_widgets input` acima). */
.sk-card form label:not(.sk-btn):has(> input[type="checkbox"]) > input[type="checkbox"],
.sk-field label:not(.sk-btn):has(> input[type="checkbox"]) > input[type="checkbox"] {
  width: auto;
  flex: none;
}
/* Valor somente-leitura dentro de um .sk-field (ex: LOJA-14, espelho de
   MASTER-11 sem nenhum controle de edição) — mesma tipografia de
   .sk-field input, sem a caixa/borda de campo editável. */
/* VALOR EM LEITURA dentro de um `.sk-field` — agora com CAIXA PRÓPRIA,
   AFUNDADA (docs/14 Princípio 44, Prompt #293).

   O PADDING VERTICAL NÃO É ESTÉTICA: é o que alinha a linha de base
   deste texto com a dos campos EDITÁVEIS da mesma linha do grid
   (Prompt #178, docs/14 Princípio 38). Sem ele, o valor encostava no
   topo da célula enquanto os vizinhos tinham a altura do input, e a
   linha inteira lia como desalinhada — visto na LOJA-03 (CPF /
   Nascimento / Idade), mas presente em toda tela que mistura os dois.

   Os números são os do input, copiados de propósito: `padding:
   var(--e2) var(--e3)` e `border: 0.5px`. **O comentário anterior dizia
   `padding: 9px` e `border: 0.5px`** — o `9px` envelheceu no trabalho
   de densidade e nunca foi corrigido; hoje `--e2` é `8px * densidade`.
   Comentário que cita número solto mente na primeira mudança de escala.

   ---------------------------------------------------------------------
   O QUE MUDOU NO #293, e a premissa que morreu junto.

   Este comentário afirmava que *"o padding HORIZONTAL fica em zero — o
   texto se alinha ao próprio rótulo, e indentá-lo 11px o descolaria dele
   sem ganhar nada"*. **Essa premissa acabou:** com o Princípio 44 existe
   uma CAIXA, e a indentação passa a alinhar o texto do valor com o texto
   DENTRO do input ao lado, que é o alinhamento que o 44 pede. O padding
   horizontal vai a `--e3`, o mesmo do input.

   **A borda transparente de 0.5px FICA**, e não é resíduo: o Princípio
   44 diz "sem borda" no sentido de sem borda VISÍVEL. Aquela borda é
   MÉTRICA — veio do Princípio 38 para dar à caixa a mesma altura do
   input. Removê-la encolhe a caixa e quebra justamente o alinhamento
   que o 44 existe para garantir.

   **O `border-radius` entra igual ao do input**, senão a caixa afundada
   e a caixa editável na mesma linha teriam cantos diferentes.

   ---------------------------------------------------------------------
   A PROFUNDIDADE REAL, MEDIDA — e ela é menor do que a maquete sugeria.

   `rgba(0,0,0,.35)` foi julgado sobre maquete, cujo fundo era escolhido.
   Na tela real esta caixa vive dentro de um `.sk-card`, cujo fundo é
   `--sk-surface` (`rgba(255,255,255,.03)`) sobre `--sk-bg`. Medido no
   #293, tema Pedra Bruta: fundo do cartão `#111111`, caixa afundada
   `#0B0B0B` — **6,1 níveis de diferença em 255**, razão de contraste
   **1,04:1**. A queda de luminância é de 40%, mas 40% de quase-zero
   continua quase-zero.

   **Está aplicado assim mesmo, com o valor que Wagner decidiu.** O
   número foi para o `§2` do #293 para ele julgar na tela real; se disser
   que sumiu, o próximo prompt começa com a conta pronta em vez de
   adivinhação. Melhorar por conta própria seria trocar o julgamento
   dele pelo meu.

   ---------------------------------------------------------------------
   REFERÊNCIA AMBÍGUA, deixada como está por decisão do #293: o
   comentário original citava "(docs/29 §2.1)". O `docs/29` é Membros e
   sua §2.1 é o MODELO `PerfilObreiro`, não um padrão visual — a tela
   está na §3. Pode ter sido para ser o **Princípio 29 do `docs/14`**
   (tipografia de rótulo), que o assunto aproxima. Não dá para saber sem
   chutar, e chutar numa referência é como ela envelhece: fica anotado
   aqui e relatado no `§3`. */
.sk-field-valor {
  margin: 0;
  color: var(--sk-fg);
  font-size: 0.8125rem;
  white-space: pre-line;
  padding: var(--e2) var(--e3);
  border: 0.5px solid transparent;
  border-radius: var(--sk-radius-sm);
  background: rgba(0, 0, 0, 0.35);
}
.sk-field-dica {
  display: block;
  font-size: 0.6875rem;
  color: var(--sk-muted);
  margin-top: var(--e1);
  /* Princípio 45 (docs/14, decidido por Wagner em 06/09/2026 sobre maquete
     de cinco tratamentos combináveis). ITÁLICO e RECUO, e só.

     **Tamanho, cor e peso NÃO mudam** - continuam idênticos aos do rótulo,
     que é o Princípio 43 e fica intacto.

     O 45 nasce de uma CONSEQUÊNCIA do 43, não de um defeito: ao descer o
     rótulo para `0.6875rem`/`--sk-muted`, o 43 o deixou com a tipografia
     exata da dica, e o que os separava passou a ser só a posição.

     **A colisão real não é dentro do campo - é entre campos.** Rótulo e dica
     do mesmo campo nunca se tocam: entre os dois está o input. O que colide é
     a dica de um campo contra o rótulo do SEGUINTE, e só um formulário
     corrido mostra isso; a maquete que julgou um campo isolado não podia ver.

     **O recuo reforça um agrupamento que já existia**, em vez de disputar com
     ele: a dica está a `--e1` do campo dela e a `--e4` do próximo rótulo,
     quatro para um. Recuar diz "isto pertence ao que está acima", que é a
     relação verdadeira. Encolher ou apagar diria "isto é menos importante", e
     a dica não é menos importante que o rótulo: é SUBORDINADA a ele. */
  font-style: italic;
  padding-left: var(--e3);
}
/* Campo + botão "Testar link" lado a lado (docs/17, item 2.1 — video_url/
   audio_url). */
.sk-field-com-teste {
  display: flex;
  gap: var(--e2);
  align-items: center;
}
.sk-field-com-teste input {
  flex: 1;
}
.sk-help-imagem-atual {
  display: flex;
  align-items: center;
  gap: var(--e3);
}
.sk-help-imagem-atual img {
  width: 64px;
  height: 64px;
  object-fit: cover;
  border-radius: var(--sk-radius-sm);
  border: 0.5px solid var(--sk-border-strong);
}
/* Espaçamento entre o formset de imagens e a linha de botões de ação logo
   abaixo — sem isto ficam colados (docs/14, princípio 9 ampliado). */
.sk-help-imagens-formset {
  margin-bottom: var(--e5);
}

/* Preview da logomarca atual da Loja (MASTER-11, docs/05 seção 2.5) —
   object-fit: contain (não cover) porque ~50% das Lojas têm logomarca em
   proporção 4:3 vertical, não quadrada (docs/05, item 2.2.1) — nunca
   recortar, só limitar o tamanho de exibição. A remoção em si usa o
   checkbox "Clear" nativo do ClearableFileInput do Django, escondido
   visualmente dentro do botão "Remover logomarca atual"
   (`apps.core.widgets.LogomarcaClearableFileInput`, `.sk-visualmente-
   oculto` abaixo) — nunca o texto/link cru padrão do widget. */
.sk-logomarca-preview {
  display: block;
  max-width: 200px;
  max-height: 200px;
  object-fit: contain;
  border-radius: var(--sk-radius-sm);
  border: 0.5px solid var(--sk-border-strong);
  background: color-mix(in srgb, var(--sk-fg) 4%, var(--sk-bg));
  padding: var(--e2);
}

/* Preview da Foto do obreiro (LOJA-03, docs/29 §2.1, Prompt #64) —
   circular (border-radius: 50%), tamanho de exibição menor que a
   logomarca. object-fit: cover (diferente de .sk-logomarca-preview, que
   usa contain) — um retrato de pessoa é sempre esperado preencher o
   círculo todo (padrão universal de avatar), diferente de uma logomarca
   institucional, onde qualquer recorte é inaceitável. */
.sk-foto-obreiro-preview {
  display: block;
  width: 160px;
  height: 160px;
  object-fit: cover;
  border-radius: 50%;
  border: 0.5px solid var(--sk-border-strong);
  background: color-mix(in srgb, var(--sk-fg) 4%, var(--sk-bg));
}

/* Esconde visualmente um elemento sem tirá-lo da árvore de acessibilidade
   (nunca display:none/visibility:hidden, que também some para leitores de
   tela) — ex: o checkbox nativo "Clear" do ClearableFileInput, envolvido
   por um <label> estilizado como botão (`.sk-btn`). */
.sk-visualmente-oculto {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px; /* sk-excecao: padrão visualmente-oculto de acessibilidade — o -1px é parte do clip, não espaçamento */
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------------
   CAMPO DE ARQUIVO COM RÓTULO PRÓPRIO (Prompt #139)

   "Choose File"/"No file chosen" vêm do widget nativo do navegador e não
   aceitam tradução por CSS nem por Django. O input fica visualmente
   oculto (`.sk-visualmente-oculto`, acima — nunca `display:none`, que o
   tiraria do teclado e dos leitores de tela) e um `<label class="sk-btn">`
   faz o papel de gatilho.

   ESTADO VISÍVEL DA REMOÇÃO. Antes o checkbox "Limpar" era FILHO do
   label: clicar não mudava nada na tela, e a marcação só se revelava ao
   salvar. Agora ele é IRMÃO, o que permite `:checked + label` em CSS
   puro — sem JS, e sem depender de `:has()`. O rótulo troca para dizer o
   que vai acontecer E QUANDO ("ao salvar"), porque a remoção não é
   imediata e um "✓" sozinho deixaria isso ambíguo. */
.sk-remover-arquivo-marcado {
  display: none;
}
.sk-remover-arquivo-controle:checked + .sk-remover-arquivo .sk-remover-arquivo-ocioso {
  display: none;
}
.sk-remover-arquivo-controle:checked + .sk-remover-arquivo .sk-remover-arquivo-marcado {
  display: inline;
}
/* Marcado, o botão deixa de ser neutro: a cor de ação destrutiva
   (`--sk-danger`) é o que diferencia "posso remover" de "vou remover". */
.sk-remover-arquivo-controle:checked + .sk-remover-arquivo {
  background: var(--sk-danger);
  color: #fff;
}
/* O foco do checkbox oculto precisa aparecer NO LABEL, senão navegar por
   teclado até ele não mostra nada. */
.sk-remover-arquivo-controle:focus-visible + .sk-remover-arquivo {
  outline: 2px solid var(--sk-gold);
  outline-offset: 2px;
}
.sk-arquivo-nome {
  display: inline-block;
  margin-left: var(--e2);
  font-size: 0.75rem;
  color: var(--sk-muted);
  vertical-align: middle;
}

/* Avatar clicável (Prompt #187, LOJA-03, docs/59 §6 fase 3) — variante do
   campo de arquivo acima em que a IMAGEM é o gatilho. Mesmo mecanismo de
   remoção (`.sk-remover-arquivo*`), só reposicionado abaixo da imagem em
   vez de ao lado de um botão de texto. */
.sk-avatar-clicavel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e2);
}
.sk-avatar-clicavel-gatilho {
  position: relative;
  display: block;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  cursor: pointer;
  /* `.sk-field label:not(.sk-btn)` é global mas escopado a `.sk-field` —
     este gatilho fica fora dele, então não herda `display:block`/
     `margin-bottom` de propósito; declarado aqui para não depender de
     onde o widget for colocado no futuro. */
  margin: 0;
}
.sk-avatar-clicavel-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
  border: 0.5px solid var(--sk-border-strong);
  background: color-mix(in srgb, var(--sk-fg) 4%, var(--sk-bg));
}
/* Véu "Trocar" — só no hover/foco, mesmo padrão de affordance sob
   demanda já usado no botão de ações do Datagrid (docs/38 §2.16). */
.sk-avatar-clicavel-veu {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 0.625rem;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 120ms ease;
  /* O clique precisa alcançar o <label> por baixo, não o véu. */
  pointer-events: none;
}
.sk-avatar-clicavel-gatilho:hover .sk-avatar-clicavel-veu,
.sk-avatar-clicavel-gatilho:focus-visible .sk-avatar-clicavel-veu {
  opacity: 1;
}
.sk-avatar-clicavel-gatilho:focus-visible {
  outline: 2px solid var(--sk-gold);
  outline-offset: 2px;
}
/* Link de remoção MENOR que o botão padrão — é um acessório abaixo do
   avatar, não uma ação de mesmo peso que "Salvar"/"Cancelar". */
.sk-avatar-clicavel-remover {
  height: auto;
  padding: 3px var(--e3);
  font-size: 0.6875rem;
}

/* QUAL REGISTRO ESTÁ SENDO EDITADO (docs/14, Princípio 34 — Prompt #139).

   Nasceu como cabeçalho do MASTER-11 (docs/05, seção 2.5.1: logomarca à
   esquerda + nome completo da Loja) e foi GENERALIZADO, não recriado: o
   #139 precisava do mesmo tratamento na LOJA-03 (foto + nome do obreiro),
   e um segundo par de classes dizendo a mesma coisa divergiria na primeira
   mudança de estilo.

   `.sk-loja-cabecalho*` permanece como ALIAS dos nomes genéricos — a
   MASTER-11 continua com o HTML que sempre teve, e nenhum teste ou
   template dela precisou mudar por causa desta generalização. */
.sk-registro-cabecalho,
.sk-loja-cabecalho {
  display: flex;
  align-items: center;
  gap: var(--e4);
  margin-bottom: var(--e5);
}
.sk-loja-cabecalho-logo {
  height: 56px;
  width: auto;
  max-width: 120px;
  object-fit: contain;
  border-radius: var(--sk-radius-sm);
}
/* A foto do obreiro é redonda (mesmo recorte do preview em
   `.sk-foto-obreiro-preview`) e menor que a logomarca da Loja: aqui ela
   identifica, não é o objeto da tela. */
.sk-registro-cabecalho-foto {
  height: 56px;
  width: 56px;
  object-fit: cover;
  border-radius: 50%;
  border: 0.5px solid var(--sk-border-strong);
  background: color-mix(in srgb, var(--sk-fg) 4%, var(--sk-bg));
}
.sk-registro-cabecalho-nome,
.sk-loja-cabecalho-nome {
  font-family: var(--sk-font-display);
  margin: 0;
}
/* Linha de identificação sob o nome (Prompt #187, LOJA-03) — "Cadastro
   28931 · Mestre · Ativo (Regular)". Mesma tipografia de
   `.sk-field-dica`: é contexto, não conteúdo. */
.sk-registro-cabecalho-meta {
  display: block;
  font-size: 0.75rem;
  color: var(--sk-muted);
  margin-top: 2px;
}

.sk-table {
  width: 100%;
  border-collapse: collapse;
  border: 0.5px solid var(--sk-border-strong);
  border-radius: 10px;
  overflow: hidden;
}
/* Cabeçalho de coluna (docs/38 §2.8 item 4, Prompt #109) — negrito e
   levemente MAIOR que a célula de dado, com contraste pleno. Até aqui era
   o inverso (0.6875rem, --sk-muted, peso 500 contra 0.8125rem da célula):
   o cabeçalho lia-se como legenda de rodapé, não como cabeçalho. Regra
   fixa do componente, nunca parametrizável por Tema (§2.6) — é
   legibilidade estrutural, não estética. */
/* `.sk-table` GENÉRICA — fora do Datagrid (a relação de obreiros da
   LOJA-29, por exemplo). Continua na escala --eN do resto do sistema: a
   régua da grade (docs/74 §2.3) é do COMPONENTE, e aplicá-la aqui
   mudaria tabelas que a spec não julgou. */
.sk-table th {
  background: color-mix(in srgb, var(--sk-fg) 4%, var(--sk-bg));
  text-align: left;
  font-size: 0.875rem;
  color: var(--sk-fg);
  padding: var(--e3) var(--e4);
  font-weight: 700;
}
.sk-table td {
  padding: var(--e3) var(--e4);
  border-top: 0.5px solid var(--sk-border);
  font-size: 0.8125rem;
}
/* Alinhamento por tipo_exibicao TAMBÉM no cabeçalho (docs/38 §2.6,
   corrigido no Prompt #109). O `<th>` sempre recebeu a classe da coluna,
   mas ela perdia na cascata: `.sk-table th` (0-1-1) vence
   `.sk-col-numerico` (0-1-0), então o cabeçalho de coluna numérica ficava
   à esquerda enquanto os números ficavam à direita — a spec descrevia um
   comportamento que o CSS não entregava. Estas regras (0-2-1) restauram o
   que o §2.6 sempre afirmou. */
.sk-table th.sk-col-numerico {
  text-align: right;
}
.sk-table th.sk-col-status,
.sk-table th.sk-col-data,
.sk-table th.sk-col-imagem {
  text-align: center;
}
/* Linha de tabela navegável ao clicar (ex: LOJA-15 "Obreiros", Prompt #24)
   — só aplicada quando a linha tem `data-href` (JS cuida da navegação);
   ausente para quem não pode gerenciar, então a linha nunca fica com
   aparência clicável sem realmente ser. */
.sk-table tr[data-href] {
  cursor: pointer;
}
/* Realce de INTERAÇÃO da linha (docs/38 §2.8.3, Prompt #109) — sobe de 4%
   para 12%. A 4% ele perdia para o zebrado de 6%: passar o mouse sobre
   uma linha par *reduzia* o realce. Vale para qualquer `.sk-table`,
   dentro ou fora do Datagrid — o realce de interação é o mesmo no sistema
   inteiro. O degrau da linha zebrada vem logo abaixo, junto do zebrado. */
.sk-table tr[data-href]:hover {
  background: color-mix(in srgb, var(--sk-fg) 12%, var(--sk-bg));
}
.sk-table tr[data-href]:focus-visible {
  outline: 2px solid var(--sk-gold);
  outline-offset: -2px;
}
/* Lançamento em Rascunho no extrato (docs/47 §3.4, Prompt #100) — visão
   do Financeiro: linha esmaecida + selo, nunca misturado silenciosamente
   aos oficiais. */
.sk-linha-rascunho td {
  opacity: 0.55;
  font-style: italic;
}
.sk-selo-rascunho {
  border: 1px solid var(--sk-gray);
  border-radius: 3px;
  padding: 1px var(--e2);
  font-size: 0.6875rem;
  font-style: normal;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--sk-gray);
}
/* Porta 2 do vínculo (docs/48 §2.2, Prompt #103) — link discreto dentro
   da linha clicável do Datagrid de registros (LOJA-19). */
.sk-registro-vincular-quitar {
  margin-left: var(--e2);
  font-size: 0.75rem;
  color: var(--sk-gold);
  white-space: nowrap;
}

/* Componente Datagrid Compartilhado (docs/38) — busca, cabeçalho
   ordenável, paginação. `.sk-tabela-scroll` evita que a tabela quebre o
   layout em telas estreitas (scroll horizontal só na tabela, nunca a
   página inteira) — EXCETO sob cabeçalho fixo, onde ela é neutralizada;
   ver a regra de `--cabecalho-fixo` mais abaixo e o porquê do preço.

   `.sk-datagrid-barra` (a barra superior própria) foi REMOVIDA daqui no
   Prompt #239, junto com o markup: a ação de criação passou a morar na
   linha do "Buscar e filtrar" em toda grade, e não havia mais nada na
   barra. */
.sk-datagrid-busca-campo {
  width: 100%;
  box-sizing: border-box;
  background: color-mix(in srgb, var(--sk-fg) 4%, var(--sk-bg));
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius-sm);
  padding: var(--e2) var(--e3);
  color: var(--sk-fg);
}
.sk-datagrid-exportar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e2);
}
.sk-datagrid-filtros {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e3);
  margin-bottom: var(--e3);
}
.sk-datagrid-filtro-acesso-sistema,
.sk-datagrid-filtro-situacao-cadastral {
  max-width: 220px;
}

/* Filtros da LOJA-36 (docs/61, Prompt #225) — dois intervalos de data,
   um intervalo numérico, dois campos de digitação com filtro e um
   seletor. Fundo/borda/padding já vêm de `.sk-field input`/`select`
   acima (regra genérica, por descendência) — aqui só a largura, que
   precisa ser ESTREITA (um <input> de data/número não deve esticar
   como o campo de busca). */
.sk-datagrid-filtro-obreiro,
.sk-datagrid-filtro-status,
.sk-datagrid-filtro-categoria {
  max-width: 220px;
}
.sk-datagrid-filtro-intervalo-campos {
  display: flex;
  align-items: center;
  gap: var(--e1);
}
.sk-datagrid-filtro-intervalo-campos input {
  width: 130px;
}
.sk-datagrid-filtro-intervalo-campos input[type="number"] {
  width: 100px;
}
/* Filtro de ATRASO da LOJA-58 (docs/88 R7, Prompt #570): DOIS seletores no
   mesmo campo (o número de dias e o formato "N dias ou mais" / "Até N dias").
   O `select` herdava `width: 100%` de `.sk-field` e, dentro do flex sem base,
   encolhia até cortar o texto; o par também não pode quebrar de linha nem
   herdar o teto de 220px dos filtros de uma entrada só. Escopado ao painel de
   filtros: nenhuma tela existente tem `select` dentro de `-campos`. */
.sk-datagrid-painel-filtros .sk-datagrid-filtro-intervalo-campos select {
  min-width: 9.5rem;
}
.sk-datagrid-painel-filtros .sk-datagrid-filtro-atraso {
  flex: 0 0 auto;
  max-width: none;
}
.sk-datagrid-painel-filtros .sk-datagrid-filtro-atraso .sk-datagrid-filtro-intervalo-campos {
  flex-wrap: nowrap;
}

/* Painel de busca e filtros recolhível (docs/38 §2.20, Prompt #224;
   disposição da maquete de 25/08 aplicada no Prompt #239).

   LINHA DE CIMA: o botão que abre o painel, e a ação primária da tela à
   direita. `margin-left: auto` no grupo da direita em vez de
   `space-between`: o botão de filtros não deve esticar nem se deslocar
   quando a tela não tem ação de criação nenhuma — ele fica onde está, e
   só a ação muda de existência. */
.sk-datagrid-barra-filtros {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e2);
  /* `--e4`, não `--e3` (Prompt #241, validação de Wagner). A maquete usa
     `--e3` aqui e MEDIMOS os dois em 9px — não era divergência dela, é
     ajuste pedido sobre ela: os dois botões encostavam no que vem
     abaixo, seja o grid (painel fechado) ou o painel (aberto). Um degrau
     acima na escala do docs/59, nunca um valor solto. */
  margin-bottom: var(--e4);
}
/* A ação primária ENCOSTA na borda direita do grupo (maquete de 25/08,
   item 2 do Prompt #240): `margin-left: auto` come todo o vão livre, e o
   grupo não tem padding próprio — a borda dele é a borda do card, a
   mesma em que a tabela abaixo termina. */
.sk-datagrid-barra-filtros-acoes {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--e2);
}
/* Acao primaria que e um POST (LOJA-27, Prompt #505): o slot de acao do Datagrid
   so desenha link, entao o botao mora no cartao da grade, antes dela, e flutua a
   direita. A barra do "Buscar e filtrar" e um flex (formatador proprio) e se
   estreita ao lado do botao; sem espaco, desce para baixo dele. */
.sk-acao-criar-na-barra {
  float: right;
  margin-left: var(--e2);
}
/* O gatilho é um `<button>` desde o Prompt #239 — era `<summary>` de um
   `<details>`.

   PASSOU A USAR `.sk-btn` no Prompt #240, e esta é a correção do relato
   "fora do padrão visual". Ele nascera com valores PRÓPRIOS de fonte,
   peso, padding e borda — escritos à mão para "não arrastar o padding
   dos botões de ação", uma precaução que a maquete refuta: lá o botão de
   filtros É um `.sk-btn`, igual aos demais. Escrever um botão do zero ao
   lado de outro que usa a classe garante que os dois divirjam.

   O que `.sk-btn` traz e faltava aqui, item a item: `font-size`
   0.8125rem (era 0.875rem), `font-weight` 500 (era 600), `padding`
   `--e3 --e5` (era `--e2 --e3`), o `:hover` padrão do sistema, e
   sobretudo `appearance: none` — sem ela o navegador pinta o widget
   NATIVO do `<button>` por cima do CSS, e o botão fica visivelmente
   diferente do `<a class="sk-btn">` ao lado ("Cadastrar Obreiro") mesmo
   com estilo computado igual. Esse defeito já estava diagnosticado por
   extenso no comentário de `.sk-btn`, e este botão não o herdava.

   Resta aqui só o que a maquete tem a MAIS que um `.sk-btn` comum: o
   arranjo em linha (selo + seta), a altura padronizada e as cores do
   estado inativo/ativo. */
.sk-datagrid-painel-filtros-botao {
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
  /* ALTURA PADRONIZADA (maquete de 25/08). O `.sk-btn` do projeto acha a
     altura pelo padding vertical; a maquete crava `--sk-btn-height` em
     TODO controle de uma linha, e é isso que faz botões, campos e
     seletores se alinharem em vez de quase-se-alinharem. O token já
     existe em `tokens.css` — nada inventado aqui. */
  min-height: var(--sk-btn-height);
  padding: 0.25em var(--e3);
  white-space: nowrap;
  /* COR: vem de `.sk-btn-tertiary` na marcação (Prompt #241) — a mesma
     variante do "Limpar" que este botão abre. Nada de `color`/`border`
     próprios aqui; era justamente o `color: var(--sk-fg)` escrito neste
     bloco que deixava o botão branco ao lado de um "Limpar" cinza. */
}
.sk-datagrid-painel-filtros-botao:focus-visible {
  outline: 2px solid var(--sk-gold);
  outline-offset: 2px;
}
/* Filtro ativo com o painel FECHADO: o selo diz quantos, a borda dourada
   diz que há. Sem isto, um painel fechado filtrando registros se parece
   com um painel fechado sem filtro nenhum — e o selo sozinho é pequeno
   demais para carregar esse peso. */
.sk-datagrid-painel-filtros-botao--ativo {
  border-color: var(--sk-gold);
  color: var(--sk-gold);
}
.sk-datagrid-painel-filtros-seta {
  font-size: 0.6875rem;
  color: var(--sk-muted);
}
.sk-datagrid-painel-filtros-botao[aria-expanded="true"] .sk-datagrid-painel-filtros-seta {
  /* Rotaciona em vez de trocar o glifo: um caractere só, e a transição
     acompanha a abertura do painel. */
  transform: rotate(180deg);
}
/* LINHA DE BAIXO: o painel em si. */
.sk-datagrid-painel-filtros {
  border: 0.5px solid var(--sk-border);
  border-radius: var(--sk-radius-sm);
  margin-bottom: var(--e3);
  padding: 0 var(--e3);
}
.sk-datagrid-painel-filtros[hidden] {
  display: none;
}
/* O CABEÇALHO DE `<summary>` CONTINUA AQUI, e não é resíduo: a LOJA-29
   (`detalhe_lote_cobranca.html`, Prompt #227) reaproveita
   `.sk-datagrid-painel-filtros` + este cabeçalho para um `<details>`
   PRÓPRIO dela — a relação de obreiros selecionados —, que não é o
   painel de busca e filtros do componente e não passou a botão no
   Prompt #239. Apagar estas regras junto com a conversão do componente
   deixaria aquele `<summary>` sem estilo nenhum, com o marcador nativo
   de volta. */
.sk-datagrid-painel-filtros-cabecalho {
  display: flex;
  align-items: center;
  gap: var(--e2);
  padding: var(--e2) 0;
  cursor: pointer;
  font-size: 0.875rem;
  font-weight: 600;
  list-style: none;
}
.sk-datagrid-painel-filtros-cabecalho::-webkit-details-marker {
  display: none;
}
/* Seta própria, no lugar do marcador nativo removido acima — aponta para
   baixo fechado, para cima aberto (mesmo idioma visual do menu principal,
   docs/20 §7.1). */
.sk-datagrid-painel-filtros-cabecalho::before {
  content: "▸";
  color: var(--sk-muted);
}
.sk-datagrid-painel-filtros[open] > .sk-datagrid-painel-filtros-cabecalho::before {
  content: "▾";
}
/* O selo é OBRIGATÓRIO (docs/59 §6, docs/38 §2.20) — sem ele, um painel
   fechado com filtro ativo por padrão (ex: Situação Cadastral = Ativo na
   LOJA-15) mostraria uma contagem parcial como se fosse o total, sem
   nenhum sinal de que há filtro escondendo registros. */
.sk-selo-filtros-ativos {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Mesma correção do `.sk-help-icone` (Prompt #291): `min-width` já
     estava certo, `height` não. O selo é uma pílula, não um círculo - não
     precisa de `aspect-ratio`, só pode crescer. */
  min-width: 1.25em;
  min-height: 1.25em;
  padding: 0 0.35em;
  border-radius: 999px;
  background: var(--sk-gold);
  color: var(--sk-bg);
  font-size: 0.6875rem;
  font-weight: 700;
}
/* MESMA LINHA DE BASE para busca e filtros (maquete de 25/08, Prompt
   #239): `align-items: flex-end` alinha pelo fundo dos controles, e o
   rótulo de cada um sobe por cima — que é o arranjo da maquete. Antes o
   campo de busca não tinha `<label>` e por isso ficava mais baixo que os
   dois seletores ao lado, parecendo uma linha própria. */
.sk-datagrid-painel-filtros-conteudo {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--e3);
  /* O padding vertical mora AQUI, não no painel: o painel é
     compartilhado com o `<details>` da LOJA-29, que tem `<summary>` com
     padding próprio antes deste bloco. */
  padding: var(--e3) 0;
}
.sk-datagrid-painel-filtros-conteudo .sk-datagrid-filtros {
  margin-bottom: 0;
}
/* Os filtros da tela chegam dentro do seu próprio `.sk-datagrid-filtros`
   (um parcial por tela, docs/38 §2.13). `display: contents` dissolve
   esse invólucro para que os campos participem da MESMA linha flex da
   busca — sem ele haveria dois níveis de flex e a busca nunca
   compartilharia a linha de base com os seletores. */
.sk-datagrid-painel-filtros-conteudo > .sk-datagrid-filtros {
  display: contents;
}
.sk-datagrid-painel-filtros-conteudo .sk-field {
  margin: 0;
}
/* MESMA ALTURA PARA TODOS OS CONTROLES DA LINHA (maquete de 25/08, item
   3 do Prompt #240). Sem isto os campos ficavam em `--e2` de padding
   vertical e o botão "Limpar" em `--e3` (herdado de `.sk-btn`): ~6px de
   diferença de altura, alinhados pelo fundo por `align-items: flex-end`
   — o que deixa o topo do botão acima do topo dos campos. Alinhado pelo
   fundo E de altura diferente é justamente o que se lê como
   "desalinhado".

   A maquete resolve cravando `--sk-btn-height` em todo controle da
   linha, e é o que se faz aqui. O token já existe em `tokens.css`. */
.sk-datagrid-painel-filtros-conteudo .sk-field input,
.sk-datagrid-painel-filtros-conteudo .sk-field select,
.sk-datagrid-painel-filtros-conteudo .sk-datagrid-busca-campo {
  /* `min-height`, não `height` (Prompt #291) - estes controles já têm
     padding próprio; o token entra só como piso, para a linha continuar
     alinhada. */
  min-height: var(--sk-btn-height);
}
/* "Limpar" na EXTREMA DIREITA do painel (maquete de 25/08), alinhado com
   os campos ao lado pela mesma altura. */
.sk-datagrid-painel-filtros-limpar {
  margin-left: auto;
  min-height: var(--sk-btn-height);
  display: inline-flex;
  align-items: center;
  padding: 0.25em var(--e3);
  /* ALINHAMENTO QUE NÃO DEPENDE DE O VIZINHO TER RÓTULO (Prompt #241,
     V4). O container alinha por `align-items: flex-end`, e isso só põe
     este botão na linha dos campos porque os campos ao lado são mais
     ALTOS que ele — label em cima, controle embaixo. Um controle sem
     rótulo colocado nesta linha (este botão, um checkbox, um ícone)
     depende, para ficar certo, de que ALGUÉM ao lado tenha rótulo.

     `align-self: flex-end` declara a intenção no próprio elemento: ele
     se alinha pelo fundo da linha, tenha ou não vizinho com rótulo.
     Vale para o componente inteiro — é a regra para todo controle sem
     rótulo que entrar nesta linha. */
  align-self: flex-end;
}
/* "Exibir Contas inativas" (LOJA-08, `_filtros_contas.html`) — relato de
   Wagner (B.02, Prompt #271): checkbox desalinhado verticalmente com a
   linha. Era exatamente o caso que o comentário acima já previa por
   nome ("este botão, um checkbox, um ícone") sem que este controle
   tivesse recebido o tratamento: faltava `align-self: flex-end` (o
   filtro tinha `.sk-field`, mas nunca `.sk-field-checkbox`, e por isso
   não herdava o alinhamento de nenhuma das duas regras). O `label`
   também ganha o mesmo flex de `#id_widgets label` acima — o checkbox
   fica DENTRO do `<label>` aqui (associação nativa por aninhamento, sem
   `for`/`id` a manter em sincronia), e sem `align-items: center` no
   próprio `label` o widget e o texto não compartilham a mesma linha de
   base (docs/14, princípio 12). */
.sk-datagrid-filtro-incluir-inativas {
  align-self: flex-end;
}
/* Especificidade igual à de `.sk-field label:not(.sk-btn)` (Princípio 29)
   — precisa do prefixo `.sk-field` explícito, não só a classe específica
   deste filtro, senão a regra genérica de rótulo (display:block,
   margin-bottom) continua vencendo por especificidade e o label nunca
   vira flex de verdade (medido: 46px de altura em vez de uma linha só). */
.sk-field.sk-datagrid-filtro-incluir-inativas label {
  display: flex;
  align-items: center;
  gap: var(--e2);
  margin-bottom: 0;
}
/* O campo de busca é o que pode esticar; os seletores ao lado têm
   largura própria (`max-width` nas classes de filtro). */
.sk-datagrid-busca {
  flex: 1 1 250px;
  max-width: 320px;
}
/* ---------------------------------------------------------------------
   Pilotos do Datagrid (docs/38 §2.16, Prompt #135) — em piloto na LOJA-19.
   Todos ligados por classe no contêiner, derivada de flag na config: uma
   grade sem a classe renderiza exatamente como antes.

   ACHADO que definiu a implementação do cabeçalho fixo: neste layout o
   cabeçalho do sistema NÃO sobrepõe o conteúdo. Quem rola é `.sk-main`
   (`overflow-y: auto`), que fica ENTRE o header e o footer — os dois já
   estão fora da área que rola. Não há, portanto, altura de cabeçalho a
   medir nem a descontar: "parar abaixo do cabeçalho" é onde o conteúdo já
   começa.

   O obstáculo real era outro: `.sk-tabela-scroll` declara
   `overflow-x: auto`, e pela especificação isso faz o eixo Y computar
   `auto` também — o elemento vira o scrollport mais próximo, e um
   `position: sticky` dentro dele passa a se ancorar numa caixa que não
   rola verticalmente, ficando parado sem nunca "grudar".

   O PILOTO RESOLVIA ISSO DANDO À GRADE UMA ÁREA DE ROLAGEM PRÓPRIA
   (`max-height: 60vh; overflow: auto`), E ERA ESSA A SOLUÇÃO ERRADA —
   revertida no Prompt #239, contra a maquete aprovada em 25/08.

   Duas consequências, e as duas apareceram em tela:

   1. DUAS BARRAS DE ROLAGEM. `.sk-main` já rola (o layout do Prompt #60
      põe cabeçalho e rodapé fora da área rolável), então a grade rolando
      por dentro dá ao usuário dois cilindros concêntricos: rola um, o
      outro não anda.
   2. UMA LINHA CORTADA sob o cabeçalho. Com o scrollport em
      `.sk-tabela-scroll`, o `<thead>` gruda no topo DELE — e o topo dele
      não coincide com o topo da área visível, então sobra uma faixa de
      linha aparecendo por baixo.

   A CORREÇÃO É NÃO TER SCROLLPORT NENHUM AQUI: o `<thead>` passa a se
   ancorar ao scrollport de verdade, que é o `.sk-main`. Nesse layout o
   topo do `.sk-main` é exatamente onde o conteúdo começa, logo abaixo do
   cabeçalho fixo do sistema — "grudar na janela", da maquete, é isto
   aqui, porque a janela do produto não rola (`body { overflow: hidden }`).

   PREÇO ACEITO, e ele é real: sem `overflow-x: auto` sob cabeçalho fixo,
   uma tabela mais larga que a tela deixa de ter rolagem horizontal
   própria. Não há meio-termo — a especificação NÃO permite
   `overflow-x: auto` com `overflow-y: visible`; pedir isso computa
   `auto` nos dois e o scrollport volta. Entre uma tabela larga sem
   rolagem lateral e um cabeçalho fixo que não gruda, a maquete escolheu
   o cabeçalho. */
.sk-datagrid--cabecalho-fixo .sk-tabela-scroll {
  overflow: visible;
}
/* A CAUSA REAL de o sticky não funcionar (Prompt #136).

   `.sk-table` declara `overflow: hidden` (para o `border-radius` recortar
   os cantos). `overflow` diferente de `visible` cria um scrollport, e um
   `position: sticky` se ancora ao scrollport MAIS PRÓXIMO — que passava a
   ser a própria <table>, não o wrapper com a área de rolagem. Como a
   tabela nunca rola dentro de si mesma, o cabeçalho não tinha do que
   grudar: rolava junto e era recortado pela borda do wrapper, que é
   exatamente o sintoma relatado.

   O Prompt #135 achou um obstáculo verdadeiro (`.sk-tabela-scroll` com
   `overflow-x: auto`) e o removeu, mas parou no wrapper e não olhou a
   própria tabela — havia DOIS ancestrais com overflow, e neutralizar um
   só não muda nada. */
.sk-datagrid--cabecalho-fixo .sk-table {
  overflow: visible;
  /* Sem `overflow` não há o que recortar, então o raio da tabela não
     teria efeito nenhum — e o `.sk-card` ao redor já arredonda o
     conjunto, que é o desenho da maquete. Zerado explicitamente para
     que a regra genérica de `.sk-table` não deixe um raio inerte aqui.
     (Até o Prompt #239 o raio vivia no wrapper, que recortava; o
     wrapper deixou de recortar quando deixou de ser scrollport.) */
  border-radius: 0;
}
.sk-datagrid--cabecalho-fixo thead th {
  position: sticky;
  /* NÃO É ZERO, e o motivo é medido (Prompt #242, V11).
   *
   * Com `top: 0` o cabeçalho parava no topo do CONTENT box do `.sk-main`
   * (y=77 na medição), enquanto o scrollport visível começa no PADDING
   * box (y=65). Os 12px de diferença são o `padding-top: var(--e4)` do
   * próprio `.sk-main` — e essa faixa é área VISÍVEL por onde o conteúdo
   * rola. Resultado: uma linha de dados aparecia acima da linha de
   * títulos, e a seguinte ficava meio coberta. Medido rolando de
   * verdade: em scrollTop=320, a linha 3 ocupava y=39..78, com 12px dela
   * expostos acima do cabeçalho.
   *
   * `-var(--e4)` faz o cabeçalho grudar 12px mais acima, exatamente no
   * topo do padding box: faixa de 12px para 0px, medido, e o cabeçalho
   * continua inteiro (41px de altura, 41px visíveis).
   *
   * ACOPLAMENTO DECLARADO, porque ele é real e ninguém adivinharia: este
   * valor É o `padding-top` do `.sk-main`. Os dois usam `--e4` de
   * propósito — se aquele padding mudar de degrau, este precisa mudar
   * junto, e o defeito voltaria em silêncio.
   *
   * A MAQUETE NÃO PODIA TER REVELADO ISTO: lá quem rola é a janela, sem
   * padding no topo, então `top: 0` basta. O defeito nasce do layout do
   * Prompt #60 (rolagem no `.sk-main`), que a maquete não reproduz. */
  top: calc(-1 * var(--e4));
  z-index: 2;
  /* Fundo OPACO obrigatório: sem ele as linhas passariam por trás do
     cabeçalho e continuariam legíveis através dele.

     ATENÇÃO (Prompt #148): DENTRO do Datagrid esta declaração NÃO é a que
     vence. `.sk-datagrid .sk-table th` (0-2-1) sobrepõe esta regra
     (0-1-2), e era lá que o alfa entrava — o defeito viveu meses atrás de
     um comentário que afirmava a garantia no lugar errado. A opacidade do
     componente está garantida lá, em camadas; esta declaração serve à
     tabela que use `--cabecalho-fixo` FORA de um `.sk-datagrid`.

     `color-mix` só resulta opaco porque `--sk-fg` e `--sk-bg` são opacos
     nos quatro temas — se um dia um deles ganhar alfa, o resultado herda
     o alfa, e há teste cobrindo isso. */
  background: color-mix(in srgb, var(--sk-fg) 4%, var(--sk-bg));
}

/* Borda e respiro que separam o rodapé da última linha da grade.
   `.sk-datagrid-rodape-controles` e `.sk-datagrid-barra-controles` foram
   removidos daqui no Prompt #239 junto com o markup: a ação de criação
   deixou de trocar de ponta conforme `controles_no_rodape` e passou a
   morar sempre na linha do "Buscar e filtrar" (§2.17). */
.sk-datagrid-paginacao--completa {
  border-top: 0.5px solid var(--sk-border);
  padding-top: var(--e3);
}

/* Ação de criação da tela (docs/38 §2.17, Prompt #138) — desde o Prompt
   #239 mora sempre à direita da linha do botão "Buscar e filtrar".
   `flex: none` porque o grupo é flex: sem isto o botão seria o item que
   cede espaço, e o rótulo quebraria em duas linhas antes de qualquer
   outra coisa encolher um pixel.

   `white-space: nowrap` pela mesma razão, para o caso de a linha
   quebrar: "Gerar novo lote" partido ao meio muda a altura da linha
   inteira. */
.sk-datagrid-acao-criar {
  flex: none;
  white-space: nowrap;
  /* Mesma altura do botão "Buscar e filtrar" ao lado (maquete de 25/08,
     Prompt #240) — os dois dividem a linha, e altura diferente entre
     eles é a primeira coisa que se nota. */
  min-height: var(--sk-btn-height);
  display: inline-flex;
  align-items: center;
  padding: 0.25em var(--e5);
}

/* Ações por linha só em evidência. `visibility`, nunca `display: none`:
   o espaço fica RESERVADO, senão a linha mudaria de largura ao passar o
   mouse e a tabela inteira dançaria.

   `:focus-within` não é refinamento — sem ele a ação simplesmente não
   existe para quem navega por teclado, já que o foco chega ao link sem
   que ponteiro nenhum passe por ali. */
.sk-datagrid--acoes-em-evidencia tbody td .sk-registro-vincular-quitar {
  visibility: hidden;
  opacity: 0;
  transition: opacity 120ms ease-in-out;
}
.sk-datagrid--acoes-em-evidencia tbody tr:hover .sk-registro-vincular-quitar,
.sk-datagrid--acoes-em-evidencia tbody tr:focus-within .sk-registro-vincular-quitar {
  visibility: visible;
  opacity: 1;
}

.sk-tabela-scroll {
  overflow-x: auto;
}

/* =====================================================================
   TELA DE GRADE LARGA (docs/14 Princípio 47.1, Prompt #334)
   =====================================================================

   **A DECLARAÇÃO É DO TEMPLATE, e é marca de COMPORTAMENTO** - a tela
   escreve `sk-body--grade-larga` no bloco `body_modifier` do `base.html`.

   Ler o código da tela (`LOJA-53`) ou o nome do template para adivinhar
   faria a próxima grade larga **nascer estreita sem que nada acusasse**, e
   quem a escrevesse não teria como saber que existia uma lista para
   entrar. É a mesma régua da `R2.25-5.1` do `docs/38`.

   **Por que no `<html>` e não no `<body>` nem no `.sk-main`:** as duas
   regras abaixo precisam alcançar ANCESTRAIS do `.sk-main`, e CSS não
   sobe. E entre os dois ancestrais, é o ELEMENTO RAIZ que governa - o
   `overflow` do `<body>` **propaga para o viewport** em vez de contê-lo.

   **Medido no Prompt #334, e a primeira tentativa foi no `<body>`:** com
   `height: 100vh; overflow: hidden` ali, o body ficava contido
   (`scrollHeight` 900) **e a página continuava rolando** - um
   `window.scrollTo(0, 500)` devolvia `scrollY > 0`. A medição que
   revelou isso não foi `scrollHeight > clientHeight` (que mede altura
   POTENCIAL); foi tentar rolar e ler de volta.

   -------------------------------------------------------------------
   (1) UMA BARRA DE ROLAGEM, NUNCA DUAS - docs/72 §6, R2.25-1
   -------------------------------------------------------------------
   **Medido no Prompt #334, ANTES desta regra:** a grade rolava por dentro
   (`scrollTopMax: 825`) **e a página rolava por trás** (`scrollTopMax:
   586`). Duas barras, e nenhuma fazendo o que o usuário espera - o
   cabeçalho de mês some quando ele rola a de fora, que é exatamente o que
   a §2.25 existe para impedir.

   A cadeia: o `body` toma a altura da janela e não rola; o `.sk-main`
   toma o que sobra e passa a régua adiante para o card e para a grade,
   que já sabem encolher (`flex: 0 1 auto` + `min-height: 0`, R2.25-2).

   🔴 **O `padding` do `.sk-main` NÃO é tocado aqui, e não é descuido.** O
   `top` do cabeçalho fixo é `calc(-1 * var(--e4))`, calculado a partir do
   `padding-top` dele (`docs/38` §2.16, acoplamento "real e deliberado").
   Mexer num sem o outro reabre a faixa por onde o conteúdo rola acima do
   cabeçalho - **e isso já reprovou no Prompt #333**. */
html.sk-grade-larga,
html.sk-grade-larga body {
  height: 100%;
  overflow: hidden;
}
html.sk-grade-larga body {
  display: flex;
  flex-direction: column;
}
html.sk-grade-larga .sk-main {
  flex: 1 1 auto;
  min-height: 0;
}

/* -------------------------------------------------------------------
   (2) A LARGURA NO ESTADO EXPANDIDO - Princípio 47.1
   -------------------------------------------------------------------
   O Princípio 27 limita a área útil a `min(80%, 1440px)`, e **em tela
   cheia os dois princípios colidem**: o 27 é CSS e vence, o 47 é intenção
   e perde. O Prompt #333 mediu o resultado - grade de **1070 px** numa
   janela de **1440 px**, e o número **não mudava** em tela cheia.

   **A exceção vale só no estado expandido, e só na tela que declara.**
   Toda outra tela continua em `min(80%, 1440px)` em qualquer estado - o
   Princípio 27 tem razão nas telas de texto, onde linha de 1440 px é
   ilegível. Liberar para todas resolveria o Mapa e estragaria os
   formulários.

   Os DOIS estados entram, e por seletores diferentes porque os mecanismos
   são diferentes: `:fullscreen` é o pseudo-estado que o navegador aplica
   ao elemento em tela cheia (modo a); `.sk-expandido` é a classe que o
   `tela-cheia.js` põe no elemento raiz quando a Fullscreen API não existe
   ou é recusada (modo b). **Os dois no mesmo lugar de propósito** -
   separá-los faria um deles envelhecer sozinho. */
html.sk-grade-larga:fullscreen .sk-main,
html.sk-grade-larga.sk-expandido .sk-main {
  width: 100%;
}

/* ---------------------------------------------------------------------
   ROLAGEM PRÓPRIA DA GRADE NOS DOIS EIXOS (docs/38 §2.25, Prompt #305)

   A grade passa a ser o scrollport, dimensionada para o espaço que sobra.
   **Ligada por tela**, pelo modificador `.sk-main--grade-rola` que o
   template da tela declara no bloco `main_modifier` do `base.html` — as
   demais grades não mudam de comportamento por causa deste bloco.

   POR QUE UM MODIFICADOR NO `.sk-main`, e não `:has()`: a regra precisa
   TIRAR a rolagem de um ANCESTRAL do Datagrid, e CSS não sobe. `:has()`
   resolveria, e a decisão foi por opt-in explícito no template — a
   guarda de unicidade lê o CSS estático, e um seletor condicional
   tornaria a contagem de scrollports dependente do DOM em vez do estilo.

   R2.25-1 — a grade é o ÚNICO scrollport da tela. `.sk-main` deixa de
   rolar, e deixa de rolar por `visible`, NUNCA por `hidden`: `overflow:
   hidden` também cria um scroll container pela especificação, e o sticky
   se ancoraria nele. Seriam dois, e a guarda de unicidade acusaria — com
   razão. */
.sk-main--grade-rola {
  overflow: visible;
  display: flex;
  flex-direction: column;
}
/* R2.25-2 — `flex: 0 1 auto` com `min-height: 0`, e SEM piso em px.
   Grade curta fica do tamanho da tabela (sem moldura vazia); grade longa
   ocupa o que sobra. O piso foi tentado na maquete e retirado por
   medição: `flex: 1` com piso de 140px transformava uma grade de três
   linhas numa caixa de 292px com 160px de vazio dentro (§2.16,
   refinamento de 06/09). */
/* O `.sk-card` ENTRE o `.sk-main` e a grade, e ele não é detalhe: medido
   no Prompt #305, a primeira versão desta regra deixou a grade com 3137px
   dentro de um `.sk-main` de 671px, sem rolar nada.

   `flex` só governa FILHO DIRETO. O item de flex aqui é o card, não a
   grade - e o card nascia `display: block` com `min-height: auto`, que é
   o tamanho mínimo automático: ele se recusa a encolher abaixo do próprio
   conteúdo, e a cadeia inteira de `flex: 0 1 auto` abaixo dele não tem
   efeito nenhum.

   `:has()` para alcançar SÓ o card que contém a grade: a tela tem outros
   cards (Dados Cadastrais, filtros), e transformá-los todos em coluna
   flexível encolhível mudaria coisas que este prompt não deve tocar. */
.sk-main--grade-rola > .sk-card:has(.sk-datagrid--cabecalho-fixo) {
  display: flex;
  flex-direction: column;
  flex: 0 1 auto;
  min-height: 0;
}
.sk-main--grade-rola .sk-datagrid--cabecalho-fixo {
  display: flex;
  flex-direction: column;
  flex: 0 1 auto;
  min-height: 0;
}
.sk-main--grade-rola .sk-datagrid--cabecalho-fixo .sk-tabela-scroll {
  overflow: auto;
  flex: 0 1 auto;
  min-height: 0;
}
/* R2.25-11 — a tabela deixa de ser forçada a caber, e o eixo horizontal
   passa a existir de verdade.

   O `.sk-table { width: 100% }` da regra base é de 2026 e nasceu como
   remédio para a AUSÊNCIA de rolagem horizontal: uma tabela mais larga que
   a área visível quebrava o layout porque não havia para onde rolar. Com o
   eixo de volta (R2.25-1), o remédio perdeu a doença — e continuava fazendo
   a tabela caber sempre. Medido no Prompt #306, em oito telas a 1440x900:
   largura da tabela IDÊNTICA à do scrollport, 1070px em todas.

   **A regra base NÃO muda (R2.25-11.1).** O `width: 100%` continua
   governando toda outra tabela do sistema; o que existe aqui é uma exceção
   nomeada, sob o mesmo modificador que liga a capacidade. `width: auto` com
   `min-width: 100%`: a tabela toma a largura do conteúdo quando ele excede,
   e continua preenchendo a porta quando não excede — sem o `min-width` uma
   grade de poucas colunas encolheria e deixaria faixa vazia à direita.

   **Larguras mínimas por tipo de coluna foram avaliadas e recusadas por
   Wagner**, com o argumento na R2.25-11: o risco da largura natural (uma
   coluna de texto longo esticando a tabela) deixa de custar orientação
   agora que a identidade fica congelada à esquerda. O Irmão rola e não se
   perde, que era o problema real por trás do "quebra o layout". */
.sk-main--grade-rola .sk-datagrid--cabecalho-fixo .sk-table {
  width: auto;
  min-width: 100%;
}
/* R2.25-3 — cabeçalho a `top: 0`, SEM compensação, e é aqui que a
   capacidade paga por si.

   A regra geral usa `top: calc(-1 * var(--e4))` porque o sticky se
   ancora ao `.sk-main`, cujo `padding-top` é área visível por onde o
   conteúdo rolava acima do cabeçalho (medido no #242). Com a grade como
   scrollport, o `.sk-tabela-scroll` NÃO tem padding — não há faixa a
   compensar, e o acoplamento que a §2.16 chamava de "real e deliberado",
   com o aviso de que mudar um degrau traria o defeito de volta em
   silêncio, simplesmente DEIXA DE EXISTIR nesta tela.

   z-index 3: camada de cabeçalho e de total (R2.25-7). */
.sk-main--grade-rola .sk-datagrid--cabecalho-fixo thead th {
  top: 0;
  z-index: 3;
  /* Fundo opaco NA MESMA REGRA que declara a camada, e não numa regra
     vizinha: é a lição do #148 (`garantia declarada na regra errada não
     é garantia`), e a guarda
     `test_nenhum_elemento_sobreposto_ficou_sem_fundo_opaco` reprovou a
     primeira versão deste bloco exatamente por isso. As três camadas são
     as mesmas de `.sk-datagrid .sk-table th`, repetidas aqui porque esta
     regra tem especificidade maior e venceria aquela. */
  background-color: var(--sk-bg);
  background-image:
    linear-gradient(
      var(--sk-datagrid-cabecalho-bg),
      var(--sk-datagrid-cabecalho-bg)
    ),
    linear-gradient(var(--sk-surface), var(--sk-surface));
}
/* R2.25-7 — as camadas são QUATRO, e a quarta só existe porque os dois
   eixos congelam:

     0  célula normal do corpo
     2  célula congelada do corpo (esquerda e direita)
     3  célula normal de cabeçalho
     4  os CANTOS — congelada e presa ao mesmo tempo

   Sem a camada 4 o canto empata com uma das outras e a ordem do DOM
   decide qual aparece: o canto piscaria conforme a rolagem. Grade que
   congela num eixo só nunca encontra isto. */
.sk-main--grade-rola .sk-datagrid--cabecalho-fixo tbody td:first-child,
.sk-main--grade-rola .sk-datagrid--cabecalho-fixo tbody td:last-child {
  position: sticky;
  z-index: 2;
  background-color: var(--sk-bg);
  background-image: linear-gradient(var(--sk-surface), var(--sk-surface));
}
.sk-main--grade-rola .sk-datagrid--cabecalho-fixo tbody td:first-child,
.sk-main--grade-rola .sk-datagrid--cabecalho-fixo thead th:first-child {
  left: 0;
}
.sk-main--grade-rola .sk-datagrid--cabecalho-fixo tbody td:last-child,
.sk-main--grade-rola .sk-datagrid--cabecalho-fixo thead th:last-child {
  right: 0;
}
.sk-main--grade-rola .sk-datagrid--cabecalho-fixo thead th:first-child,
.sk-main--grade-rola .sk-datagrid--cabecalho-fixo thead th:last-child {
  z-index: 4;
  /* Fundo opaco NA MESMA REGRA que declara a camada, e não numa regra
     vizinha: é a lição do #148 (`garantia declarada na regra errada não
     é garantia`), e a guarda
     `test_nenhum_elemento_sobreposto_ficou_sem_fundo_opaco` reprovou a
     primeira versão deste bloco exatamente por isso. As três camadas são
     as mesmas de `.sk-datagrid .sk-table th`, repetidas aqui porque esta
     regra tem especificidade maior e venceria aquela. */
  background-color: var(--sk-bg);
  background-image:
    linear-gradient(
      var(--sk-datagrid-cabecalho-bg),
      var(--sk-datagrid-cabecalho-bg)
    ),
    linear-gradient(var(--sk-surface), var(--sk-surface));
}
/* R2.25-4 / R2.25-5 / R2.25-5.1 — A LINHA DE TOTAL PRESA EMBAIXO
   (Prompt #334; especificada em 06/09 e não implementada até aqui - o
   Prompt #330 mediu a ausência e a prendeu por teste declarado).

   **R2.25-5: comportamento DERIVADO, não flag.** A condição é dupla e
   está inteira no seletor: a grade **é dona do scrollport**
   (`.sk-main--grade-rola`) **E** o rodapé **declarou que é um total**
   (`.sk-datagrid-tfoot--total`). Nenhum flag novo no `DatagridConfig` -
   *"flag que nasce sem exceção prevista é inventário"*.

   **R2.25-5.1: o componente NÃO sabe distinguir total de legenda, e a
   regra depende disso.** Medido no Prompt #334: **CINCO** telas usam o
   `<tfoot>` - `LOJA-08` (Contas) e `LOJA-17` (Subcontas) com total,
   `LOJA-24` (Agenda) e `LOJA-26` (Sementes) com LEGENDA, e a `LOJA-53`
   (Mapa Anual) com total. Uma regra escrita como "todo `<tfoot>` prende"
   alcançaria as cinco e estaria **errada em duas** - legenda não tem por
   que ficar fixa. Por isso o rodapé DECLARA o que é, e o componente lê a
   declaração; ler o nome do template para adivinhar seria o defeito do
   `TipoSessao.e_recesso` por outra porta.

   **R2.25-4: fundo OPACO**, herdado do #148 - o corpo passa a rolar POR
   TRÁS dela, e fundo translúcido põe número por cima de número. As três
   camadas são as mesmas do cabeçalho, repetidas aqui porque esta regra
   tem especificidade maior e venceria aquela.

   `z-index: 3` = camada de cabeçalho e de total (R2.25-7). */
.sk-main--grade-rola
  .sk-datagrid--cabecalho-fixo
  tfoot.sk-datagrid-tfoot--total
  td {
  position: sticky;
  bottom: 0;
  z-index: 3;
  background-color: var(--sk-bg);
  background-image:
    linear-gradient(
      var(--sk-datagrid-cabecalho-bg),
      var(--sk-datagrid-cabecalho-bg)
    ),
    linear-gradient(var(--sk-surface), var(--sk-surface));
}
/* R2.25-7, a camada 4 — OS CANTOS, congelados e presos ao mesmo tempo.
   Sem ela o canto empata com uma das outras e a ordem do DOM decide qual
   aparece: o total PISCARIA conforme a rolagem. É o caso que só existe
   porque agora os DOIS eixos congelam.

   🔴 **O fundo é repetido AQUI, e não herdado da regra acima.** A
   primeira versão declarava só o `z-index`, e a guarda do #148
   (`test_nenhum_elemento_sobreposto_ficou_sem_fundo_opaco`) reprovou com
   `td:last-child (sem background)` - **com razão**. A lição do #148 é
   literal: *"garantia declarada na regra errada não é garantia"*. Aqui o
   fundo até viria pela cascata, porque é o mesmo elemento; mas a guarda
   verifica a CLASSE, não a instância, e quem escrever a próxima regra de
   camada não terá como saber que o fundo dependia de outra. */
.sk-main--grade-rola
  .sk-datagrid--cabecalho-fixo
  tfoot.sk-datagrid-tfoot--total
  td:first-child,
.sk-main--grade-rola
  .sk-datagrid--cabecalho-fixo
  tfoot.sk-datagrid-tfoot--total
  td:last-child {
  z-index: 4;
  background-color: var(--sk-bg);
  background-image:
    linear-gradient(
      var(--sk-datagrid-cabecalho-bg),
      var(--sk-datagrid-cabecalho-bg)
    ),
    linear-gradient(var(--sk-surface), var(--sk-surface));
}
/* 🔴 O EIXO HORIZONTAL do canto do rodape (Prompt #444).

   **A celula foi declarada canto na CAMADA e nunca foi ancorada na
   POSICAO.** A regra acima da `z-index: 4` e o fundo opaco; a regra do
   `tfoot` da `position: sticky` e `bottom: 0`. **Nenhuma das duas declara
   `left` nem `right`** - entao o rotulo `Total do exercicio` e o total do
   `Saldo Atual` grudavam embaixo e escorregavam para o lado, enquanto toda
   linha do corpo mantinha identidade e veredito. Sintoma relatado por Wagner
   na `LOJA-53`.

   **Separadas como o corpo ja faz** (`tbody`/`thead`, acima), e nao juntas
   numa regra so: e a mesma leitura do `#148` - *"garantia declarada na regra
   errada nao e garantia"* -, e ela vale nos dois sentidos. A camada 4 da
   `R2.25-7` exige AS DUAS ancoragens, e declarar uma sem a outra produz canto
   que escorrega. */
.sk-main--grade-rola
  .sk-datagrid--cabecalho-fixo
  tfoot.sk-datagrid-tfoot--total
  td:first-child {
  left: 0;
}
.sk-main--grade-rola
  .sk-datagrid--cabecalho-fixo
  tfoot.sk-datagrid-tfoot--total
  td:last-child {
  right: 0;
}

/* R2.25-8 — o zebrado entra TAMBÉM nas colunas congeladas, senão elas
   viram uma tira lisa correndo sobre linhas listradas.

   E NUNCA por `opacity`: opacity vale para o elemento inteiro, fundo
   incluso, e célula congelada semitransparente é janela para o que rola
   atrás — é o #148 por outra porta. A opacidade é propriedade da REGRA,
   nas mesmas três camadas que o #148 fixou para o cabeçalho: base opaca,
   a superfície do card por cima, e o tom por último. Nenhum Tema novo e
   nenhum Master editando cor na MASTER-08 reintroduz o vazamento. */
.sk-main--grade-rola
  .sk-datagrid--cabecalho-fixo
  tbody
  tr:nth-child(even)
  td:first-child,
.sk-main--grade-rola
  .sk-datagrid--cabecalho-fixo
  tbody
  tr:nth-child(even)
  td:last-child {
  background-image:
    linear-gradient(
      color-mix(in srgb, var(--sk-fg) 6%, transparent),
      color-mix(in srgb, var(--sk-fg) 6%, transparent)
    ),
    linear-gradient(var(--sk-surface), var(--sk-surface));
}
/* Resumo em grade de três colunas (LOJA-47, Prompt #288 Fase 3).

   Wagner especificou o cabeçalho de conferência em DUAS LINHAS DE TRÊS —
   "Conta · Período · Saldo final" e "Transações · Já importadas · A
   conferir" —, e uma `<dl>` empilhada não entrega isso: a leitura de
   conferência é comparar de relance, e para isso os seis precisam caber
   na mesma altura de olho.

   O `<div>` em volta de cada par existe porque `display: grid` numa `<dl>`
   trataria `<dt>` e `<dd>` como células independentes, e rótulo e valor
   cairiam em colunas diferentes. Agrupá-los é o que mantém o par junto —
   e continua sendo `<dl>` válido: o HTML permite `<div>` envolvendo cada
   par desde o HTML 5.2, e é o que preserva a semântica para o leitor de
   tela.

   Colapsa para uma coluna em tela estreita, pelo mesmo motivo da
   `.sk-form-grade-dupla` abaixo: três colunas em 360px não são três
   colunas. */
.sk-resumo-grade {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--e4);
  margin: 0 0 var(--e5) 0;
}
.sk-resumo-grade dt {
  color: var(--sk-muted);
  font-size: 0.875em;
}
.sk-resumo-grade dd {
  margin: 0;
}
@media (max-width: 700px) {
  .sk-resumo-grade {
    grid-template-columns: 1fr;
  }
}

/* LOJA-19 — pacote de produtividade (Prompt #134). */

/* Item 2: bloco de edição em três linhas de dois campos. Grade de duas
   colunas; numa tela estreita colapsa para uma, porque dois campos lado a
   lado em 360px não são duas colunas, são dois campos espremidos. */
.sk-form-grade-dupla {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 var(--e4);
  align-items: start;
}
@media (max-width: 700px) {
  .sk-form-grade-dupla {
    grid-template-columns: 1fr;
  }
}
/* Categoria e "Transferir para" dividem a MESMA célula (só um dos dois
   fica visível por vez): sem o invólucro, alternar a aba de Operação
   deslocaria os campos seguintes. */
.sk-form-grade-celula > [hidden] {
  display: none;
}
/* Número + Situação + Origem dividem a célula do Número (docs/71 R3,
   Prompt #274: "à direita do campo Número") — os TRÊS ficam visíveis ao
   mesmo tempo (diferente de `.sk-form-grade-celula` acima, que alterna),
   então é `flex` simples, não um invólucro de alternância. */
.sk-form-grade-subpar {
  display: flex;
  gap: var(--e3);
  align-items: start;
}
.sk-form-grade-subpar > .sk-field {
  flex: 1;
  min-width: 0;
}
/* Memo em uma linha de altura — o campo é de anotação curta, e três
   linhas empurravam o botão de salvar para fora da vista. */
.sk-form-grade-dupla textarea {
  min-height: 0;
  height: calc(1.5em + 16px);
  resize: vertical;
}

/* Modal de edição a partir de uma grade (Prompt #405, `R5` - `docs/14`
   Princípio 40, forma LOJA-19). Substitui o painel ancorado à direita de
   "Dados Cadastrais" e o formulário de registro no rodapé. Centralizado,
   COBRINDO a grade - decisão de Wagner: a dor não é a cobertura, é perder
   o lugar, e o lugar volta por `modal-edicao.js`. Mesma família visual do
   `.sk-help-modal`. */
.sk-modal-edicao {
  padding: var(--e5);
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius);
  background: var(--sk-surface-solid);
  color: var(--sk-fg);
  width: min(960px, calc(100vw - 32px));
  max-height: calc(100vh - 32px);
  overflow-y: auto;
}
.sk-modal-edicao--estreito {
  width: min(480px, calc(100vw - 32px));
}
.sk-modal-edicao::backdrop {
  background: rgba(0, 0, 0, 0.6);
}
.sk-modal-edicao-cabecalho {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e3);
}
.sk-modal-edicao-cabecalho h2 {
  margin: 0;
}
.sk-modal-edicao-fechar {
  background: none;
  border: none;
  color: var(--sk-muted);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  padding: var(--e1) var(--e2);
}
.sk-modal-edicao-fechar:hover {
  color: var(--sk-gold);
}
/* A barra da edição, uma só (`P48`): `Salvar` à esquerda, o grupo da
   exclusão (`Motivo` + `Excluir`) empurrado para a extrema direita. */
.sk-barra-edicao {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--e2);
  margin-top: var(--e4);
}
.sk-barra-edicao-exclusao {
  display: flex;
  align-items: flex-end;
  gap: var(--e2);
  margin-left: auto;
}
.sk-barra-edicao-exclusao label {
  align-self: center;
}
.sk-conta-subtitulo {
  margin-top: -8px; /* sk-excecao: compensação negativa aproxima o subtítulo do título — a escala não tem passo negativo */
}

.sk-saldo-destaque {
  font-size: 1.75rem;
  font-weight: 600;
  color: var(--sk-gold);
  margin: var(--e1) 0;
}
.sk-datagrid-th-botao {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-transform: inherit;
  letter-spacing: inherit;
}
.sk-datagrid-th-botao:hover {
  color: var(--sk-datagrid-cabecalho-hover-fg);
}
.sk-datagrid-th-botao:focus-visible {
  outline: 2px solid var(--sk-gold);
  outline-offset: 2px;
}
/* ORDEM DA MAQUETE (Prompt #239): seletor de registros | "Mostrando…" |
   chevrons | exportar. `justify-content: space-between` saiu — com cinco
   filhos ele distribuiria o vão entre todos; o que se quer é UM vão só,
   depois do texto, empurrando navegação e exportação para a direita.
   `margin-right: auto` no texto faz exatamente isso e nada mais. */
.sk-datagrid-paginacao {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e2);
  margin-top: var(--e3);
}
.sk-datagrid-paginacao-texto {
  font-size: 0.75rem;
  color: var(--sk-muted);
  margin-right: auto;
}
.sk-datagrid-paginacao-botoes {
  display: flex;
  gap: var(--e2);
}
/* MOLDURA DISCRETA nos controles do rodapé (Prompt #241, V6b). Medido:
   a maquete usa `rgba(255,255,255,0.15)` — o `--sk-border-strong`, quase
   invisível sobre o fundo —, e o produto usava `--sk-gray` sólido,
   herdado de `.sk-btn-tertiary`. A moldura EXISTE nos dois; o que estava
   diferente era a cor, e um contorno cinza cheio em volta de um ícone o
   faz parecer um botão de ação em vez de um controle de navegação.

   Vale para chevrons e exportação juntos: eles formam um grupo, e um com
   borda forte ao lado de outro com borda fraca seria pior que os dois
   errados igualmente. */
.sk-datagrid-paginacao-botoes .sk-btn,
.sk-datagrid-exportar-botao {
  border-color: var(--sk-border-strong);
}
.sk-datagrid-paginacao-botoes .sk-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
/* Chevron em vez de texto (docs/38 §2.21, Prompt #224) — mesmo botão,
   mesma URL/estado desabilitado; só a forma muda. Fonte maior porque "‹"/
   "›" desenhados no tamanho de texto normal ficam pequenos demais para
   ler como seta.

   ALTURA PADRONIZADA no Prompt #240: era `padding` vertical `--e3`
   (herdado de `.sk-btn`) contra `padding` horizontal `--e2` — caixa mais
   alta que larga, e de altura diferente do seletor de registros e do
   texto ao lado. A maquete crava `--sk-btn-height` com padding vertical
   ZERO em todo botão do rodapé; é o que se faz aqui. */
.sk-datagrid-paginacao-botoes .sk-btn {
  font-size: 1.125rem;
  line-height: 1;
  min-height: var(--sk-btn-height);
  /* `--e2`, o mesmo da maquete (medido: ela usa 6px, o produto usava 9px). */
  padding: 0.25em var(--e2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* Seletor de registros por página (docs/38 §2.21, Prompt #224) —
   preferência GLOBAL do usuário (`Usuario.registros_por_pagina`), gravada
   pelo próprio ato de trocar o seletor.

   SEM RÓTULO VISÍVEL desde o Prompt #239 (maquete de 25/08): o "Mostrando
   1-12 de 32" logo à direita já explica o número, e um "Por página"
   escrito ao lado dele repetia a mesma informação. O `aria-label` do
   `<select>` carrega o rótulo para quem não vê o texto ao lado. */
.sk-datagrid-paginacao-registros {
  display: flex;
  align-items: center;
  font-size: 0.75rem;
  color: var(--sk-muted);
}
/* "Mostrando 1-12 de 32" IMEDIATAMENTE ao lado do seletor (item 4 do
   Prompt #240): o vão da linha inteira já é o `margin-right: auto` do
   texto, e o que separa os dois é só o `gap` do rodapé. O seletor ganha
   a mesma altura dos demais controles da linha — era ele o único com
   altura calculada por `em`, e uma caixa mais baixa que a vizinha lê-se
   como afastada mesmo quando o vão horizontal é o mesmo. */
.sk-datagrid-paginacao-registros select {
  background: color-mix(in srgb, var(--sk-fg) 4%, var(--sk-bg));
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius-sm);
  min-height: var(--sk-btn-height);
  padding: 0.25em var(--e2);
  color: var(--sk-fg);
}

/* Menu de exportação agrupado (docs/38 §2.21, Prompt #224) — substitui os
   três botões separados por um único botão-ícone com flyout, mesmo
   comportamento hover-intent do menu principal (`menu-flyout.js`,
   generalizado para aceitar este seletor). */
/* Menu de ações da LINHA do Datagrid (docs/82, Prompt #390) - a terceira
   adoção do mecanismo de `menu-flyout.js`. O posicionamento é a única
   parte de desenho novo do componente: abre para baixo por padrão, e a
   classe `--para-cima` é posta pelo JS quando não couber abaixo, medido
   na abertura (docs/82 §5). A grade NÃO rola para acomodar o menu. */
.sk-col-menu-linha {
  width: 1%;
  white-space: nowrap;
  text-align: right;
}
.sk-menu-linha {
  position: relative;
  display: inline-block;
}
.sk-menu-linha > summary {
  list-style: none;
  cursor: pointer;
  /* O controle é discreto por desenho: o que a R7 combate é a grade
     virar painel de controle, e um botão chamativo por linha devolveria
     o problema com outra forma. */
  padding: 2px var(--e1);
  line-height: 1;
}
.sk-menu-linha > summary::-webkit-details-marker,
.sk-menu-linha > summary::marker {
  display: none;
  content: "";
}
.sk-menu-linha-conteudo {
  position: absolute;
  right: 0;
  top: calc(100% + var(--e1));
  z-index: 5;
  min-width: 180px;
  /* A largura acompanha o texto do maior item: com 180px fixos o "Registrar
     pagamento" (192px de painel) saía cortado pelo `overflow-y: auto`, que
     faz o eixo horizontal virar `auto` também (docs/88, Prompt #570). */
  width: max-content;
  display: flex;
  flex-direction: column;
  background: var(--sk-surface-solid);
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  padding: var(--e2);
  gap: 2px;
  /* Nunca ultrapassa a viewport (docs/82 §5, mesma regra do docs/18). */
  max-height: 60vh;
  overflow-y: auto;
}
.sk-menu-linha--para-cima .sk-menu-linha-conteudo {
  top: auto;
  bottom: calc(100% + var(--e1));
}
/* A célula do menu ABERTO fica acima das linhas seguintes (docs/88, Prompt
   #570). Sob `.sk-main--grade-rola` a última célula é `position: sticky` com
   `z-index: 2` e cria um contexto de empilhamento por linha: o `z-index: 5` do
   painel fica preso dentro do contexto da própria célula, e o ⋮ da linha de
   baixo (também 2, mais tarde no DOM) pinta por cima do fim do texto. Subir a
   CÉLULA para 3 resolve sem mexer no sticky: 3 vence os 2 das linhas
   seguintes, e os cantos do cabeçalho e do rodapé (4) continuam por cima dela.

   Foi a CÉLULA, e não a linha (`tr`, como a maquete): a linha em 3 empatava com
   o cabeçalho e o rodapé e ganhava deles pela ordem do DOM, e o fundo opaco que
   a guarda do #148 exige de quem se sobrepõe (`test_cabecalho_datagrid_opaco`)
   achataria o zebrado e o realce de hover da linha. O fundo abaixo é o MESMO
   da regra do sticky (`var(--sk-bg)`), repetido só para a garantia morar na
   regra que sobe a camada - as imagens do zebrado seguem as da regra par.

   Escopado ao menu da linha (`:has(.sk-menu-linha[open])`): um `<details>`
   qualquer numa célula (a fatura da LOJA-29) não sobe. */
.sk-main--grade-rola
  .sk-datagrid--cabecalho-fixo
  tbody
  tr:has(.sk-menu-linha[open])
  td:last-child {
  z-index: 3;
  background-color: var(--sk-bg);
}
/* As entradas são texto, não botões empilhados: dentro de um menu o
   realce de botão competiria com o próprio menu. `<form>` sem margem
   para o item dentro dele alinhar com os irmãos. */
.sk-menu-linha-conteudo form {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.sk-menu-linha-conteudo a,
.sk-menu-linha-conteudo button {
  appearance: none;
  background: none;
  border: 0;
  color: var(--sk-text);
  font: inherit;
  text-align: left;
  text-decoration: none;
  padding: var(--e1) var(--e2);
  border-radius: var(--sk-radius-sm);
  cursor: pointer;
  white-space: nowrap;
}
.sk-menu-linha-conteudo a:hover,
.sk-menu-linha-conteudo a:focus-visible,
.sk-menu-linha-conteudo button:hover,
.sk-menu-linha-conteudo button:focus-visible {
  background: color-mix(in srgb, var(--sk-acento) 14%, transparent);
}

.sk-datagrid-exportar-menu {
  position: relative;
}
/* Botão-ícone: documento-com-seta + chevron (maquete de 25/08, Prompt
   #239). O chevron é o que anuncia "abre opções" — sem ele, o ícone
   sozinho promete uma ação imediata que não acontece.

   PROPORÇÃO CORRIGIDA no Prompt #240. O botão saíra mais ALTO que
   LARGO, e a causa eram os dois eixos vindo de fontes diferentes: o
   #239 sobrescreveu só `padding-left`/`padding-right` (`--e2`), deixando
   o vertical no `--e3` que `.sk-btn` declara em `padding: var(--e3)
   var(--e5)`. Nove pixels em cima e embaixo contra seis nos lados, num
   conteúdo de 16px — caixa esticada na vertical, e ainda de altura
   diferente dos chevrons ao lado, que sofriam do mesmo desencontro.

   A maquete não tem esse problema porque lá NENHUM botão usa padding
   vertical: a altura é `--sk-btn-height` e o padding só existe no eixo
   horizontal. É essa a regra adotada aqui, e ela conserta proporção,
   tamanho e alinhamento com os vizinhos de uma vez — não são três
   defeitos, é um. */
.sk-datagrid-exportar-botao {
  cursor: pointer;
  list-style: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--e1);
  min-height: var(--sk-btn-height);
  /* `--e2`, o mesmo da maquete. */
  padding: 0.25em var(--e2);
}
.sk-datagrid-exportar-botao svg {
  width: 16px;
  height: 16px;
  display: block;
}
.sk-datagrid-exportar-seta {
  font-size: 0.6875rem;
  color: var(--sk-muted);
}
.sk-datagrid-exportar-botao::-webkit-details-marker {
  display: none;
}
/* ABRE PARA CIMA, sempre (Prompt #242, V10). Medido: o menu abria em
   `top: 100%` — 165px para baixo a partir de um botão que vive no
   RODAPÉ do grid, ou seja, contra o fim da área rolável. A maquete já
   abre para cima (`bottom: calc(100% + 6px)`), e é o que se replica.

   SEMPRE para cima, nunca escolha automática de lado conforme o espaço:
   o botão só existe no rodapé do grid, então o lado certo é conhecido de
   antemão. Medir espaço em tempo de execução acrescentaria um
   comportamento que ninguém validou para resolver um caso que não
   ocorre. */
.sk-datagrid-exportar-conteudo {
  position: absolute;
  right: 0;
  bottom: calc(100% + var(--e1));
  z-index: 5;
  min-width: 140px;
  display: flex;
  flex-direction: column;
  background: var(--sk-surface-solid);
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  padding: var(--e2);
  gap: 2px;
}
.sk-datagrid-exportar-titulo {
  margin: 0 0 var(--e1);
  padding: 0 var(--e2);
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--sk-muted);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.sk-datagrid-exportar-conteudo a {
  padding: var(--e2);
  border-radius: var(--sk-radius-sm);
  color: var(--sk-fg);
}
.sk-datagrid-exportar-conteudo a:hover,
.sk-datagrid-exportar-conteudo a:focus-visible {
  background: color-mix(in srgb, var(--sk-fg) 8%, var(--sk-bg));
  text-decoration: none;
}

/* ALTURA DE LINHA DO GRID (Prompt #241, V5). Medido com Playwright, a
   escala 100 nos dois: linha de 45px no produto contra 36px na maquete —
   25% mais alta, e a medição de Wagner por screenshot (~56 contra ~44)
   estava certa.

   A HIPÓTESE DE PARTIDA ERA OUTRA E ESTAVA ERRADA: supunha-se que as
   regras do grid não multiplicavam por `--sk-densidade`. Elas
   multiplicam — `--e3` resolve 9px e `--e4` 12px nos dois lados, com a
   densidade aplicada. O que difere é o DEGRAU ESCOLHIDO: `.sk-table`
   usa `--e3 --e4`, a maquete usa `--e2 --e3`. Um passo acima na escala,
   não um multiplicador ausente.

   Restrito a `.sk-datagrid .sk-table` de propósito: `.sk-table` genérica
   serve tabelas fora do componente (a relação de obreiros da LOJA-29,
   por exemplo), e o pedido é sobre o Datagrid. Especificidade 0-2-1
   vence a regra genérica 0-1-1 sem precisar de `!important`.

   SUPERADO EM 04/09/2026 (docs/74, Prompt #287) — o texto acima fica
   como registro do que se mediu no #241, mas o degrau `--e2 --e3` que
   ele escolheu não existe mais aqui: a grade saiu da escala `--eN` e
   ganhou régua própria em `em`. E `--sk-densidade` (citado acima) foi
   eliminado junto com a densidade do obreiro. */
/* RÉGUA PRÓPRIA DA GRADE (docs/74 §2.3, R1/R2/R7 — Prompt #287). Era
   `var(--e2) var(--e3)`: `px` fixo multiplicado por uma densidade que
   NÃO dependia do zoom, enquanto a fonte da célula escala em `rem`. É o
   cruzamento do §0.2 da spec — existe um ponto em que a letra encosta no
   padding e depois passa por cima dele. Em `em`, o respiro cresce junto
   com a letra por construção.

   `line-height` explícito (R7), não herdado: o `1.6` do `body` daria uma
   linha mais alta que a medida sobre maquete, e deixaria a entrelinha da
   grade à mercê de qualquer mudança na tipografia geral do sistema.

   Especificidade 0-2-1 vence a regra genérica 0-1-1 sem `!important` — a
   mesma cascata que o Prompt #241 já usava aqui. */
.sk-datagrid .sk-table th,
.sk-datagrid .sk-table td {
  padding: var(--g-vertical) var(--g-lateral);
  line-height: var(--g-entrelinha);
}

/* docs/38 §2.7 — cabeçalho de coluna do Datagrid usa o token dedicado
   (não o fundo genérico de .sk-table th, usado por tabelas comuns fora do
   componente) — maior especificidade (.sk-datagrid + .sk-table) sobrepõe
   a regra genérica só dentro do Datagrid.

   OPACIDADE É REQUISITO FUNCIONAL, NÃO ACABAMENTO (Prompt #148). Desde
   que o cabeçalho virou `sticky` (#136), o conteúdo rola POR BAIXO dele —
   e com qualquer canal alfa no fundo as linhas de dados continuam
   legíveis através do cabeçalho. Era o defeito relatado por Wagner em
   19/08/2026 no Pedra Bruta e no Pavimento Mosaico (e presente também no
   Cinzas, que ninguém estava usando).

   O #136 escreveu "fundo opaco obrigatório" na regra do sticky
   (`.sk-datagrid--cabecalho-fixo thead th`, mais abaixo) e a garantia
   nunca valeu, por CASCATA: aquela regra tem especificidade 0-1-2 e esta
   tem 0-2-1 — classe vence elemento, então quem pinta o cabeçalho dentro
   do Datagrid é ESTA, com um token que trazia alfa. A lição vale além
   deste caso: *garantia declarada na regra errada não é garantia*.

   A correção não confia no VALOR do token. As camadas abaixo tornam a
   opacidade uma propriedade da REGRA, então nem um Tema novo, nem um
   Master editando a cor na MASTER-08 conseguem reintroduzir o vazamento:

   - `background-color` opaco no fundo de tudo;
   - o `--sk-surface` do card por cima dele (o cabeçalho vive dentro de um
     `.sk-card`, e sem esta camada o tom ficaria 3% mais escuro que hoje);
   - o tom do token no topo, agora composto sobre base opaca.

   Camadas de `background-image` empilham da PRIMEIRA (topo) para a
   última — daí a ordem invertida em relação à leitura natural. */
.sk-datagrid .sk-table th {
  background-color: var(--sk-bg);
  background-image:
    linear-gradient(
      var(--sk-datagrid-cabecalho-bg),
      var(--sk-datagrid-cabecalho-bg)
    ),
    linear-gradient(var(--sk-surface), var(--sk-surface));
}
/* Linha de cabeçalho de grupo (Manutenção de Sementes, docs/45 §7 —
   seções fixas por tipo de Lembrete dentro de um único Datagrid): mesmo
   fundo do cabeçalho de colunas, regra fixa do componente (nunca por
   Tema — mesma decisão de docs/38 §2.6). */
.sk-datagrid .sk-table tr.sk-datagrid-grupo td {
  background: var(--sk-datagrid-cabecalho-bg);
}
/* Zebrado de linhas alternadas — deriva do fundo base via color-mix()
   (docs/38 §2.7), nunca um token dedicado: mistura --sk-fg em --sk-bg a
   6%, o que sempre resulta num passo sutil e legível em qualquer Tema,
   claro ou escuro (diferente de um rgba(255,255,255,...) fixo, que só
   funciona em fundos escuros). */
.sk-datagrid tbody tr:nth-child(even) {
  background: color-mix(in srgb, var(--sk-fg) 6%, var(--sk-bg));
}
/* Degrau do hover sobre linha JÁ zebrada (docs/38 §2.8.3, Prompt #109) —
   mesma mecânica do menu (docs/20 §7.1.1, Prompt #107): o realce de
   interação nunca pode ficar abaixo do realce de estado, e cada linha
   sobe o MESMO delta ao receber o ponteiro (12 pontos), o que preserva o
   zebrado durante o hover em vez de achatá-lo. Escala do Datagrid:
     repouso ímpar 0% · repouso par 6% · hover ímpar 12% · hover par 18%  */
/* Realce da linha recém-editada/criada (docs/38 §2.24, relato C.02,
   Prompt #271) — fundo + borda à esquerda, mesma linguagem visual de
   `[data-sugerido]` (docs/49 §5): nunca só cor, para sobreviver a quem
   enxerga cor de outro jeito. Vem DEPOIS do zebrado de propósito
   (mesma técnica do degrau de hover logo abaixo) — precisa vencer a
   linha par E a ímpar com a MESMA especificidade, e a ordem no arquivo
   é o que decide. Borda no `td:first-child`, não no `tr`: com
   `border-collapse: collapse` (`.sk-table`), borda em `tr` não
   renderiza de forma confiável entre navegadores — a borda pertence à
   primeira célula, que é onde ela sempre aparece. */
.sk-datagrid tbody tr[data-realce] {
  background: color-mix(in srgb, var(--sk-gold) 8%, var(--sk-bg));
}
.sk-datagrid tbody tr[data-realce] td:first-child {
  border-left: 2px solid var(--sk-gold);
}
.sk-datagrid tbody tr:nth-child(even)[data-href]:hover {
  background: color-mix(in srgb, var(--sk-fg) 18%, var(--sk-bg));
}

/* Prompt #488: as linhas de TITULO e de TOTAL do Demonstrativo (LOJA-56,
   docs/83 v1.8 §4.8). As classes `sk-demo-bloco`, `sk-demo-total` e
   `sk-demo-resultado` estao no template desde o #483, mas so tinham peso e
   borda: sem fundo proprio, o total herdava o zebrado da linha par ou impar
   e o titulo ficava no tom do cabecalho de colunas, quase igual ao zebrado
   (medido: o cabecalho e o zebrado par diferem por poucos pontos). Aqui cada
   grupo ganha um tom que o zebrado nao tem, so de token, derivado por
   `color-mix`, como o proprio zebrado:

     titulo   fg 16% sobre bg   (--sk-demo-tom-titulo)
     total    gold 38% sobre bg (--sk-demo-tom-total; o destaque do sistema,
                                 vale tambem para `Receitas menos Despesas`)

   Prompt #504 (docs/83 v1.9 §4.9): os dois percentuais sao UM NUMERO cada, nas
   duas propriedades abaixo, declaradas uma vez. Os valores sao a variante B
   (media) da escolha de Wagner; medidos no navegador, em pixel, em cada Tema
   (o #488 provou a cor lendo este texto, e na tela o total ficou igual ao
   titulo). Trocar o tom e trocar o numero, aqui e so aqui.

   As TRES CAMADAS do zebrado das colunas congeladas (docs/38 §2.25, R2.25-8):
   base opaca, a superficie do card e o tom por ultimo. A mesma pintura vale
   para toda celula da linha, e nas duas colunas presas ela repete a
   especificidade da regra do zebrado e vem DEPOIS dela, que e o que faz o tom
   vencer na linha par e na impar. O titulo e um unico `<th colspan>`: nao e
   alcancado pelo `td:first-child` do congelamento, entao a regra geral basta. */
.sk-datagrid--cabecalho-fixo {
  --sk-demo-tom-titulo: 16%;
  --sk-demo-tom-total: 38%;
}
.sk-datagrid tbody tr.sk-demo-bloco > th,
.sk-datagrid tbody tr.sk-demo-bloco > td {
  font-weight: 600;
  background-color: var(--sk-bg);
  background-image:
    linear-gradient(
      color-mix(in srgb, var(--sk-fg) var(--sk-demo-tom-titulo), transparent),
      color-mix(in srgb, var(--sk-fg) var(--sk-demo-tom-titulo), transparent)
    ),
    linear-gradient(var(--sk-surface), var(--sk-surface));
}
.sk-datagrid tbody tr.sk-demo-total > td,
.sk-datagrid tbody tr.sk-demo-resultado > td {
  font-weight: 600;
  background-color: var(--sk-bg);
  background-image:
    linear-gradient(
      color-mix(in srgb, var(--sk-gold) var(--sk-demo-tom-total), transparent),
      color-mix(in srgb, var(--sk-gold) var(--sk-demo-tom-total), transparent)
    ),
    linear-gradient(var(--sk-surface), var(--sk-surface));
}
.sk-main--grade-rola
  .sk-datagrid--cabecalho-fixo
  tbody
  tr.sk-demo-total
  td:first-child,
.sk-main--grade-rola
  .sk-datagrid--cabecalho-fixo
  tbody
  tr.sk-demo-total
  td:last-child,
.sk-main--grade-rola
  .sk-datagrid--cabecalho-fixo
  tbody
  tr.sk-demo-resultado
  td:first-child,
.sk-main--grade-rola
  .sk-datagrid--cabecalho-fixo
  tbody
  tr.sk-demo-resultado
  td:last-child {
  background-color: var(--sk-bg);
  background-image:
    linear-gradient(
      color-mix(in srgb, var(--sk-gold) var(--sk-demo-tom-total), transparent),
      color-mix(in srgb, var(--sk-gold) var(--sk-demo-tom-total), transparent)
    ),
    linear-gradient(var(--sk-surface), var(--sk-surface));
}
/* Fim do bloco do Prompt #488. */

/* Linha totalizadora opcional (<tfoot>, docs/38 §2.5, Prompt #71) — borda
   mais forte e negrito para se distinguir claramente das linhas normais
   de dado, sem token de cor próprio (reaproveita --sk-border-strong, já
   usado na moldura externa de .sk-table). */
.sk-datagrid tfoot td {
  border-top: 0.5px solid var(--sk-border-strong);
  font-weight: 600;
}

/* INDICADOR DE OBSERVAÇÃO DO EXERCÍCIO (docs/72 R10 e v1.9 §4.4)

   Desde 17/09/2026 (Prompt #400) o indicador mora na célula do `Nome
   Reduzido`, na extrema direita - a coluna `Obs.` saiu. A célula do nome é
   a outra coluna congelada, nunca é vazia e fica sempre visível na rolagem
   lateral.

   A RESERVA existe em toda linha, com ou sem observação: mesmo mecanismo
   do sufixo " D" do saldo (Princípio 38 do docs/14). Todos os lápis caem no
   mesmo x, e o nome nunca invade o espaço - `min-width: 0` no texto é o que
   deixa o flex encolhê-lo antes de encolher a reserva.

   O ícone é um `<span>`, nunca um `<a>` ou `<button>` - a grade é SOMENTE
   LEITURA (R11), e o clique na linha continua sendo o que abre o extrato.
   O `datagrid-linha-clicavel.js` ignora cliques em `a` e `form`; um ícone
   clicável aqui roubaria o clique da linha sem que nada acusasse. */
.sk-mapa-nome-celula {
  display: flex;
  align-items: baseline;
}
.sk-mapa-nome-texto {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
}
.sk-mapa-obs-reserva {
  flex: 0 0 1.6em;
  text-align: right;
}
.sk-mapa-obs {
  color: var(--sk-gold);
  cursor: help;
}

/* LARGURAS DO MAPA ANUAL (docs/72 v1.9 §4.4, Prompt #400)

   A régua é a IGUALDADE dentro de cada grupo, nunca o número: os doze
   meses iguais entre si, os dois totais iguais entre si, os dois saldos
   iguais entre si. Os valores abaixo foram MEDIDOS na tela construída, com
   Playwright, inclusive com o caso de seis dígitos (`13.920,70`) - palavra
   de Wagner: "a maquete não importa mais, o que desejo são as larguras das
   colunas na tela construída".

   Cada largura é também `min-width`: numa tabela de layout automático,
   `width` sozinho é só a preferência, e uma célula mais larga que ela
   alarga a coluna inteira - quebrando a igualdade na primeira linha que
   trouxer um número maior. O valor de cada grupo é o do MAIOR conteúdo
   possível daquele grupo, cabeçalho incluído.

   `nowrap` nas CÉLULAS DE DADO é a outra metade, e sem ela a igualdade
   MENTE: a célula que não cabe quebra em duas linhas em vez de alargar a
   coluna, e a largura medida continua igual. Medido no Prompt #400 - o total
   do `Saldo Atual` no rodapé, em negrito, saía `13.920,70` numa linha e o
   `D` na seguinte. O CABEÇALHO continua quebrando ("Total / Recebido"), como
   já quebrava: com `nowrap` nele, o rótulo passaria a ditar a largura.

   Medido em 17/09/2026, fonte a 100%, pior caso de seis dígitos no rodapé em
   negrito: meses 80px, totais 104px, saldos 116px (com o " D"). Os valores
   em `em` acompanham a escala de fonte junto com o conteúdo.

   **Por POSIÇÃO, e só nesta grade.** A ordem das colunas é regra da §4.1, e
   o congelamento do docs/38 §2.25 já prende por posição. **Isto NÃO é
   regra de sistema** (§4.4): se a igualdade de largura virar régua de
   todas as grades, o endereço é o docs/38, e a decisão é de Wagner.

   Colunas: 1 Nome Reduzido · 2 Saldo Anterior · 3-14 meses · 15 Total
   Recebido · 16 Total Devido · 17 Saldo Atual. */
#sk-datagrid-mapa-anual :is(th, td):nth-child(n+3):nth-child(-n+14) {
  width: 5em;
  min-width: 5em;
}
#sk-datagrid-mapa-anual :is(th, td):nth-child(n+15):nth-child(-n+16) {
  width: 6.5em;
  min-width: 6.5em;
}
#sk-datagrid-mapa-anual :is(th, td):nth-child(2),
#sk-datagrid-mapa-anual :is(th, td):nth-child(17) {
  width: 7em;
  min-width: 7em;
}
#sk-datagrid-mapa-anual td:nth-child(n+2) {
  white-space: nowrap;
}

/* MARCA DE COBRANÇA EM ABERTO (docs/72 R5.1, Prompt #331 — Mapa Anual)

   POR QUE ELA EXISTE, e não é enfeite: com a célula do mês passando a
   mostrar o COBRADO (R5) e não o recebido, a linha do Irmão em dia e a do
   Irmão que parou de pagar em setembro ficam IDÊNTICAS — `300 300 300` nas
   duas. O objetivo declarado da tela (docs/72 §1) inclui "quem está para
   trás, E DESDE QUANDO", e é a PRIMEIRA marca da linha que responde o
   "desde quando".

   COR E FORMA JUNTAS, nunca só cor: quem não distingue vermelho continua
   vendo o ponto. É a mesma régua do sufixo " D" logo abaixo — a marca do
   devedor é forma (a letra), e a cor vem por cima.

   O ponto é `aria-hidden` no template e vem acompanhado de texto
   visualmente oculto ("Cobrança em aberto: "), porque um `●` lido em voz
   alta por leitor de tela não diz nada.

   O SEGUNDO ESTADO (R5.2) — o amarelo do pagamento PARCIAL — entra como
   uma regra irmã desta quando o status `PARCIAL` existir (docs/78, Sprint
   #02). NÃO construir antes: marca sem estado por trás é enfeite que
   alguém vai testar. */
.sk-mapa-marca {
  color: var(--sk-danger);
  margin-right: 0.25ch;
  font-size: 0.7em;
  vertical-align: middle;
}

/* SUFIXO " D" DE DEVEDOR (docs/72 §4.3, Prompt #330 — Mapa Anual LOJA-53)

   As MESMAS duas classes já existiam, escritas à mão dentro do <style> da
   `pagina_publica_cobranca.html` (PUB-03, docs/68 R-P10). Elas moram lá
   porque aquela página é AUTO-CONTIDA por decisão do #259: não carrega
   este arquivo nem o `tokens.css`, e por isso repete o vermelho como
   literal. **Não é duplicação a corrigir — é a mesma regra em dois
   universos de CSS que não se alcançam.** Aqui ela vem para o app
   autenticado, e aqui o vermelho é o token.

   AS TRÊS EXIGÊNCIAS DA LETRA, e as três já custaram medição (decisões de
   Wagner de 06/09, docs/72 §4.3):

   1. a largura fica RESERVADA nas linhas SEM letra — o <span> existe
      sempre, inclusive vazio. Sem ele a vírgula desalinha entre quem deve
      e quem não deve, e coluna de dinheiro que não alinha na vírgula é
      coluna lida duas vezes;
   2. a letra OBEDECE O NÚMERO: mesma cor, mesma família, mesmo tamanho,
      mesmo peso. Ela não tem tipografia própria — por isso este bloco não
      declara `font-family`, `font-size` nem `font-weight`;
   3. a reserva de largura é a ÚNICA coisa que a distingue do número.

   Em 06/09 a letra saía em monoespaçada, 0.86em e peso 600 — um estilo
   inventado só para ela, que se propagou porque uma maquete copiou da
   outra. Como o sufixo é FILHO do próprio número, a divergência de COR
   nunca existiu, e foi isso que escondeu as outras três por dias: o olho
   confere a cor primeiro e para ali. A guarda testa as três SEPARADAMENTE
   (`test_mapa_anual_tela.py`). */
.sk-dc-letra {
  display: inline-block;
  width: 1.1ch;
  text-align: left;
  padding-left: 0.35ch;
}
.sk-dc--devedor {
  color: var(--sk-danger);
}

/* docs/38 §2.6 — tipo_exibicao por coluna: alinhamento e fonte são regra
   FIXA do componente (nunca parametrizável por Tema, diferente da cor
   acima) — aplicada tanto no cabeçalho (th, via apps.core.datagrid) quanto
   na célula de dado (td, marcada manualmente por cada linha_template). */
.sk-col-numerico {
  text-align: right;
}
.sk-col-status {
  text-align: center;
  font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace;
}
/* DATA (Prompt #62) — mesmo centro de STATUS, mas sem monoespaçada: uma
   data (ex: Nascimento, LOJA-15) não é "símbolo de 1 caractere". */
/* Coluna de IMAGEM do Datagrid (docs/38 §2.18, Prompt #156) — a primeira
   coluna que o componente ganhou desde o §2.6, e a única cujo conteúdo não
   é texto.

   Largura fixa e mínima: uma miniatura não deve competir por espaço com as
   colunas que carregam informação. `width` numa célula de tabela é
   sugestão, não ordem — daí o par com `white-space: nowrap`, que impede a
   coluna de esticar para acomodar quebra. */
.sk-col-imagem {
  text-align: center;
  width: 3em;
  white-space: nowrap;
}
/* A miniatura em si. Circular e `object-fit: cover` pelo mesmo motivo do
   `.sk-foto-obreiro-preview` (§ acima): retrato de pessoa preenche o
   círculo; recorte é esperado, distorção não.

   `vertical-align: middle` porque a imagem divide a linha com células de
   texto — sem ele, a linha de base do texto alinharia com o pé da imagem e
   a linha inteira ficaria alta demais. */
.sk-datagrid-miniatura {
  display: inline-block;
  width: 2em;
  height: 2em;
  object-fit: cover;
  border-radius: 50%;
  vertical-align: middle;
  border: 0.5px solid var(--sk-border-strong);
  background: color-mix(in srgb, var(--sk-fg) 4%, var(--sk-bg));
}
.sk-col-data {
  text-align: center;
}

.sk-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
  font-size: 0.6875rem;
}
.sk-badge-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  display: inline-block;
}
.sk-badge-dot--ok { background: var(--sk-success); }
.sk-badge-dot--warn { background: var(--sk-warning); }
.sk-badge-dot--danger { background: var(--sk-danger); }
.sk-badge-dot--muted { background: var(--sk-gray); }

/* Valor com sinal (LOJA-36, docs/61 §6 R3) — a cor é REFORÇO, nunca o
   único portador: o sinal (+/−) já está no número em si; isto só
   reforça visualmente o que o sinal já diz. Reaproveita os tokens de
   sucesso/perigo já existentes (nenhuma cor nova inventada) — mesmos
   usados em `.sk-badge-dot--ok`/`--danger` acima. */
.sk-valor-credito { color: var(--sk-success-fg); }
.sk-valor-debito { color: var(--sk-danger-fg); }

/* Número que não quebra em duas linhas (LOJA-58, docs/88, Prompt #570). Com
   `.sk-main--grade-rola .sk-table { width: auto; min-width: 100% }` a tabela
   divide a largura entre as colunas e uma célula numérica podia ser espremida
   abaixo do próprio texto, quebrando "1.234,56 D" no ponto do milhar. Classe
   PRÓPRIA da tela, e não `.sk-col-numerico` (43 arquivos) nem
   `.sk-valor-credito`/`-debito` (2 templates mais os da LOJA-58): o alcance é
   o dos templates que a escrevem, e nenhuma tela existente muda. */
.sk-col-sem-quebra { white-space: nowrap; }

.sk-messages {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--e5);
}
.sk-messages li {
  font-size: 0.8125rem;
  padding: var(--e3) var(--e4);
  border-radius: var(--sk-radius-sm);
  background: rgba(99, 153, 34, 0.1);
  border: 0.5px solid rgba(99, 153, 34, 0.3);
  color: var(--sk-success-fg);
  margin-bottom: var(--e2);
}
.sk-messages li.error {
  background: rgba(192, 57, 43, 0.1);
  border-color: rgba(192, 57, 43, 0.3);
  color: var(--sk-danger-fg);
}
/* Aviso usa --sk-warning-fg, não --sk-gold: o dourado é token de CHROME
   (botão de Nível 2, ícones de destaque) e num Tema monocromático vira
   neutro — a mensagem de aviso perderia a sinalização. O valor de fábrica
   é o mesmo dourado, então "Pedra Bruta" não muda. */
.sk-messages li.warning {
  background: rgba(201, 168, 76, 0.12);
  border-color: rgba(201, 168, 76, 0.35);
  color: var(--sk-warning-fg);
}

/* Erros de validação de formulário (docs/14, princípio 19) — Django gera
   ul.errorlist sem nenhuma classe própria por campo, herdando a cor de texto
   padrão; contra o tema escuro isso passa despercebido. Global (não restrito
   a .sk-card) porque telas pré-login (ex: login.html) também renderizam
   formulários fora de um card. */
.errorlist {
  list-style: none;
  margin: 0 0 var(--e2);
  padding: 0;
}
/* `--sk-danger-fg`, não `--sk-danger` (Prompt #123, docs/37 §10.3.1).

   `--sk-danger` tem dois papéis incompatíveis: é o FUNDO de
   `.sk-btn-danger` (que fixa `color:#fff`, exigindo cor escura) e era a
   cor deste TEXTO sobre o fundo do Tema (exigindo cor clara). O Prompt
   #118 provou que os intervalos de luminância exigidos pelos dois não se
   intersectam em tema escuro — nenhum valor de token resolvia, então a
   correção tinha de ser aqui.

   `--sk-danger-fg` existe desde o Prompt #106 exatamente para "texto
   legível de erro", e `.errorlist.nonfield li` já o usava. O texto de
   erro por campo é `font-weight: 700` a `0.75rem`, portanto sem direito
   ao limiar de "texto grande" — precisa de 4,5:1 cheios.

   `display:flex`/`gap` (Prompt #187) alinham o ícone de erro ao lado do
   texto — ver `.errorlist li::before`, onde o símbolo em si mora. */
.errorlist li {
  display: flex;
  align-items: center;
  gap: var(--e1);
  color: var(--sk-danger-fg);
  font-weight: 700;
  font-size: 0.75rem;
}
.errorlist.nonfield {
  background: rgba(192, 57, 43, 0.1);
  border: 0.5px solid rgba(192, 57, 43, 0.3);
  border-radius: var(--sk-radius-sm);
  padding: var(--e3) var(--e4);
  margin: 0 0 var(--e4);
}
.errorlist.nonfield li {
  color: var(--sk-danger-fg);
}

/* Estilo padrão para campos renderizados via {{ form.as_p }} — os módulos
   ainda não têm widgets custom para usar .sk-field diretamente, então esta
   regra dá a mesma aparência a qualquer <p> gerado pelo Django dentro de um
   sk-card, sem exigir mudança nos formulários. */
.sk-card form > p,
.sk-card fieldset > p {
  margin: 0 0 var(--e4);
}
/* A regra de rótulo destes `<p>` foi UNIFICADA com `.sk-field label` lá
   em cima (docs/14, princípio 29) — ver a nota de lá sobre o `>` que
   deixava rótulos aninhados escaparem. Só o espaçamento do `<p>` fica
   aqui, porque é layout e não tipografia. */
.sk-card form input,
.sk-card form select,
.sk-card form textarea,
.sk-card fieldset input,
.sk-card fieldset select,
.sk-card fieldset textarea {
  width: 100%;
  box-sizing: border-box;
  background: color-mix(in srgb, var(--sk-fg) 4%, var(--sk-bg));
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius-sm);
  padding: var(--e2) var(--e3);
  color: var(--sk-fg);
  font-size: 0.8125rem;
  font-family: var(--sk-font-body);
}
.sk-card fieldset {
  border: 0.5px solid var(--sk-border);
  border-radius: var(--sk-radius);
  padding: var(--e3) var(--e4) var(--e1);
  margin: 0 0 var(--e4);
}
.sk-card fieldset legend {
  padding: 0 var(--e2);
  font-size: 0.75rem;
  color: var(--sk-gold);
}

.sk-page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e4);
  margin-bottom: var(--e4);
}
/* Prompt #226 (docs/59 P2 — "entrelinhamento por escala tipográfica,
   nunca valor único"). O `line-height: 1.5` herdado do `body` serve a
   texto corrido; num glifo de título ele reserva quase meia linha de
   vazio DENTRO da caixa do <h1> — espaço que nenhuma margem explica e
   que ninguém acha procurando por `margin`. `1.15` é o ganho maior, e
   o menos óbvio, dos três. `margin-bottom` some da regra genérica
   `h1,h2,h3{margin:0 0 0.5em}` (que dava ~21px, o equivalente a
   `--e5`) e vira `--e3` explícito, mais próximo do respiro que o
   entrelinhamento apertado já deixa. */
.sk-page-header h1 {
  line-height: 1.15;
  margin-bottom: var(--e3);
}
/* Mesma ideia de .sk-page-header (título + ação lado a lado), mas para um
   subtítulo dentro de um grupo de campos (ex: "Endereço de Cobrança" + o
   botão "Copiar dados do endereço institucional", docs/05 item 2.1,
   Prompt #35) — nunca reaproveita .sk-page-header diretamente porque esse
   nome já é específico de <h1>/topo de página. */
.sk-subtitulo-com-acao {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e3);
}
.sk-subtitulo-com-acao h3 {
  margin: 0;
}

.sk-empty {
  color: var(--sk-muted);
  font-style: italic;
}

.sk-pagination {
  display: flex;
  align-items: center;
  gap: var(--e3);
  font-size: 0.75rem;
  color: var(--sk-muted);
  margin-top: var(--e4);
}

.sk-login-erro {
  color: var(--sk-danger-fg);
  font-size: 0.8125rem;
  margin: 0 0 var(--e4);
}

/* Navegação "Voltar" — sempre ícone de seta + texto, nunca um sem o outro
   (docs/14-Diretrizes_de_Interface_Skirret.md, princípio 2). O ícone é
   injetado via ::before para que a classe sozinha já garanta o padrão,
   sem exigir que cada template repita o caractere no texto. Nível 3
   (navegação/dispensar) é sempre um botão real (sk-btn-tertiary), nunca um
   link de texto (docs/14, changelog 02/08/2026). */
.sk-btn-tertiary--voltar::before {
  content: "←";
  margin-right: var(--e2);
}

/* Linha de ações lado a lado, para qualquer par de botões relacionados
   (ação principal + navegação) — Etapa 1 ("Continuar" + "Outras formas de
   acesso"), Etapa 2 ("Confirmar" + "Voltar") e telas de confirmação OAuth
   ("Continuar" + "Voltar") (docs/14, princípio 9). */
.sk-login-step-actions {
  display: flex;
  align-items: center;
  gap: var(--e3);
}
.sk-login-step-actions form {
  margin: 0;
}
/* Etapa 1 do login (docs/06, seção 3.1): "Continuar" fica à esquerda, o
   grupo dos 3 botões-ícone (Magic Link/Google/Microsoft) fica à direita —
   scoped a #sk-step-1 para não afetar outras telas que reaproveitam
   .sk-login-step-actions (Etapa 2, Etapa 4, confirmação OAuth), onde o par
   de botões continua colado como antes. */
#sk-step-1 .sk-login-step-actions {
  justify-content: space-between;
}
.sk-login-icones-grupo {
  display: flex;
  align-items: center;
  gap: var(--e3);
  flex-shrink: 0;
}

/* Tela de login: logomarca centralizada no lugar do sk-header padrão. */
.sk-login-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e4);
  padding: 48px var(--e6) var(--e6); /* sk-excecao: o 48px é o respiro solitário sobre a logomarca do login — a exceção registrada do docs/59 §3, fora da escala de propósito */
}
/* Avisos na tela de login (Prompt #107) — mesmo componente visual do
   cabeçalho interno (.sk-header-aviso), mas empilhado e centrado sob a
   logomarca, já que aqui não existe a grade de duas colunas do header
   autenticado. Só ocupa espaço quando há mensagem. */
.sk-login-avisos {
  display: flex;
  flex-direction: column;
  gap: var(--e2);
  width: 100%;
  max-width: 420px;
}
.sk-login-avisos:empty {
  display: none;
}
.sk-login-logo-link {
  display: block;
  line-height: 0;
}
.sk-login-logo {
  height: 128px;
  width: auto;
}
.sk-login-providers {
  display: flex;
  flex-direction: column;
  gap: var(--e3);
}
.sk-login-providers .sk-btn {
  text-align: center;
  width: 100%;
}
/* Espaçamento entre a última opção empilhada (Nível 2) e um botão de
   Nível 3 que venha em seguida (ex: "Voltar") — sem isto ficam colados
   (docs/14, princípio 9). */
.sk-login-providers + .sk-btn {
  margin-top: var(--e4);
}

/* Botões quadrados só de ícone (Etapa 1 do login, docs/06 seção 3.1: Magic
   Link, Google, Microsoft) — o fundo (laranja para Magic Link, claro para
   Google/Microsoft) já vem embutido no PNG (src/static/img/auth/), então o
   botão em si não tem chrome próprio, só molda tamanho/cantos e o estado
   de foco/hover. Sem texto visível (title + aria-label cobrem tooltip e
   leitor de tela — docs/14, princípio 2, exceção documentada no template
   para logotipos de marca já reconhecíveis). */
/* Altura/largura fixas via --sk-btn-height (docs/14, princípio 23) — nunca
   o tamanho natural do PNG, para ficar exatamente igual à altura real do
   ".sk-btn" de texto ao lado ("Continuar"), no mesmo grupo de ações.
   ``cursor: pointer`` explícito porque este botão não estende ``.sk-btn``
   (que já declara isso) e o <button> nativo do navegador, ao contrário do
   <a>, não usa "pointer" por padrão. */
.sk-login-icone-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--sk-btn-height);
  height: var(--sk-btn-height);
  padding: 3px;
  border: none;
  border-radius: var(--sk-radius-sm);
  background: transparent;
  cursor: pointer;
  overflow: hidden;
  flex-shrink: 0;
}
.sk-login-icone-btn img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: contain;
}
.sk-login-icone-btn:hover,
.sk-login-icone-btn:focus-visible {
  outline: 2px solid var(--sk-gold);
  outline-offset: 2px;
}

/* Código da tela do catálogo (docs/10 e docs/14, princípio 3) — discreto,
   canto direito do sk-footer. */
.sk-tela-codigo {
  font-size: 0.6875rem;
  color: var(--sk-gray);
}
/* Navegação rápida (docs/14, princípio 3 — Prompt #129): quando há telas
   a oferecer, o código deixa de ser texto morto e passa a ser um gatilho
   de duplo clique. O tracejado inferior é o único sinal permanente — o
   `title` só aparece no hover, e um código que não anuncia nada nunca
   seria descoberto. `user-select: none` evita que o duplo clique, que no
   navegador seleciona a palavra, deixe o código realçado por baixo do
   diálogo que acabou de abrir. */
.sk-tela-codigo--navegavel {
  cursor: pointer;
  border-bottom: 1px dotted var(--sk-gray);
  user-select: none;
}
.sk-tela-codigo--navegavel:hover {
  color: var(--sk-fg);
  border-bottom-color: var(--sk-fg);
}
/* Agrupa código da tela + ícone de Help contextual como um único item de
   flex do rodapé — sem isto, "justify-content: space-between" trata cada
   um como item separado e empurra o código da tela para o meio do rodapé
   em vez de ficar colado ao ícone, à direita (docs/14, princípio 3; bug
   corrigido em 02/08/2026).

   O `margin-left: auto` do Prompt #71 SAIU daqui na terceira reforma
   (#141): quem empurra para a direita agora é `.sk-footer-direita`, o
   elemento que envolve os três controles. Ver o comentário lá. */
.sk-footer-tela-grupo {
  display: flex;
  align-items: center;
  gap: var(--e2);
}

/* Novo conteúdo do rodapé (docs/14, Prompt #60) — Apelido/e-mail (title) e
   Nome da Loja atual na extrema esquerda; Controle de Zoom, código da
   tela e ícone de Ajuda na extrema direita (reposicionados no Prompt #71
   — antes, tudo ficava alinhado à esquerda numa única linha). A Mensagem/
   `rodape_texto` do Tema deixou de ser exibida aqui desde o Prompt #60
   (continua existindo em ConfiguracaoVisual, só não é mais renderizada
   neste local). */
/* ---------------------------------------------------------------------
   TERCEIRA REFORMA DO RODAPÉ (docs/14 — Prompt #141), depois do #60
   (conteúdo) e do #71 (dois grupos).

   As duas zonas passam a ser ELEMENTOS, em vez de dois `margin-left: auto`
   competindo entre si. O arranjo do #71 funcionava enquanto tudo coubesse
   numa linha: o rodapé tem `flex-wrap: wrap`, e quando os itens quebram
   para a segunda linha a margem automática passa a agir POR LINHA — a
   "extrema direita" vira a extrema direita da linha em que o item caiu.
   Com um elemento por zona, o `space-between` do rodapé resolve a
   distribuição e a quebra leva a zona inteira junto. */
.sk-footer-esquerda,
.sk-footer-direita {
  display: flex;
  align-items: center;
  /* 10px, não os 20px do rodapé: dentro de uma zona os itens são partes
     de um mesmo bloco de informação; entre as zonas, o espaço é o vão. */
  gap: var(--e3);
  flex-wrap: wrap;
}
.sk-footer-direita {
  margin-left: auto;
}
/* O pipe é o separador explícito da zona esquerda (Loja | Usuário). Mais
   apagado que os dois lados que separa — é pontuação, não informação. */
.sk-footer-separador {
  color: var(--sk-border-strong);
  user-select: none;
}
.sk-footer-usuario {
  cursor: default;
}
.sk-footer-loja {
  color: var(--sk-muted);
}

/* Escala de fonte por usuário (docs/14, princípio 15) — só renderizado em
   telas autenticadas (ver base.html).

   O `margin-left: auto` do Prompt #71 SAIU daqui na terceira reforma
   (#141): este controle é agora um item de `.sk-footer-direita`, e é a
   zona que se posiciona. Ter a margem aqui grudaria o zoom no vão e
   deixaria os outros dois colados à direita — o oposto do "três
   controles distintos, com espaçamento entre si" que a reforma pede. */
.sk-escala-fonte {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  user-select: none;
}
.sk-escala-fonte-btn {
  background: none;
  border: 0.5px solid var(--sk-gray);
  color: var(--sk-gray);
  border-radius: var(--sk-radius-sm);
  /* CAIXA ELÁSTICA, não fixa (Prompt #147). O `em` é relativo ao
     `font-size` DESTE elemento, então a caixa acompanha o próprio glifo:
     no padrão (100% = base 20px) o resultado é exatamente o mesmo px de
     antes, e nos extremos da faixa nova a caixa cresce junto em vez de
     cortar o conteúdo.

     Por que era preciso: a base subiu para 20px e o teto da escala
     continua em 150%, ou seja 30px de raiz — terreno que nunca existiu
     (o máximo antigo eram 24px). Com a caixa cravada em px, o glifo
     passava a caixa lá em cima e o botão ficava com o símbolo
     transbordando. Nada muda no tamanho que Wagner valida. */
  width: 1.4545em;
  height: 1.4545em;
  line-height: 1;
  cursor: pointer;
  font-size: 0.6875rem;
  padding: 0;
}
.sk-escala-fonte-btn:hover {
  color: var(--sk-gold);
  border-color: var(--sk-gold);
}
.sk-escala-fonte-valor {
  font-size: 0.6875rem;
  color: var(--sk-gray);
  min-width: 30px;
  text-align: center;
}

/* Densidade de espaçamento por usuário (docs/59 §4) — seletor único
   (Prompt #273, decisão de Wagner sobre maquete: era três ícones
   separados). `position: relative` é o que ancora a lista SEM cálculo
   de coordenada em JS: a lista se posiciona relativa a este `<span>`,
   não à viewport. */


/* Help Contextual (docs/17, seção 3.1) — ícone "?" discreto no rodapé, ao
   lado do código da tela; abre um modal nativo (<dialog>) com altura
   máxima e rolagem interna própria — comportamento único e consistente
   independente do tamanho do conteúdo, sem lógica condicional de
   truncamento (substituiu o painel expansível via <details>, que
   truncava conteúdos longos — atualizado em 02/08/2026). */
.sk-help-contextual {
  display: inline-block;
}
.sk-help-icone {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* CAIXA ELÁSTICA, não fixa (Prompt #147). O `em` é relativo ao
     `font-size` DESTE elemento, então a caixa acompanha o próprio glifo:
     no padrão (100% = base 20px) o resultado é exatamente o mesmo px de
     antes, e nos extremos da faixa nova a caixa cresce junto em vez de
     cortar o conteúdo.

     Por que era preciso: a base subiu para 20px e o teto da escala
     continua em 150%, ou seja 30px de raiz — terreno que nunca existiu
     (o máximo antigo eram 24px). Com a caixa cravada em px, o glifo
     passava a caixa lá em cima e o botão ficava com o símbolo
     transbordando. Nada muda no tamanho que Wagner valida. */
  /* `min-*` em vez de tamanho cravado, e `content-box` (Prompt #291).

     A CAUSA, medida - e não era a que parecia. O `em` do #147 já fazia a
     caixa acompanhar o glifo; o que sobrava vinha da BORDA. `border: 0.5px`
     é renderizada como 1px inteiro pelo Chrome, e com o `box-sizing:
     border-box` global isso comia 2px do espaço INTERNO de uma caixa
     calibrada em 1.3091em sem contar com eles: em 80% de escala sobravam
     `client=12` para um conteúdo de 14. `content-box` devolve os 2px, que é
     o que a medida do #147 sempre quis dizer - 1.3091em é o tamanho do
     GLIFO, não o da caixa com contorno.

     CONSEQUÊNCIA DECLARADA: o ícone passa de 18px para 20px em 100%. É a
     devolução do espaço que a borda tomava, não um aumento de desenho.

     `aspect-ratio` foi tentado antes e REMOVIDO: não mudou nenhum número
     (medido), porque a altura já vinha da largura por outro caminho. */
  min-width: 1.3091em;
  min-height: 1.3091em;
  box-sizing: content-box;
  line-height: 1;
  border-radius: 50%;
  border: 0.5px solid var(--sk-gray);
  background: none;
  color: var(--sk-gray);
  font-size: 0.6875rem;
  font-family: var(--sk-font-body);
  padding: 0;
  cursor: pointer;
}
.sk-help-icone:hover {
  color: var(--sk-gold);
  border-color: var(--sk-gold);
}
.sk-help-modal {
  padding: 0;
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius);
  background: var(--sk-bg);
  color: var(--sk-fg);
  width: min(480px, calc(100vw - 48px));
  max-height: min(70vh, 560px);
}
.sk-help-modal[open] {
  display: flex;
  flex-direction: column;
}
.sk-help-modal::backdrop {
  background: rgba(0, 0, 0, 0.6);
}
.sk-help-modal-cabecalho {
  display: flex;
  justify-content: flex-end;
  flex: none;
  padding: var(--e2) var(--e3) 0;
}
.sk-help-modal-fechar {
  background: none;
  border: none;
  color: var(--sk-muted);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  padding: var(--e1) var(--e2);
}
.sk-help-modal-fechar:hover {
  color: var(--sk-gold);
}
.sk-help-modal-corpo {
  overflow-y: auto;
  padding: var(--e1) var(--e5) var(--e5);
  font-size: 0.8125rem;
  text-align: left;
}
.sk-help-titulo {
  margin-top: 0;
}
/* Título + ícone de impressão lado a lado (docs/17, item 3.1). */
.sk-help-artigo-cabecalho {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e3);
}
.sk-help-artigo-cabecalho .sk-help-titulo {
  flex: 1;
}
.sk-help-imprimir-btn {
  flex: none;
  background: none;
  border: 0.5px solid var(--sk-gray);
  color: var(--sk-gray);
  border-radius: var(--sk-radius-sm);
  /* Caixa elástica — ver a nota em `.sk-btn-icone` (Prompt #147). */
  width: 1.6em;
  height: 1.6em;
  line-height: 1;
  cursor: pointer;
  font-size: 0.8125rem;
  padding: 0;
}
.sk-help-imprimir-btn:hover {
  color: var(--sk-gold);
  border-color: var(--sk-gold);
}
.sk-help-imagem {
  margin: var(--e3) 0;
}
.sk-help-imagem img {
  display: block;
  max-width: 100%;
  border-radius: var(--sk-radius-sm);
}
.sk-help-imagem figcaption {
  font-size: 0.6875rem;
  color: var(--sk-muted);
  margin-top: var(--e1);
}

/* Feedback 👍/👎 (docs/17, item 2.4) — discreto, ao final do conteúdo. */
.sk-help-feedback {
  display: flex;
  align-items: center;
  gap: var(--e2);
  margin-top: var(--e4);
  padding-top: var(--e3);
  border-top: 0.5px solid var(--sk-border);
}
.sk-help-feedback-label {
  font-size: 0.6875rem;
  color: var(--sk-muted);
}
.sk-help-feedback-btn {
  background: none;
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius-sm);
  cursor: pointer;
  padding: var(--e1) var(--e2);
  font-size: 0.8125rem;
}
.sk-help-feedback-btn:hover {
  border-color: var(--sk-gold);
}
/* Voto enviado via fetch (docs/17, regra 12) — destaca o botão escolhido,
   sem depender de reload/mensagem de confirmação. */
.sk-help-feedback-btn--selecionado {
  background: rgba(201, 168, 76, 0.15);
  border-color: var(--sk-gold);
}

/* Componente de Diálogo (docs/18) — substitui confirm()/alert() nativos
   (docs/14, princípio 16). Reaproveita o mesmo mecanismo de <dialog> +
   ::backdrop já usado no Help (docs/17), sempre centralizado na viewport
   (comportamento nativo de showModal(), não um posicionamento manual —
   funciona mesmo com a página rolada, docs/18 cenário 8) e com rolagem
   interna própria para mensagens longas (docs/18 cenário 7). Estrutura
   definida pelo núcleo genérico (dialogo-core.js); só as cores/tamanhos
   abaixo são específicos do tema Pedra Bruta. */
.dialogo-modal {
  padding: 0;
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius);
  background: var(--sk-bg);
  color: var(--sk-fg);
  width: min(440px, calc(100vw - 32px));
  max-height: min(80vh, 640px);
}
.dialogo-modal[open] {
  display: flex;
  flex-direction: column;
}
.dialogo-modal::backdrop {
  background: rgba(0, 0, 0, 0.6);
}
.dialogo-modal-corpo {
  overflow-y: auto;
  padding: var(--e5);
  font-size: 0.8125rem;
  text-align: left;
}
/* Ícone à esquerda, título+mensagem à direita, lado a lado (docs/14,
   princípio 2) — antes eram blocos empilhados sem nenhum wrapper flex. */
.dialogo-modal-cabecalho {
  display: flex;
  align-items: flex-start;
  gap: var(--e3);
  margin-bottom: var(--e4);
}
.dialogo-modal-textos {
  flex: 1;
  min-width: 0;
}
.dialogo-modal-icone {
  font-size: 1.75rem;
  line-height: 1;
  flex-shrink: 0;
}
/* ------------------------------------------------------------------
   Variante ALERTA — o Aviso Urgente (docs/24 §6, Prompt #158)
   ------------------------------------------------------------------
   O Aviso Urgente é a comunicação que INTERROMPE e é dispensável (docs/14,
   ao lado do Princípio 35). Até aqui ele usava o mesmo fundo do chrome da
   interface, e era visualmente "mais um card" — um aviso urgente que não se
   distingue do resto falhou no seu único propósito.

   **Derivado de TOKEN, nunca hex fixo.** O #106 e o #118 mostraram que cor
   fixa produz resultado imprevisível num tema de base clara ou
   monocromático: o mesmo vermelho que alerta sobre preto some sobre branco.
   `color-mix` sobre `--sk-danger` resolve nos três de fábrica e em qualquer
   tema futuro, porque a mistura acompanha o `--sk-bg` de cada um.

   **18% é o ponto medido**, não escolhido no olho: abaixo disso a diferença
   contra o fundo normal é imperceptível no Pavimento Mosaico; acima, o
   ganho de separação é marginal e o fundo começa a competir com o texto.
   Com 18%, o texto fica entre 14,19:1 e 15,07:1 nos três temas. */
.dialogo-modal--alerta {
  background: color-mix(in srgb, var(--sk-danger) 18%, var(--sk-bg));
  border-color: var(--sk-danger);
}
/* O ícone da variante usa `--sk-danger-fg`, e não `--sk-warning` como os
   demais diálogos de aviso. Dois motivos, e o segundo foi medido:

   1. Semântica: isto é um ALERTA, não um aviso.
   2. `--sk-warning` (#d9a441) sobre o fundo claro do Pavimento Mosaico dá
      **2,25:1** — abaixo do AA, e já era assim ANTES desta mudança. Sobre o
      fundo de alerta cairia para 1,71:1. Com `--sk-danger-fg`, sobe para
      6,14:1 ali, e fica em 7,27:1 / 6,18:1 nos outros dois. */
.dialogo-modal--alerta .dialogo-modal-icone {
  color: var(--sk-danger-fg);
  /* 50% maior que o 1.75rem padrão (Prompt #158). */
  font-size: 2.625rem;
}
/* Ícone centralizado em relação ao BLOCO de título+mensagem, só nesta
   variante. Com `align-items: flex-start` (o padrão dos demais diálogos),
   um título de duas linhas deixava o ícone pendurado no topo, parecendo
   encolhido ao lado do texto. Centralizar dá a ele o peso de coluna que um
   alerta pede. */
.dialogo-modal--alerta .dialogo-modal-cabecalho {
  align-items: center;
}
.dialogo-modal-icone--critico { color: var(--sk-danger); }
.dialogo-modal-icone--pergunta { color: var(--sk-gold); }
.dialogo-modal-icone--aviso { color: var(--sk-warning); }
.dialogo-modal-icone--informacao { color: var(--sk-gold); }
.dialogo-modal-titulo {
  margin: 0 0 var(--e2);
}
.dialogo-modal-mensagem {
  margin: 0;
  white-space: pre-line;
}
/* Checkbox adjacente ao rótulo (docs/14, princípio 12), mesmo padrão de
   .sk-field-checkbox. */
.dialogo-modal-campo-checkbox {
  display: flex;
  align-items: center;
  gap: var(--e2);
  margin-bottom: var(--e4);
  font-size: 0.75rem;
  color: var(--sk-muted);
}
.dialogo-modal-campo-checkbox label {
  margin-bottom: 0;
}
/* Botões lado a lado (docs/14, princípio 9); quebra em telas estreitas. */
.dialogo-modal-botoes {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--e3);
}
@media (max-width: 480px) {
  .dialogo-modal {
    width: calc(100vw - 24px);
  }
  .dialogo-modal-botoes {
    justify-content: stretch;
  }
  .dialogo-modal-botoes button {
    flex: 1;
  }
}

/* Calculadora de Multa e Juros — LOJA-10 (docs/33 §4, botão
   "Calculadora", Prompt #28). Mesmo mecanismo de <dialog> + ::backdrop
   de docs/18/docs/17, mas com campos de entrada próprios — fora do
   escopo do componente genérico de Diálogo nesta fase (docs/18 §3). */
.calc-multa-juros-modal {
  padding: 0;
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius);
  background: var(--sk-bg);
  color: var(--sk-fg);
  width: min(420px, calc(100vw - 32px));
  max-height: min(85vh, 640px);
}
.calc-multa-juros-modal[open] {
  display: flex;
  flex-direction: column;
}
.calc-multa-juros-modal::backdrop {
  background: rgba(0, 0, 0, 0.6);
}
.calc-multa-juros-modal-cabecalho {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex: none;
  padding: var(--e4) var(--e5) 0;
}
.calc-multa-juros-modal-cabecalho h2 {
  margin: 0;
  font-size: 1rem;
}
.calc-multa-juros-modal-fechar {
  background: none;
  border: none;
  color: var(--sk-muted);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  padding: var(--e1) var(--e2);
}
.calc-multa-juros-modal-fechar:hover {
  color: var(--sk-gold);
}
.calc-multa-juros-modal-corpo {
  overflow-y: auto;
  padding: var(--e3) var(--e5) var(--e5);
  font-size: 0.8125rem;
  text-align: left;
}
.calc-multa-juros-modal-aviso {
  color: var(--sk-muted);
  font-size: 0.75rem;
  margin-top: 0;
}
.calc-multa-juros-modal-erro {
  color: var(--sk-danger);
  font-size: 0.75rem;
}
.calc-multa-juros-modal-botoes {
  display: flex;
  justify-content: flex-end;
  margin: var(--e3) 0;
}
.calc-multa-juros-resultado {
  margin: 0;
  padding-top: var(--e3);
  border-top: 0.5px solid var(--sk-border);
}
.calc-multa-juros-resultado dt {
  color: var(--sk-muted);
  font-size: 0.75rem;
}
.calc-multa-juros-resultado dd {
  margin: 0 0 var(--e3);
  font-size: 0.9375rem;
}
@media (max-width: 480px) {
  .calc-multa-juros-modal {
    width: calc(100vw - 24px);
  }
}

/* Central de Ajuda (docs/17, seção 3.2). */
.sk-help-busca {
  display: flex;
  gap: var(--e3);
  margin-top: var(--e3);
}
.sk-help-busca input {
  flex: 1;
  background: color-mix(in srgb, var(--sk-fg) 4%, var(--sk-bg));
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius-sm);
  padding: var(--e2) var(--e3);
  color: var(--sk-fg);
  font-size: 0.8125rem;
  font-family: var(--sk-font-body);
}
.sk-help-artigo + .sk-help-artigo {
  margin-top: var(--e5);
  padding-top: var(--e5);
  border-top: 0.5px solid var(--sk-border);
}

/* Impressão de conteúdo marcado ".sk-imprimivel" (docs/17, item 3.1;
   generalizado para qualquer tela em 03/08/2026 — primeiro reaproveitado
   por MASTER-11, docs/05 seção 2.5) — nunca o fundo escuro do tema Pedra
   Bruta no papel: fundo branco, texto escuro, sempre, independente do tema
   ativo. Acionada por window.print() no ícone 🖨 (data-help-imprimir em
   base.html).

   Técnica "imprime só este elemento" via visibility (não display): esconde
   tudo em <body> e revela só o elemento marcado + seus descendentes — mais
   robusta que listar seletores de chrome um a um (header/nav/footer têm
   classes diferentes em telas diferentes, ex: o cabeçalho do login usa
   .sk-login-header, não .sk-header) e funciona em qualquer tela que tenha
   um container ".sk-imprimivel", sem precisar conhecer sua estrutura de
   antemão. O botão base.html adiciona "sk-imprimindo" ao <body> e
   "sk-artigo-para-imprimir" ao elemento clicado antes de chamar
   window.print(); ambos são removidos no evento "afterprint". */
@media print {
  body {
    background: #fff !important;
    color: #111 !important;
    /* Desfaz o truque de layout fixo (Prompt #60, ver comentário na regra
       `body` acima) — sem isso, `.sk-artigo-para-imprimir` mais alto que
       100vh ficaria cortado pelo `overflow: hidden` da tela. */
    height: auto !important;
    overflow: visible !important;
  }
  .sk-main {
    overflow: visible !important;
    height: auto !important;
  }

  body.sk-imprimindo * {
    visibility: hidden !important;
  }
  body.sk-imprimindo .sk-artigo-para-imprimir,
  body.sk-imprimindo .sk-artigo-para-imprimir * {
    visibility: visible !important;
  }
  body.sk-imprimindo .sk-artigo-para-imprimir {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    background: #fff;
    color: #111;
  }
  /* Botão de imprimir e widget de feedback nunca fazem sentido no papel —
     mesmo sendo descendentes do elemento marcado (portanto "visíveis" pela
     regra acima), display:none sempre vence sobre visibility:visible. */
  body.sk-imprimindo [data-help-imprimir],
  body.sk-imprimindo .sk-help-feedback {
    display: none !important;
  }

  /* O modal do Help Contextual é fixed/limitado em altura com rolagem
     interna e teria fundo escuro — neutraliza tudo isso para não virar o
     contexto de posicionamento do artigo (que usa position:absolute) nem
     cortar o conteúdo em páginas de papel. */
  .sk-help-modal {
    position: static !important;
    max-height: none !important;
    width: auto !important;
    margin: 0 !important;
    border: none !important;
    background: transparent !important;
  }
  .sk-help-modal::backdrop {
    display: none !important;
  }
  .sk-help-modal-corpo {
    overflow: visible !important;
    max-height: none !important;
  }

  .sk-help-imagem img {
    max-width: 100%;
  }

  /* Abas de formulário (docs/05, seção 2.5.2) — no papel, mostra todas as
     abas de uma vez (não só a ativa) e esconde a navegação de abas em si,
     que não faz sentido impresso. [hidden] tem display:none como padrão do
     navegador, sempre vencendo visibility:visible sozinho — precisa do
     display:block explícito aqui. */
  .sk-aba-form-conteudo[hidden],
  .sk-sub-aba-conteudo[hidden] {
    display: block !important;
  }
  .sk-abas-form,
  .sk-sub-abas {
    display: none !important;
  }
}

/* Abas de formulário (docs/05, seção 2.5.2) — CAD-03/MASTER-11: Dados
   Gerais, Endereços, Dados Jurídicos, Responsáveis, Sessões. Mecanismo
   puramente client-side (todos os campos continuam no DOM e são enviados
   juntos no POST — só a exibição alterna), então nenhuma aba perde dado
   ao trocar de outra antes de salvar. */
.sk-abas-form {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e1);
  border-bottom: 0.5px solid var(--sk-border);
  margin-bottom: var(--e4);
}
/* Realce por FUNDO na aba (docs/14, Princípio 37 — Prompt #177), mesma
   lógica de degraus já usada no menu (#107) e no Datagrid (#109): não é
   uma terceira mecânica, é a mesma.

   O DEFEITO QUE ISTO CORRIGE. Até 21/08/2026 o `:hover` daqui mudava só
   a COR do texto — e `.sk-aba-form-botao.active`, com a mesma
   especificidade (0-2-0) e vindo depois no arquivo, vencia. Resultado:
   passar o mouse sobre a aba já ativa não produzia **nenhuma** mudança
   visual (achado do #109, registrado no #110).

   POR QUE FUNDO, e não um tom de cor. Os dois realces passam a viver em
   CANAIS DIFERENTES: o **estado** ativo é cor do texto + borda inferior
   em gold; a **interação** é fundo. Sem disputa de especificidade — não
   é o hover "ganhando" do ativo, é cada um marcando o que lhe cabe.

   Isto é de natureza diferente do `docs/38` §2.8.3, que trata de realce
   de interação ficando ABAIXO do realce de estado: lá havia redução,
   aqui havia AUSÊNCIA. */
.sk-aba-form-botao {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: var(--e2) var(--e4);
  color: var(--sk-muted);
  font-size: 0.875rem;
  cursor: pointer;
  border-radius: var(--sk-radius-sm) var(--sk-radius-sm) 0 0;
  transition: background-color 120ms ease;
}
.sk-aba-form-botao:hover {
  color: var(--sk-fg);
  background: color-mix(in srgb, var(--sk-fg) 8%, var(--sk-bg));
}
.sk-aba-form-botao.active {
  color: var(--sk-gold);
  border-bottom-color: var(--sk-gold);
}
/* A aba ATIVA sob o ponteiro: mantém o gold do estado e ganha o mesmo
   fundo do hover. Escrita separada, com especificidade maior (0-3-0), de
   propósito — se um dia `.active` passar a definir `background`, esta
   regra continua vencendo e o feedback não desaparece de novo em
   silêncio. É a defesa contra a repetição exata do defeito do #110. */
.sk-aba-form-botao.active:hover {
  background: color-mix(in srgb, var(--sk-fg) 8%, var(--sk-bg));
}
/* Foco de teclado (docs/14, princípio 4 — paridade entre apontar e
   navegar): mesmo fundo do hover, MAIS o indicador de foco próprio. */
.sk-aba-form-botao:focus-visible {
  background: color-mix(in srgb, var(--sk-fg) 8%, var(--sk-bg));
  outline: 2px solid var(--sk-gold);
  outline-offset: -2px;
}
.sk-aba-form-conteudo {
  margin-bottom: var(--e4);
}

/* Alternancia lista/grafico do modal da celula do Demonstrativo (LOJA-56,
   Prompts #487 e #504): dois alternadores de exibicao (estado, nao acao) lado a
   lado. Os botoes sao `sk-btn sk-btn-secondary` e a aparencia vem dessas classes
   (o #487 tinha repetido aqui tudo o que elas dao, para fugir da regra do verbo
   do Principio 36; o #504 devolveu a classe e declarou a excecao por rotulo, docs/14).
   Aqui fica so o que e do alternador: a linha dos dois e o estado pressionado,
   com o fundo de ouro suave. So token, nenhuma cor nova. */
.sk-celula-modo {
  display: flex;
  gap: var(--e2);
  margin: var(--e4) 0;
}
.sk-celula-modo [aria-pressed="true"] {
  background: color-mix(in srgb, var(--sk-gold) 18%, var(--sk-bg));
}

.sk-sub-abas {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e1);
  margin-bottom: var(--e3);
}
.sk-sub-aba-botao {
  padding: var(--e2) var(--e3);
  font-size: 0.8125rem;
}
.sk-sub-aba-conteudo {
  border-top: 0.5px solid var(--sk-border);
  padding-top: var(--e3);
}

/* Gestão de Temas (docs/37) — MASTER-08. */
.sk-tema-seletor {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e3);
  margin-bottom: var(--e4);
}
.sk-tema-seletor select {
  min-width: 220px;
}
.sk-tema-acoes {
  align-items: flex-start;
  margin-bottom: var(--e4);
}
.sk-tema-acoes > details {
  border: 0.5px solid var(--sk-border);
  border-radius: var(--sk-radius-sm);
  padding: var(--e2) var(--e3);
}
.sk-tema-acoes summary {
  cursor: pointer;
  color: var(--sk-gold);
  font-size: 0.8125rem;
}
.sk-tema-acao-form {
  margin-top: var(--e3);
  min-width: 220px;
}
/* Restaurar tema para o padrão (docs/37 §14, Prompt #177). O selo
   "personalizado" e o ↻ por token só existem quando o valor difere do
   `valor_padrao` do catálogo — o próprio par é o indicador do que foi
   customizado, então ele não pode competir com o campo: fica miúdo, em
   `--sk-muted`, à direita. */
/* Destinos de âncora do ↻ (Prompt #178). O `scroll-margin-top` existe
   porque `.sk-main` rola sob um cabeçalho fixo (docs/14, Prompt #60):
   sem ele, o navegador encosta o alvo no topo do container e o elemento
   fica PARCIALMENTE ESCONDIDO atrás do cabeçalho — o campo restaurado
   seria justamente o que não se vê. */
#sk-tokens-de-design,
[data-tema-form-tokens] .sk-field {
  scroll-margin-top: 96px;
}
.sk-tema-tokens-cabecalho {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e3);
}
.sk-tema-restaurar-global {
  margin: 0;
}
.sk-tema-restaurar-token {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--e2);
  margin-top: -8px; /* sk-excecao: compensação negativa cola o controle no bloco acima — a escala não tem passo negativo */
  margin-bottom: var(--e3);
}
.sk-tema-customizado {
  font-size: 0.6875rem;
  color: var(--sk-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.sk-tema-categoria {
  margin: var(--e5) 0 var(--e2);
  font-size: 0.9375rem;
  color: var(--sk-gold);
  border-bottom: 0.5px solid var(--sk-border);
  padding-bottom: var(--e2);
}
.sk-tema-categoria:first-of-type {
  margin-top: 0;
}
/* Campo de Cor: input de texto (aceita rgba(), que <input type="color">
   não representa) + swatch nativo companheiro, sincronizados via JS
   (telefone-mascara.js/cpf-cnpj-mascara.js são o precedente de máscara
   client-side deste projeto; aqui o "sincronizar dois campos" é o
   equivalente para cor). */
.sk-tema-campo-cor {
  display: flex;
  gap: var(--e2);
  align-items: center;
}
.sk-tema-campo-cor input[type="text"] {
  flex: 1;
}
.sk-tema-campo-cor input[type="color"] {
  width: 37px;
  height: 37px;
  padding: 2px;
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius-sm);
  background: var(--sk-surface);
  cursor: pointer;
}

.sk-tema-layout {
  display: flex;
  gap: var(--e6);
  align-items: flex-start;
  flex-wrap: wrap;
}
.sk-tema-layout > .sk-tema-form-coluna {
  flex: 1 1 360px;
  min-width: 0;
}
.sk-tema-preview {
  flex: 1 1 320px;
  min-width: 0;
  position: sticky;
  top: 16px;
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius);
  padding: var(--e4);
  background: var(--sk-bg);
}
.sk-tema-preview-titulo {
  font-size: 0.8125rem;
  color: var(--sk-muted);
  margin: 0 0 var(--e3);
}
.sk-tema-preview-corpo > * {
  margin-bottom: var(--e4);
}
.sk-tema-preview-botoes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e2);
}
.sk-tema-preview-dialogo {
  position: static;
  display: block;
  width: auto;
  max-width: none;
  margin: 0;
  padding: 0;
  border: none;
  background: transparent;
  color: inherit;
}
.sk-tema-preview-dialogo .dialogo-modal-corpo {
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius);
  padding: var(--e4);
  background: var(--sk-surface-solid);
}

/* Combobox Editável com Typeahead (docs/49 §2, Prompt #111) — substitui o
   <datalist> nativo, que só oferecia lista filtrada, sem typeahead inline
   nem controle de teclado. O invólucro é criado pelo próprio JS
   (combobox.js), então nenhuma tela precisa mudar de estrutura. */
.sk-combobox {
  position: relative;
}
.sk-combobox-menu {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 20;
  max-height: 260px;
  overflow-y: auto;
  margin: 2px 0 0;
  padding: var(--e1);
  list-style: none;
  background: var(--sk-surface-solid);
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
}
.sk-combobox-item {
  padding: var(--e2) var(--e3);
  border-radius: var(--sk-radius-sm);
  font-size: 0.8125rem;
  cursor: pointer;
}
/* Mesma escala de realce do menu (docs/20 §7.1.1) e do Datagrid (docs/38
   §2.8.3): o item destacado pelo teclado é o "estado", o hover é a
   "interação" e fica acima dele — nunca abaixo. */
.sk-combobox-item:hover {
  background: color-mix(in srgb, var(--sk-fg) 12%, var(--sk-bg));
}
.sk-combobox-item--ativo {
  background: color-mix(in srgb, var(--sk-fg) 8%, var(--sk-bg));
}
.sk-combobox-item--ativo:hover {
  background: color-mix(in srgb, var(--sk-fg) 18%, var(--sk-bg));
}
.sk-combobox-vazio {
  padding: var(--e2) var(--e3);
  font-size: 0.75rem;
  color: var(--sk-muted);
}

/* Diálogo da navegação rápida por código de tela (docs/14, princípio 3 —
   Prompt #129). `<dialog>` nativo pelo mesmo motivo do Menu Principal e
   do Help Contextual: foco preso e fechar com Esc de graça.

   Ancorado no ALTO da viewport, não centralizado: o gatilho está no
   rodapé, e um diálogo centralizado com um menu de até 260px abrindo
   para baixo encostaria na borda inferior justamente nas telas baixas. */
.sk-navegar-tela {
  margin: 12vh auto auto;
  padding: 0;
  width: 460px;
  max-width: 92vw;
  border: 0.5px solid var(--sk-border-strong);
  border-radius: var(--sk-radius);
  background: var(--sk-surface-solid);
  color: var(--sk-fg);
  /* O menu do combobox é `position: absolute` e cresce para fora do
     corpo do diálogo — qualquer recorte aqui o cortaria pela metade. */
  overflow: visible;
}
.sk-navegar-tela::backdrop {
  background: rgba(0, 0, 0, 0.6);
}
.sk-navegar-tela-corpo {
  padding: var(--e4) var(--e5) var(--e5);
}
.sk-navegar-tela-rotulo {
  display: block;
  font-size: 0.75rem;
  color: var(--sk-muted);
  margin-bottom: var(--e2);
}
.sk-navegar-tela-campo {
  width: 100%;
}
.sk-navegar-tela-dica {
  margin: var(--e2) 0 0;
  font-size: 0.75rem;
  color: var(--sk-muted);
}
.sk-navegar-tela-dica code {
  font-size: 0.75rem;
  color: var(--sk-fg);
}
.sk-navegar-tela-acoes {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--e4);
}

/* Campo preenchido por sugestão do sistema (docs/49 §5, Prompt #122) —
   o auto-preenchimento contextual marca com data-sugerido, e o realce
   some ao primeiro toque do usuário (o JS remove o atributo).

   Realce DISCRETO, como a §5 pede: uma borda à esquerda e um fundo
   levemente destacado, derivados dos tokens do Tema via color-mix (o
   mesmo padrão do zebrado do Datagrid, docs/38 §2.7) — funciona em Tema
   claro e escuro, diferente de um rgba fixo. Nunca a cor de erro nem a
   de aviso: o valor está correto, só não foi digitado pela pessoa. */
[data-sugerido] {
  background: color-mix(in srgb, var(--sk-gold) 8%, var(--sk-bg));
  border-left: 2px solid var(--sk-gold);
}

/* Aba de formulário que contém erro de validação (docs/14, princípio 19 —
   Prompt #126). Marcador DISCRETO: um ponto antes do rótulo, na cor de
   erro, sem mudar o peso nem o tamanho do texto — a aba com erro precisa
   ser notada, não gritar.

   Vem DEPOIS de `.sk-aba-form-botao.active` de propósito: com a mesma
   especificidade (0-2-0), a ordem no arquivo é o que decide, e a aba
   ativa que contém erro deve continuar mostrando o marcador. */
.sk-aba-form-botao--com-erro::before {
  content: "•";
  color: var(--sk-danger-fg);
  margin-right: var(--e2);
  font-weight: 700;
}


/* ---------------------------------------------------------------------
   Gráfico em tela (docs/54, docs/55 §4.2) — o primeiro consumidor real do
   componente, a guia "Lançamentos" da LOJA-34.

   Nada de cor aqui: TODA cor do gráfico vem de token, lida pelo glue em
   `--sk-grafico-serie-*` (docs/54 §5.1). O que este bloco resolve é
   GEOMETRIA, que é o que o canvas não resolve sozinho.
   --------------------------------------------------------------------- */
.sk-grafico-cabecalho {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e3);
}

/* A engrenagem é um `<details>`; sem `position: relative` o painel dela
   empurraria o gráfico para baixo ao abrir, em vez de flutuar sobre ele. */
.sk-grafico-config {
  position: relative;
}
.sk-grafico-config > summary {
  list-style: none;
  cursor: pointer;
}
/* O triângulo nativo do <summary> ao lado de um ícone de engrenagem são
   dois indicadores para a mesma coisa. */
.sk-grafico-config > summary::-webkit-details-marker {
  display: none;
}
.sk-grafico-config-form {
  position: absolute;
  right: 0;
  z-index: 5;
  min-width: 220px;
  padding: var(--e3);
  border: 1px solid var(--sk-border);
  border-radius: var(--sk-radius-sm);
  background: var(--sk-surface-solid, var(--sk-bg));
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.28);
}

/* ALTURA EXPLÍCITA, e é obrigatória: o componente configura a biblioteca
   com `responsive: true` + `maintainAspectRatio: false` (docs/54 §3), e
   nessa combinação o canvas assume a altura do PAI. Sem altura no pai, o
   pai mede a do canvas, o canvas mede a do pai, e o gráfico colapsa para
   zero — some sem erro nenhum no console. */
.sk-grafico-area {
  position: relative;
  height: 320px;
  margin: var(--e4) 0 var(--e6);
}

/* Texto de estado vazio — mesma família visual de `.sk-field-dica`, sem
   ser dica de campo nenhum. */
.sk-vazio {
  color: var(--sk-muted);
  font-style: italic;
}

/* Explicação de ESTADO dentro da própria tela (Prompt #294) — não é
   `.sk-vazio` e não é `messages`.

   `.sk-vazio` é texto esmaecido em itálico, para dizer "não há nada aqui";
   as `messages` do Django são de SESSÃO, somem no próximo carregamento.
   Este bloco é permanente enquanto o estado durar, e carrega uma
   INSTRUÇÃO que o obreiro precisa ler e agir — esmaecer seria enterrar
   justamente o que ele veio buscar.

   Nasceu do achado de validação de Wagner: "Meus Dados" devolvia 404 a
   quem tem vínculo ativo sem cadastro de Obreiro. É o Princípio 41 na
   forma mais literal — toda ação produz um CAMINHO; o pior caso deixa de
   ser uma porta fechada e passa a ser uma porta com o endereço de quem
   tem a chave. */
.sk-aviso-inline {
  background: var(--sk-surface);
  border-left: 2px solid var(--sk-gold);
  border-radius: var(--sk-radius-sm);
  padding: var(--e3) var(--e4);
  margin-bottom: var(--e5);
}
.sk-aviso-inline p {
  margin: 0;
  color: var(--sk-fg);
  font-size: 0.8125rem;
  line-height: 1.5;
}


/* ---------------------------------------------------------------------
   Grade HIERÁRQUICA (LOJA-09, Prompt #146) — categoria em nível zero,
   subcategoria indentada logo abaixo da mãe.

   O recuo é do CONTEÚDO da primeira célula, nunca da linha inteira: uma
   linha deslocada quebraria o alinhamento das demais colunas, e a grade
   deixaria de se ler como tabela.

   O hífen (`.sk-hierarquia-marca`) é `aria-hidden`: para quem ouve a
   tela ele seria ruído, e o nível já é dito pela ordem de leitura.
   --------------------------------------------------------------------- */
.sk-linha-filha > td:first-child {
  padding-left: var(--e6);
}
.sk-hierarquia-marca {
  color: var(--sk-muted);
  user-select: none;
}

/* Listbox do mestre-detalhe (LOJA-09) — aparência de caixa de seleção,
   comportamento de lista de links: navegar no `change` de um `<select>`
   levaria embora quem só estivesse percorrendo as opções pelo teclado. */
.sk-mestre-detalhe {
  margin-top: var(--e5);
}
.sk-listbox {
  list-style: none;
  margin: var(--e2) 0 var(--e3);
  padding: var(--e1);
  max-height: 220px;
  overflow-y: auto;
  border: 1px solid var(--sk-border);
  border-radius: var(--sk-radius-sm);
  background: var(--sk-surface);
}
.sk-listbox li {
  margin: 0;
}
.sk-listbox a {
  display: block;
  padding: var(--e2) var(--e3);
  border-radius: var(--sk-radius-sm);
  text-decoration: none;
  color: var(--sk-fg);
}
.sk-listbox a:hover,
.sk-listbox a:focus-visible {
  background: color-mix(in srgb, var(--sk-gold) 14%, transparent);
}
.sk-listbox-vazio {
  padding: var(--e2) var(--e3);
  color: var(--sk-muted);
  font-style: italic;
}

/* ---------------------------------------------------------------------
   Catálogo Vivo (MASTER-24, docs/59 §9 fase 1, Prompt #185).
   --------------------------------------------------------------------- */
/* Amostra ESTÁTICA da família "Abas" — mesmo layout de `.sk-abas-form`,
   mas sem ser a classe em si: o mecanismo de abas-form.js opera
   GLOBALMENTE sobre `.sk-abas-form > .sk-aba-form-botao`, e esta amostra
   não pode disparar aquele mecanismo (ver comentário no template). Os
   BOTÕES usam a classe real `.sk-aba-form-botao` — só o contêiner é
   próprio. */
.sk-abas-form-amostra {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e1);
  border-bottom: 0.5px solid var(--sk-border);
  margin-bottom: var(--e4);
}
.sk-catalogo-cores-grade {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--e3);
}
.sk-catalogo-cor-amostra {
  display: flex;
  align-items: center;
  gap: var(--e2);
}
.sk-catalogo-cor-swatch {
  display: inline-block;
  width: 28px;
  height: 28px;
  border-radius: var(--sk-radius-sm);
  border: 0.5px solid var(--sk-border-strong);
  flex: none;
}

/* ---------------------------------------------------------------------
   Demonstrativo Mensal por Categoria (LOJA-56, docs/83 §4.4, Prompt #483).
   So o que as classes do Datagrid nao alcancam: a hierarquia visual de uma
   grade que nao pagina nem ordena. O recuo da subcategoria e o da
   `.sk-linha-filha` (LOJA-09); a rolagem e o congelamento sao os da
   `.sk-datagrid--cabecalho-fixo` (docs/38 §2.25). Nenhuma cor nem medida
   solta: so token.
   --------------------------------------------------------------------- */
.sk-demo-bloco th {
  text-align: left;
  color: var(--sk-gold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: var(--sk-surface);
}
.sk-demo-categoria > td:first-child {
  font-weight: 600;
}
.sk-demo-inativa > td:first-child {
  color: var(--sk-muted);
}
.sk-demo-total > td,
.sk-demo-resultado > td {
  font-weight: 600;
  border-top: 0.5px solid var(--sk-border-strong);
}
.sk-demo-resultado > td {
  border-bottom: 0.5px solid var(--sk-border-strong);
}
