/* ==========================================================================
   Controle de Abastecimento e Demandas — sistema de design
   --------------------------------------------------------------------------
   Um arquivo, sem dependências e sem build. Mobile-first: o motorista lança
   o abastecimento em pé no posto, com uma mão, então os alvos de toque e a
   hierarquia são pensados para o celular primeiro — o desktop herda e expande.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. Tipografia embarcada
   -------------------------------------------------------------------------- */

/* Inter variável, subset latin, servida do próprio domínio — ver
   static/vendor/README.md para versão, licença e sha256.

   O caminho é relativo ao CSS, e não um `{% static %}`: o WhiteNoise não
   reescreve `url()` como o ManifestStorage faria, e um caminho relativo
   continua certo em qualquer prefixo de URL.

   `swap` porque isto é tela de trabalho de campo, aberta em 4G de estrada: o
   texto aparece na fonte do sistema e troca quando a Inter chega, em vez de a
   página ficar em branco esperando o arquivo. */
@font-face {
  font-family: "Inter var";
  src: url("../vendor/inter/inter-latin-wght-normal.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
  color-scheme: light;

  /* Superfícies e texto.
     O fundo é levemente azulado para o branco dos cartões se destacar dele
     sozinho, sem precisar de borda forte.

     Os dois cinzas desceram para caber a AA (4,5:1) nos dois fundos em que são
     usados, e não só no branco. O par que aperta é `--texto-fraco` sobre
     `--superficie-alt`, que é o cabeçalho de toda tabela: em #8b93a1 dava
     2,7:1, e em #6e7787 ainda dava 3,99:1. `--texto-suave` desceu junto, para a
     hierarquia entre os dois continuar visível. */
  --fundo:            #f4f6fa;
  --superficie:       #ffffff;
  --superficie-alt:   #eef1f7;
  --borda:            #e3e7ef;
  --borda-forte:      #c8cfdb;
  --texto:            #101724;
  --texto-suave:      #4d5666;
  --texto-fraco:      #626b7a;

  /* Acento */
  --primaria:         #2563eb;
  --primaria-forte:   #1d4ed8;
  --primaria-suave:   #eef3ff;
  --primaria-contraste:#ffffff;

  /* Estados — sempre usados junto de ícone ou rótulo, nunca só a cor */
  --ok:               #0f7b3f;
  --ok-suave:         #e6f6ec;
  --atencao:          #93610a;
  --atencao-suave:    #fdf3e0;
  --erro:             #b42318;
  --erro-suave:       #fdecea;
  --info:             #0b6b8f;
  --info-suave:       #e6f3f8;

  /* Séries de gráfico — ordem fixa, nunca reatribuída por valor.
     O 7 é do ARLA, e fica no fim de tudo: é a única série que não é um
     combustível, e o slot existe para ela e não para uma sétima categoria. */
  --serie-1:          #2a78d6;
  --serie-2:          #e07b39;
  --serie-3:          #0f9d8f;
  --serie-4:          #8b5cf6;
  --serie-5:          #d6455d;
  --serie-6:          #7a8290;
  --serie-7:          #15803d;

  /* Espaçamento — escala de 4px */
  --e-1: 0.25rem;  --e-2: 0.5rem;   --e-3: 0.75rem;
  --e-4: 1rem;     --e-5: 1.5rem;   --e-6: 2rem;
  --e-7: 3rem;     --e-8: 4rem;

  /* Raios */
  --r-sm: 9px;  --r: 14px;  --r-lg: 20px;  --r-full: 999px;

  /* Altura de um controle de formulário. O mesmo 44 aparece no campo, no botão e
     no rótulo que fica ao lado deles: três lugares que precisam concordar para a
     linha ficar alinhada, e que divergiriam em silêncio se cada um trouxesse o
     seu próprio número. É também o alvo de toque confortável, o que serve tanto
     ao dedo no celular quanto ao clique no desktop. */
  --altura-controle: 44px;

  /* Sombras — mais difusas e mais fracas que as antigas. O cartão se separa do
     fundo pelo raio e pela cor, não por uma borda dura; a sombra só dá o
     degrau. Sombra forte em cartão branco sobre fundo claro é o que envelhece
     uma interface. */
  --sombra-1: 0 1px 2px rgb(16 24 40 / 0.04), 0 1px 3px rgb(16 24 40 / 0.03);
  --sombra-2: 0 2px 6px rgb(16 24 40 / 0.06), 0 1px 2px rgb(16 24 40 / 0.04);
  --sombra-3: 0 8px 24px rgb(16 24 40 / 0.10), 0 2px 6px rgb(16 24 40 / 0.05);

  /* Tipografia.
     Os pesos pedidos no arquivo são quebrados — 550, 620, 650, 680 —, e é a
     Inter **variável** que os realiza: uma fonte estática arredondaria todos
     para o peso instalado mais próximo, e a hierarquia fina sumiria.

     `--fonte-num` mantém a mono como fallback, em vez de cair para a sans
     genérica: se a Inter não carregar, o que importa nos números é a grade de
     dígitos de largura fixa, e é isso que a mono devolve. Todos os lugares que
     a usam — menos `.veiculo-opcao__placa` — já pedem `tabular-nums` ao lado. */
  --fonte: "Inter var", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
           "Helvetica Neue", Arial, "Noto Sans", sans-serif;
  --fonte-num: "Inter var", ui-monospace, "SF Mono", "Cascadia Mono", Menlo,
               Consolas, monospace;

  /* Layout.
     `--altura-topo` agora mede a **barra de navegação**, e não o cabeçalho
     inteiro: é ela que fica `sticky`, e é a altura dela que o
     `scroll-padding-top` tem de descontar. A faixa alta não entra na conta
     porque sai da tela ao rolar.

     Em telas grandes o teto fixo de 1200px deixava uma margem morta enorme:
     360px de cada lado num monitor de 1920px e 680px num de 2560px. O
     `clamp()` mantém o valor antigo em qualquer tela até ~1460px de largura
     e, daí para cima, deixa o conteúdo acompanhar a janela — 82% dela, com
     teto de 1720px. Acima de ~2100px a margem volta a crescer, de propósito:
     linha de texto longa demais cansa a leitura, e o ganho de largura já não
     paga isso. */
  --largura-max: clamp(1200px, 82vw, 1720px);
  --altura-topo: 68px;

  --transicao: 140ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* O tema escuro é decidido por `data-tema`, escrito no `<html>` **antes da
   primeira pintura** pelo script inline do `<head>`.

   Não é `prefers-color-scheme`: com dois blocos — um automático, pelo sistema,
   e outro manual — a escolha do usuário perderia para o tema do sistema em
   metade das combinações, e na outra metade quem decidiria seria a ordem e a
   especificidade dos seletores, que não é uma decisão. Um bloco só, e um
   atributo que sempre diz qual dos dois temas vale.

   O `/` depois de `color-scheme` deixou de existir junto com a media query:
   quem não tem JavaScript não recebe `data-tema`, e a página fica no tema
   claro. É o pior caso documentado, e é melhor que um tema pela metade. */
:root[data-tema="escuro"] {
  color-scheme: dark;

  --fundo:            #0e1116;
  --superficie:       #171c23;
  --superficie-alt:   #1f252e;
  --borda:            #2b333d;
  --borda-forte:      #3c4653;
  --texto:            #e9ecf1;
  --texto-suave:      #a6afbc;
  /* Subiu de #6f7986: sobre a superfície escura ele dava 3,9:1, abaixo do
     mínimo para texto normal. */
  --texto-fraco:      #838d9b;

  --primaria:         #4b8dfa;
  --primaria-forte:   #6ba1fb;
  --primaria-suave:   #16233a;
  --primaria-contraste:#0f1216;

  --ok:               #4ade80;
  --ok-suave:         #10261a;
  --atencao:          #fbbf24;
  --atencao-suave:    #2a2110;
  --erro:             #f87171;
  --erro-suave:       #2c1618;
  --info:             #5cc4e8;
  --info-suave:       #10242c;

  /* No escuro as séries precisam de mais luminosidade para manter contraste */
  --serie-1:          #3987e5;
  --serie-2:          #e08a4a;
  --serie-3:          #2bb3a3;
  --serie-4:          #a78bfa;
  --serie-5:          #e0687c;
  --serie-6:          #8b93a1;
  --serie-7:          #22c55e;

  --sombra-1: 0 1px 2px rgb(0 0 0 / 0.3);
  --sombra-2: 0 1px 3px rgb(0 0 0 / 0.4), 0 1px 2px rgb(0 0 0 / 0.3);
  --sombra-3: 0 4px 12px rgb(0 0 0 / 0.45), 0 1px 3px rgb(0 0 0 / 0.3);
}

/* --------------------------------------------------------------------------
   2. Reset
   -------------------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;

  /* O trilho da barra de rolagem fica sempre reservado. Sem isto, trocar de
     uma tela curta para uma longa alarga o viewport em ~15 px e **todo** o
     conteúdo se desloca: o botão da direita anda, o que é centrado anda
     metade. É pequeno, e por isso mesmo se percebe como o layout "pulando"
     ao navegar. `stable` reserva o trilho nas duas telas; onde a rolagem é
     sobreposta (celular, macOS), a propriedade não faz nada. */
  scrollbar-gutter: stable;

  /* A barra de navegação é `sticky` e cobre o alto da página. Sem esta linha, o
     link de pular e qualquer âncora param **debaixo** dela: o alvo existe e
     recebe o foco, mas nasce escondido atrás da barra. `scroll-padding-top` é
     do contêiner que rola — o `html` —, não do alvo, então vale para todo
     `#id` sem que cada um declare o seu.

     A folga é a altura da barra, e ela tem **três** parcelas. As duas
     primeiras são a altura declarada da barra e um respiro; a terceira é o
     recorte da tela, e não é detalhe: o `base.html` usa `viewport-fit=cover` e
     a `.topo-barra` leva `padding-top: env(safe-area-inset-top)` para não
     nascer sob a barra do sistema. Num iPhone com Dynamic Island isso passa de
     68px para mais de 110px, e uma folga que só somasse as duas primeiras
     deixaria a âncora 40px debaixo da barra — exatamente o defeito que esta
     linha cura, de volta, nos aparelhos onde ele mais incomoda.

     Não há resto: a `.topo-barra` não quebra em duas linhas — numa janela
     estreita o menu rola na horizontal, com altura fixa. É por isso que a
     conta fecha na altura declarada, e não precisa de uma medida por script. */
  scroll-padding-top: calc(
    var(--altura-topo) + var(--e-3) + env(safe-area-inset-top)
  );
}

body {
  min-height: 100dvh;
  background: var(--fundo);
  color: var(--texto);
  font-family: var(--fonte);
  font-size: 1rem;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, picture, svg, video {
  display: block;
  max-width: 100%;
  height: auto;
}

input, button, textarea, select {
  font: inherit;
  color: inherit;
}

button { cursor: pointer; }

/* O duplo-toque em cima de um controle não amplia a página. **Não** é pelo
   atraso de ~300 ms do primeiro toque: esse o `<meta viewport>` de `base.html`
   já elimina, e o atraso só existe quando a largura do viewport é fixa. O que
   sobra é a outra face do mesmo gesto — com o polegar, bater duas vezes no
   mesmo botão por engano ampliava a tela e tirava do lugar quem estava
   lançando no posto.
   O `input` fica **de fora** de propósito: ali o duplo-toque é o gesto de
   selecionar a palavra, e não há atraso nenhum a compensar. O
   pinçar-para-ampliar continua valendo em todo lugar. */
a, button, [role="button"] {
  touch-action: manipulation;
}

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

/* Foco visível e consistente — requisito de acessibilidade, não enfeite */
:focus-visible {
  outline: 2px solid var(--primaria);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* --------------------------------------------------------------------------
   3. Tipografia
   -------------------------------------------------------------------------- */

h1, h2, h3, h4 {
  line-height: 1.25;
  font-weight: 650;
  letter-spacing: -0.011em;
  /* Reparte as palavras entre as linhas em vez de deixar uma sozinha na
     última. O navegador limita o efeito aos títulos curtos e cai fora sozinho
     nos longos, então não há o que dosar aqui. */
  text-wrap: balance;
}

h1 { font-size: clamp(1.5rem, 1.2rem + 1.2vw, 1.875rem); }
h2 { font-size: clamp(1.2rem, 1rem + 0.8vw, 1.375rem); }
h3 { font-size: 1.0625rem; }
h4 { font-size: 0.9375rem; }

small, .texto-pequeno { font-size: 0.8125rem; }

.texto-suave { color: var(--texto-suave); }
.texto-fraco { color: var(--texto-fraco); }

/* Números que se alinham em colunas (tabelas, valores, odômetro) */
.num {
  font-family: var(--fonte-num);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

/* --------------------------------------------------------------------------
   4. Layout
   -------------------------------------------------------------------------- */

.app {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
}

.container {
  width: 100%;
  max-width: var(--largura-max);
  margin-inline: auto;
  padding-inline: var(--e-4);
}

@media (min-width: 768px) {
  .container { padding-inline: var(--e-5); }
}

.conteudo {
  flex: 1;
  padding-block: var(--e-5) var(--e-8);
}

/* Container de formulário padronizado.
   Ocupa 100% da largura do container padrão da página, garantindo que
   o cabeçalho da página, os cartões de agrupamento de campos e a barra de botões
   sigam rigorosamente a mesma lógica visual, margens e proporções de todas as telas. */
.formulario-container,
.conteudo--formulario {
  width: 100%;
  max-width: 100%;
}

/* Variante estreita para telas de autenticação e fluxos focados (ex: login) */
.formulario-container--estreito {
  width: 100%;
  max-width: 420px;
  margin-inline: auto;
  padding-top: var(--e-6);
}

.pilha       { display: flex; flex-direction: column; gap: var(--e-4); }
.pilha-justa { display: flex; flex-direction: column; gap: var(--e-2); }
.pilha-ampla { display: flex; flex-direction: column; gap: var(--e-5); }

.linha {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  flex-wrap: wrap;
}
.linha-entre { justify-content: space-between; }
.linha-fim { justify-content: flex-end; }

/* Cabeçalho de tela: título e explicação de um lado, ações do outro.

   Duas coisas que o `.linha-entre` sozinho não dava. A primeira é o alinhamento
   pelo **topo**: centrado, uma explicação que cresce — mês sem dado, por
   exemplo — mexia na posição das ações a cada troca de mês.

   A segunda é a largura, e era a que realmente empurrava os botões para baixo.
   Um parágrafo é um item de flex, e a largura-base de um item de flex é a do
   conteúdo **sem quebra** — o texto todo numa linha só. Quando essa linha
   passava do que sobrava depois dos botões, o flex não encolhia nada: ele
   quebrava a **linha**, e as ações iam inteiras para baixo. O `flex: 1 1 0` põe
   a base em zero e deixa a sobra virar a largura; o `min-width: 0` é o que
   autoriza o texto a encolher abaixo do próprio conteúdo, que é o que a quebra
   de linha exige. Sem ele, `0` como base continua sendo um piso de
   `max-content`.

   O `flex: 0 0 auto` nas ações é o outro lado do trato: elas nunca encolhem
   nem crescem, então "Novo abastecimento" não vira um botão de três linhas
   quando a tela aperta.

   A base de `22rem` é o **ponto de quebra**, e não uma largura. Em tela larga,
   a explicação fica com toda a sobra e os botões ficam na mesma linha. Quando
   os 22rem da explicação mais os botões passam do que há, o `flex-wrap` do
   `.linha` põe as ações na linha de baixo e devolve a largura inteira ao texto.
   Sem essa base — com `flex-basis: 0` — o texto cederia até caber, e no celular
   sobrava uma coluna de quatro palavras por linha ao lado de dois botões. */
.cabecalho-pagina {
  align-items: flex-start;
  gap: var(--e-3) var(--e-4);
}
.cabecalho-pagina > :first-child { flex: 1 1 22rem; min-width: 0; }
/* O `max-width: 100%` é o que impede a rolagem lateral, e o `0 0 auto` acima é
   quem a causava. O `flex-wrap` do `.linha` **põe** as ações na linha de baixo,
   como o comentário lá em cima diz — mas o item desce com a largura natural que
   já tinha, porque com `flex-basis: auto` quem manda na largura dele é o
   conteúdo, e o conteúdo nunca foi constrangido à largura da coluna.

   Medido em Chrome headless com a Inter carregada, na moldura de 320px: as ações
   ocupam **317px** a partir de `l=16`, isto é, terminam em **`r=333`**, enquanto
   o container tem **288px** — 13px de rolagem lateral no painel, exatamente o
   `scrollWidth=333` contra `clientWidth=320`. Com o teto, o item cai para a
   largura da coluna, os dois botões embrulham pelo `flex-wrap: wrap` que o
   `.linha` já tinha, e a sobra vai a zero.

   A 360px não havia sobra nenhuma — os 317px cabiam nos 328px do container —, e
   é por isso que o defeito ficou escondido: ele só aparece na tela mais estreita.
   O que este teto **não** muda é a intenção declarada lá em cima: cada botão
   continua sem encolher; quem passou a ter limite é o recipiente deles. */
.cabecalho-pagina > :last-child { flex: 0 0 auto; max-width: 100%; }

.grade {
  display: grid;
  gap: var(--e-4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}

/* Linha de indicadores usa coluna mais estreita que a grade comum.
   Com os 260px de cima, cinco indicadores cabiam quatro por linha e o quinto
   caía sozinho, deixando um vão do tamanho de três cartões. A 172px os seis
   entram lado a lado no desktop: 6×172 + 5×16 = 1112, contra os **1152px**
   úteis do container na largura de referência (até ~1463px de janela) —
   medidos no navegador, e não estimados. Acima disso o container cresce com
   `--largura-max`, e a conta só melhora.

   Os 180px da primeira tentativa não cabiam: 6×180 + 5×16 = 1160 passa dos
   1152 por oito pixels, e a linha quebrava com o sexto indicador sozinho na
   segunda fileira. É uma diferença que não se vê na conta de cabeça e que a
   tela mostrou na hora.

   Medido no navegador: cabem seis trilhas de 1920px até ~1175px de janela;
   abaixo disso a grade cai para cinco e a sexta desce. A 1366px e a 1200px,
   que é onde o escritório trabalha, qualquer contagem até seis fica numa
   fileira só.

   O seletor é de propósito: só a grade cujos filhos são indicadores muda. A
   grade de cartões continua com os 260px, porque cartão de conteúdo precisa da
   largura.

   **A grade do painel não passa mais por aqui.** Ela precisa de fileiras
   iguais, e o `auto-fit` não faz isso: enche a trilha até não caber e deixa o
   resto sozinho na última fileira. Os oito cartões do painel têm contagem fixa
   em `.grade--painel`, logo abaixo — quem continua usando esta regra são as
   grades de indicador das telas de detalhe e a dos totalizadores do relatório,
   todas com cinco cartões ou menos, e por isso todas numa fileira só. */
@supports selector(:has(*)) {
  .grade:has(> .indicador) {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 172px), 1fr));
  }
}

/* A grade do painel: **fileiras iguais**, e para isso contagem fixa.

   A grade comum enche a trilha até não caber (`auto-fit`) e joga o que sobra
   sozinho na última fileira — com os oito indicadores do painel daria seis em
   cima e dois embaixo, um vão de quatro cartões no fim. Aqui as contagens são
   explícitas, e as três divisões de oito fecham sem resto: 4 + 4 no desktop,
   2 + 2 + 2 + 2 na faixa do tablet, 1 × 8 no celular.

   Duas colunas na faixa do tablet, e não três, pelo mesmo motivo: oito não é
   divisível por três, e a terceira fileira sairia com dois cartões.

   `.grade.grade--painel`, e não `.grade--painel` sozinho: a regra de
   indicadores acima é `.grade:has(> .indicador)`, que tem a especificidade de
   duas classes e venceria esta, que tem uma. O sintoma de perder seria
   silencioso — CSS não avisa quando um seletor perde a disputa —, e o painel
   voltaria ao `auto-fit` com a última fileira quebrada, sem nada acusar. Com
   as duas classes a especificidade empata com a dela, e aí quem decide é a
   ordem do arquivo: esta vem depois.

   Medido no Chrome, a 1366px de janela: as quatro trilhas medem **276px** cada,
   sobre os 1152px da grade. Na grade comum, os mesmos oito cartões mediriam
   179px e ficariam **6 + 2** — e eram 6 + 3 com os nove de antes, que é o vão
   que se vê no fim da última fileira.

   E as quatro trilhas só são iguais até um certo tamanho de número, porque o
   mínimo de cada uma é o *min-content* do cartão (`1fr` é `minmax(auto, 1fr)`).
   Medido, varrendo o valor: a 1366px tudo mede 276px até R$ 999.999,99, e
   R$ 1.000.000,00 já alarga a primeira trilha e encolhe as outras três; a
   1024px o mesmo acontece a partir de R$ 100.000,00. Isso é de propósito, e é o
   lado certo do trade-off: o `1fr` **deixa o cartão crescer** para caber o
   número, em vez de vazá-lo. A alternativa `minmax(0, 1fr)` igualaria as
   trilhas e faria o número transbordar do cartão — medido a 1024px, 46px para
   fora —, e um número de dinheiro cortado ou por cima do vizinho é pior do que
   um cartão mais largo. O maior mês já lançado no sistema é R$ 3.423,17, umas
   trinta vezes abaixo do limiar mais baixo; fica registrado para quem um dia
   vir o cartão de destaque mais largo e não souber por quê. */
.grade.grade--painel {
  grid-template-columns: 1fr;
}

@media (min-width: 640px) {
  .grade.grade--painel { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1024px) {
  .grade.grade--painel { grid-template-columns: repeat(4, 1fr); }
}

/* A grade dos comprovantes, e a diferença está em `auto-fill` contra o
   `auto-fit` da grade comum.

   Com `auto-fit`, as trilhas que sobram **colapsam** e as que ficaram dividem o
   espaço: um lançamento com **um** comprovante só levava o cartão à largura
   inteira do container, e a imagem dentro dele — `width: 100%` sobre um
   `aspect-ratio: 4/3` — saía com **1150×863px** nos 1152px úteis do desktop.
   Maior que a tela de um celular, para a foto de um recibo.

   Com `auto-fill` as trilhas vazias continuam existindo e segurando lugar, e o
   cartão para no tamanho da trilha: **274×206px**, seja um comprovante ou seis.
   O vão à direita é o preço, e é barato: melhor uma faixa vazia do que o recibo
   esticado.

   Tudo medido no Chrome, e não estimado. O caso de seis comprovantes mede
   276px **antes e depois** — a regra só toca o caso esparso, que é o defeito. O
   `min(100%, 260px)` do mínimo é o que deixa a trilha caber sozinha no celular:
   a 358px de container o cartão mede 358px nos dois casos, ou seja, o celular
   não muda. */
.grade--comprovantes {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr));
}

/* --------------------------------------------------------------------------
   5. Cabeçalho
   -------------------------------------------------------------------------- */

/* A faixa alta — foto, marca e painel. Ela **não** é `sticky`: rola junto com
   a página, e quem fica preso no alto é a `.topo-barra` logo abaixo. Uma faixa
   de ~170px presa comeria um quinto de um celular e esconderia o começo de
   toda tela. */
.topo { position: relative; }

.topo__faixa {
  position: relative;
  overflow: hidden;
  background: linear-gradient(105deg,
    var(--superficie) 0%,
    color-mix(in srgb, var(--primaria-suave) 70%, var(--superficie)) 100%);
}

/* A foto do cabeçalho (`static/img/cabecalho.jpg`, 1600 px de largura — o
   bastante para a faixa em tela 2x, sem os 1,8 MB do original). Ela é uma
   camada e não um `background` do container porque precisa da máscara: o
   `radial-gradient` é o que faz a borda esquerda **curva**, em vez de um corte
   reto no meio da faixa.

   `inset: 0 0 0 auto` com largura declarada, e não `right: 0` sozinho: é a
   largura que decide o quanto da faixa a foto cobre, e ela muda no celular. */
.topo__faixa::before {
  content: "";
  position: absolute;
  inset: 0 0 0 auto;
  width: min(54%, 680px);
  background: url("../img/cabecalho.jpg") right center / cover no-repeat;
  -webkit-mask-image: radial-gradient(ellipse 118% 150% at 100% 50%, #000 62%, transparent 63%);
  mask-image: radial-gradient(ellipse 118% 150% at 100% 50%, #000 62%, transparent 63%);
}

/* O degradê que assenta a foto no fundo da página — e que no tema escuro a
   escurece. Sem ele, o branco da foto encostaria no texto da marca. A cor sai
   de `--fundo`, e não de um branco fixo, justamente para acompanhar o tema. */
.topo__faixa::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg,
    var(--fundo) 0%,
    color-mix(in srgb, var(--fundo) 55%, transparent) 42%,
    transparent 68%);
}

.topo__interno {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-5);
  min-height: 168px;
  padding-block: var(--e-5);
}

/* A marca na faixa **não trunca**: ela quebra. O nome tem 36 caracteres, e o
   `.truncar` com reticências cortaria justamente o "e Demandas" — a metade do
   nome que o sistema acabou de ganhar. Quem cede é a largura da coluna, e o
   texto desce para a segunda linha.

   O `min-width: 0` continua sendo a peça que torna a cedência possível: sem
   ele o mínimo do item flex vale `auto`, que resolve para a largura mínima do
   texto, e a marca empurraria a página para o lado em vez de encolher. É a
   mesma peça que destrava o `<nav>` mais abaixo. */
.marca {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  font-weight: 680;
  font-size: 0.9375rem;
  color: var(--texto);
  letter-spacing: -0.02em;
  white-space: nowrap;
  min-width: 0;
}
.marca:hover { text-decoration: none; }

.marca--faixa {
  align-items: center;
  gap: var(--e-4);
  white-space: normal;
}

.marca__texto {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.marca__nome {
  font-size: 1.375rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.15;
}

.marca__slogan {
  font-size: 0.8125rem;
  font-weight: 450;
  color: var(--texto-suave);
  letter-spacing: 0;
}

/* `flex: none` para o quadradinho não se esmagar junto com o texto.
   Ele e o texto são os dois itens flex da marca, e os dois encolhem por
   padrão. Enquanto a marca não podia ceder (antes do `min-width: 0` acima)
   isso não aparecia, porque nada encolhia; destravada a marca, o ícone passou
   a perder largura junto — medido, **28,05 × 32px** a 320px de janela, e
   29,81px a 390. Um monograma de 28 por 32 não é um quadrado: é um retângulo
   com os cantos arredondados errados.
   Quem tem de ceder é o texto, que na faixa quebra em duas linhas — cortar o
   monograma seria deformação, não informação. */
.marca__icone {
  display: grid;
  place-items: center;
  flex: none;
  width: 32px;
  height: 32px;
  border-radius: var(--r-sm);
  background: linear-gradient(140deg,
              color-mix(in srgb, var(--primaria) 82%, white) 0%,
              var(--primaria) 55%,
              var(--primaria-forte) 100%);
  color: var(--primaria-contraste);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.22);
}

/* O quadradinho cresce na faixa — 56px, o tamanho do mockup. O seletor desce
   do pai em vez de uma segunda classe no HTML porque `apps/core/tests_nome.py`
   procura a marca pelo `class="marca__icone"` **exato**: uma classe a mais no
   atributo apagaria a assinatura da sigla que aquele teste prende. */
.marca--faixa .marca__icone {
  width: 56px;
  height: 56px;
  border-radius: var(--r);
  font-size: 1.375rem;
}

/* A barra de navegação, e a única parte do cabeçalho presa no alto.

   Ela carrega o `env(safe-area-inset-top)` que antes ficava no `.topo`: com
   `viewport-fit=cover`, a barra `sticky` nasce sob o recorte da tela e, sem o
   padding, o primeiro item fica atrás da barra de status do aparelho. */
.topo-barra {
  position: sticky;
  top: 0;
  z-index: 50;
  padding-top: env(safe-area-inset-top);
  background: color-mix(in srgb, var(--superficie) 88%, transparent);
  backdrop-filter: blur(16px) saturate(180%);
  border-bottom: 1px solid var(--borda);
  box-shadow: 0 1px 2px rgb(16 24 40 / 0.03);
}

.topo-barra > .container {
  display: flex;
  align-items: stretch;
  min-height: var(--altura-topo);
}

.menu {
  display: flex;
  align-items: stretch;
  gap: var(--e-1);
  list-style: none;
  padding: 0;
  flex: 1;
  overflow-x: auto;
  scrollbar-width: none;
}
.menu::-webkit-scrollbar { display: none; }

/* O menu já nascia como faixa rolável — `flex: 1` e `overflow-x: auto` estão
   aqui desde o começo —, mas quem é item flex não é este `<ul>`, e sim o
   `<nav>` em volta dele. O `min-width` de um item flex vale `auto`, que
   resolve para a largura mínima do próprio conteúdo: como o menu é uma fila de
   oito links com `white-space: nowrap`, essa largura mínima é a soma dos oito,
   768px. Enquanto o `nav` não podia encolher, quem cedia era a página inteira
   — medido numa janela de 390px, o documento saía com 784px e a tela rolava de
   lado, com o `flex-wrap: wrap` do cabeçalho ali ao lado sem servir para nada.
   `min-width: 0` é o que destrava a faixa: com ele o `nav` encolhe, o
   `overflow-x` do menu passa a valer e a página para de rolar. É a mesma peça
   que o `.busca-topo` já traz na faixa de cima, e pelo mesmo motivo.
   O `<nav>` não tem classe: o seletor desce da barra, que tem uma só. */
.topo-barra nav {
  display: flex;
  align-items: stretch;
  min-width: 0;
}

/* Aqui houve um degradê na borda direita do menu, para o corte não cair no meio
   de uma palavra sem aviso. Ele saiu, e vale registrar por quê, porque a ideia
   é atraente e volta sozinha: máscara não sabe se há conteúdo além da borda, e
   as duas tentativas de ensiná-la falharam na medição. Permanente, ela esmaecia
   texto que não estava escondido — 19px do único item do menu do motorista a
   480px de janela, e 31px de "Auditoria" a 1080px, com os oito à vista. E, com
   a faixa rolada até o fim, deixava o último item esmaecido para sempre (46%
   do texto), justamente o item que a pessoa acabou de arrastar para ver. Uma
   consulta de mídia no lugar disso precisaria adivinhar a largura em que cada
   menu corta, e são três perfis — oito, seis e um item — que cortam em
   larguras diferentes. O que sobra é o corte seco do `overflow`, que é honesto:
   o item cortado é o próprio aviso. */

/* Ícone em cima, rótulo embaixo — a fileira do mockup.

   O item tem a altura inteira da barra porque a marca da página atual é uma
   linha na base: um sublinhado que flutua no meio do item não encosta na borda
   da barra, que é onde ele precisa parecer colado. E todos os itens declaram a
   mesma borda transparente — sem isso, o ativo ganharia 2px de altura a mais
   que os vizinhos e a fileira desalinharia no clique. */
.menu__link {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: var(--e-2) var(--e-3);
  min-height: var(--altura-topo);
  border-bottom: 2px solid transparent;
  color: var(--texto-suave);
  font-size: 0.6875rem;
  font-weight: 560;
  white-space: nowrap;
  transition: background var(--transicao), color var(--transicao);
}
.menu__link:hover {
  background: var(--superficie-alt);
  color: var(--texto);
  text-decoration: none;
}
/* A página atual se marca por peso, por cor **e** pela linha na base — como no
   mockup. Um fundo azulado sozinho some quando a tela é grande e ninguém está
   procurando por ele; a linha corre o comprimento do item e se vê de longe. */
.menu__link[aria-current="page"] {
  color: var(--primaria);
  border-bottom-color: var(--primaria);
  font-weight: 680;
}

.menu__icone { width: 19px; height: 19px; }

/* O painel da direita: relógio, ações e busca, num cartão que flutua sobre a
   foto. A largura é limitada para ele não crescer com o nome de um usuário
   longo nem espremer o campo de busca; abaixo de 900px ele ocupa a linha
   inteira, porque ao lado da marca não sobra espaço para os dois. */
.painel-topo {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: var(--e-2);
  width: min(100%, 342px);
  padding: var(--e-3);
  background: color-mix(in srgb, var(--superficie) 92%, transparent);
  border: 1px solid color-mix(in srgb, var(--borda) 85%, transparent);
  border-radius: var(--r-lg);
  box-shadow: var(--sombra-3);
  backdrop-filter: blur(10px);
}

.painel-topo__linha {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-2);
  min-height: 34px;
}

/* Data em cima, hora embaixo, as duas encostadas na borda esquerda do painel —
   a mesma borda do campo de busca logo abaixo, que é o alinhamento pedido.
   `tabular-nums` na hora evita a trepidação de largura a cada segundo: o "1" é
   mais estreito que o "8" nesta fonte, e em 15px isso se vê. */
.agora {
  display: flex;
  flex-direction: column;
  gap: 1px;
  line-height: 1;
}
.agora__data {
  font-size: 0.6875rem;
  font-weight: 550;
  color: var(--texto-fraco);
  font-variant-numeric: tabular-nums;
}
.agora__hora {
  font-size: 0.9375rem;
  font-weight: 650;
  color: var(--texto);
  font-variant-numeric: tabular-nums;
}

.painel-topo__acoes {
  display: flex;
  align-items: center;
  gap: var(--e-1);
  margin-left: auto;
  min-width: 0;
}

.painel-topo__sair { display: flex; }

.usuario__avatar {
  display: grid;
  place-items: center;
  flex: none;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--primaria-suave);
  color: var(--primaria);
  font-size: 0.8125rem;
  font-weight: 700;
}

.usuario__nome {
  min-width: 0;
  max-width: 10rem;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--texto);
  line-height: 1.15;
}
.usuario__nivel {
  display: block;
  font-size: 0.625rem;
  font-weight: 600;
  color: var(--texto-fraco);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* Os botões redondos do painel — engrenagem, tema, sair. Os 32px são os mesmos
   do avatar: alvos de alturas diferentes na mesma linha fazem a fileira parecer
   torta. O nome acessível vem do `aria-label`/`title` do template; sem ele o
   botão seria um ícone mudo. */
.botao-icone {
  display: grid;
  place-items: center;
  flex: none;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--texto-suave);
  cursor: pointer;
  transition: background var(--transicao), color var(--transicao);
}
.botao-icone:hover {
  background: var(--superficie-alt);
  color: var(--texto);
  text-decoration: none;
}
.botao-icone svg { width: 18px; height: 18px; }
/* O `hidden` do template esconde o botão de tema até o `tema.js` montá-lo; sem
   esta linha o `display: grid` acima venceria o atributo e o botão apareceria
   morto por um instante. */
.botao-icone[hidden] { display: none; }

/* A busca em pílula do painel. O ícone é posicionado **sobre** o campo, e o
   `padding-left` do `input` é o que abre espaço para ele: os dois números têm
   de concordar, e por isso estão um ao lado do outro. */
.busca-topo {
  position: relative;
  display: flex;
  align-items: center;
}

.busca-topo__icone {
  position: absolute;
  left: 12px;
  width: 16px;
  height: 16px;
  color: var(--texto-fraco);
  pointer-events: none;
}

.busca-topo .entrada {
  width: 100%;
  min-height: 38px;
  padding-block: 0;
  padding-left: 34px;
  border-radius: var(--r-full);
  font-size: 0.8125rem;
}

/* Abaixo de 900px a marca e o painel não cabem lado a lado: a faixa vira
   coluna e o painel ocupa a largura inteira — que é o que mantém data, hora e
   busca alinhadas à esquerda, como no desktop. */
@media (max-width: 900px) {
  .topo__interno {
    flex-direction: column;
    align-items: stretch;
    gap: var(--e-4);
    min-height: 0;
    padding-block: var(--e-4);
  }
  .painel-topo { width: 100%; }
  .topo__faixa::before { width: 68%; }
  .marca__nome { font-size: 1.1875rem; }
}

@media (max-width: 560px) {
  .marca--faixa .marca__icone {
    width: 46px;
    height: 46px;
    border-radius: var(--r-sm);
    font-size: 1.125rem;
  }
  .marca__nome { font-size: 1.0625rem; }
  .marca__slogan { font-size: 0.75rem; }
  .painel-topo { padding: var(--e-2) var(--e-3); }
  .topo__faixa::before { width: 78%; }
}

/* Sem sessão, o painel não tem relógio, busca nem conta: só o botão de tema.
   A casca de cartão — fundo, borda, sombra, desfoque e o padding que reservava
   espaço para duas linhas — ficava na tela de entrada como um retângulo vazio
   sobre a foto. Aqui ela sai, e o botão fica solto à direita da faixa.

   A regra vem **depois** dos dois `@media` acima de propósito: `width: auto`
   precisa vencer o `width: 100%` do layout de coluna, e `.painel-topo--sem-conta`
   tem a mesma especificidade de `.painel-topo`. */
.painel-topo--sem-conta {
  width: auto;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  backdrop-filter: none;
}

/* Na entrada a marca é o único conteúdo da faixa, e ela fica centralizada.
   O botão de tema continua visível, mas sai do fluxo: no fluxo ele seria um
   segundo item, e o `space-between` — ou um `center` sobre os dois — dividiria
   a faixa entre marca e botão, com o nome fora do meio da página.

   Vem **depois** dos `@media`: em coluna o `.topo__interno` estica os itens, e
   o `align-items: center` daqui é o que impede o `<a>` de ocupar a largura
   toda e deixar a marca encostada à esquerda. */
.topo__interno--entrada {
  justify-content: center;
  align-items: center;
  /* Reserva a mesma faixa dos dois lados: o botão de tema não cobre o fim do
     nome quando a tela é estreita, e o padding simétrico mantém o centro. */
  padding-inline: 3rem;
}
.topo__interno--entrada .painel-topo--sem-conta {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
}

/* --------------------------------------------------------------------------
   6. Cartões
   -------------------------------------------------------------------------- */

/* O respiro do cartão é do cartão, não de quem o preenche. Ele nasce aqui e é
   zerado quando um filho já se padeia sozinho — sem isso, todo cartão que põe
   conteúdo direto dentro ficava com o texto encostado na borda, que é a maioria:
   39 dos 49 cartões do projeto.

   Tudo vive dentro do `@supports` de propósito. Uma lista de seletores com um
   item inválido é descartada INTEIRA — então, num navegador sem `:has()`, o
   `padding: 0` sumiria junto com a regra e sobraria o padding de fora somado ao
   do filho: os cartões estruturados ficariam com padding dobrado. Com o default
   fora do bloco, quem não tem `:has()` fica exatamente como estava. */
.cartao {
  padding: 0;
  background: var(--superficie);
  border: 1px solid var(--borda);
  border-radius: var(--r);
  box-shadow: var(--sombra-1);
}

@supports selector(:has(*)) {
  .cartao { padding: var(--e-4); }

  /* O cartão cede para quem tem inset próprio: o cabeçalho e o corpo já trazem
     o seu, e o estado vazio respira mais largo do que o cartão daria.

     O `.vazio` só zera quando é o **único** filho. Um cartão que tem título,
     parágrafo ou formulário além dele — o de Contatos de acompanhamento, o de
     veículos no detalhe do motorista — ficava sem padding nenhum, e o texto
     colava na borda. `:only-child` separa os dois casos, que `:has(> .vazio)`
     sozinho não separa.

     O `.referencia` NÃO entra nesta lista. O único cartão que o envolve
     (templates/abastecimento/form.html) tem dois filhos — o callout e um
     parágrafo de ajuda —, e `:has()` zera a caixa toda: a ajuda voltaria a
     colar na borda, ressuscitando o defeito dentro do próprio conserto. */
  .cartao:has(> .cartao__cabecalho),
  .cartao:has(> .cartao__corpo),
  .cartao:has(> .cartao__rodape),
  .cartao:has(> .vazio:only-child) { padding: 0; }
}

.cartao__cabecalho {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-3);
  padding: var(--e-4);
  border-bottom: 1px solid var(--borda);
  flex-wrap: wrap;
}

.cartao__titulo {
  font-size: 0.9375rem;
  font-weight: 620;
  letter-spacing: -0.01em;
}

.cartao__corpo  { padding: var(--e-4); }

/* O convite recém-gerado abre dentro do cartão de convites, numa caixa com
   borda própria. Não pode ser `.cartao__corpo`: o cartão zera o padding
   quando tem um corpo como filho (`:has(> .cartao__corpo)`), e aqui a caixa é
   só uma parte do cartão — título, parágrafos e tabelas ficariam colados na
   borda, que é o mesmo defeito que o `.referencia` já tinha. */
.caixa-convite {
  padding: var(--e-4);
  border: 1px solid var(--borda);
  border-radius: var(--r);
}
.cartao__rodape {
  padding: var(--e-3) var(--e-4);
  border-top: 1px solid var(--borda);
  background: var(--superficie-alt);
  border-radius: 0 0 var(--r) var(--r);
  font-size: 0.8125rem;
  color: var(--texto-suave);
}

/* --------------------------------------------------------------------------
   7. Indicadores (stat tiles)
   -------------------------------------------------------------------------- */

.indicador {
  display: flex;
  flex-direction: column;
  padding: var(--e-4);
  background: var(--superficie);
  border: 1px solid var(--borda);
  border-radius: var(--r);
  box-shadow: var(--sombra-1);
}

.indicador__rotulo {
  display: block;
  font-size: 0.6875rem;
  font-weight: 620;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--texto-fraco);
  margin-bottom: var(--e-3);
}

.indicador__valor {
  font-family: var(--fonte-num);
  font-variant-numeric: tabular-nums;
  font-size: clamp(1.625rem, 1.3rem + 1.6vw, 2.25rem);
  font-weight: 620;
  line-height: 1;
  letter-spacing: -0.035em;
}

.indicador__unidade {
  font-size: 0.8125rem;
  font-weight: 550;
  letter-spacing: 0;
  color: var(--texto-fraco);
  margin-left: 0.3em;
}

/* A nota desce para o rodapé da caixa e ganha um fio por cima. Como os
   indicadores são itens de grade, todos esticam à mesma altura: sem o
   `margin-top: auto`, a nota de cada um ficaria logo abaixo do número e os
   fios sairiam em alturas diferentes, na mesma linha. */
.indicador__nota {
  display: block;
  margin-top: auto;
  padding-top: var(--e-3);
  font-size: 0.75rem;
  line-height: 1.35;
  color: var(--texto-suave);
  border-top: 1px solid var(--borda);
}

.indicador--destaque {
  background: var(--primaria);
  border-color: var(--primaria);
  color: var(--primaria-contraste);
}
.indicador--destaque .indicador__rotulo,
.indicador--destaque .indicador__unidade,
.indicador--destaque .indicador__nota {
  color: color-mix(in srgb, var(--primaria-contraste) 78%, transparent);
}
.indicador--destaque .indicador__nota {
  border-top-color: color-mix(in srgb, var(--primaria-contraste) 24%, transparent);
}

/* O resumo de ARLA do painel: dois números dentro de um cartão, e não dois
   cartões dentro de um cartão. As classes `.indicador__*` entram só pela
   tipografia — rótulo, número e unidade na mesma escala do topo —, e o que
   fica de fora é a moldura do `.indicador`: com ela, o resumo viraria dois
   quadros avulsos dentro de outro, e o ARLA passaria a competir com a fileira
   de oito em vez de ler como um recorte dela.

   O `min-width` é o que faz o par quebrar para duas linhas antes de os
   números se espremerem nas telas estreitas. O `gap` é maior na horizontal
   porque é ali que as duas colunas precisam de ar quando estão lado a lado;
   na vertical, dois parágrafos já se separam sozinhos. */
.arla-resumo {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-4) var(--e-7);
}
.arla-resumo__item {
  display: flex;
  flex-direction: column;
  min-width: 8rem;
}

/* --------------------------------------------------------------------------
   8. Botões
   -------------------------------------------------------------------------- */

.botao {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--e-2);
  min-height: var(--altura-controle);
  padding: var(--e-2) var(--e-4);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: var(--primaria);
  color: var(--primaria-contraste);
  font-size: 0.9375rem;
  font-weight: 570;
  line-height: 1.2;
  white-space: nowrap;
  transition: background var(--transicao), border-color var(--transicao),
              transform var(--transicao);
}
.botao:hover {
  background: var(--primaria-forte);
  text-decoration: none;
}
.botao:active { transform: translateY(1px); }

.botao[disabled], .botao[aria-disabled="true"] {
  opacity: 0.5;
  pointer-events: none;
}

.botao--secundario {
  background: var(--superficie);
  border-color: var(--borda-forte);
  color: var(--texto);
}
.botao--secundario:hover {
  background: var(--superficie-alt);
  border-color: var(--texto-fraco);
}

.botao--discreto {
  background: transparent;
  color: var(--texto-suave);
}
.botao--discreto:hover {
  background: var(--superficie-alt);
  color: var(--texto);
}

.botao--perigo {
  background: var(--erro);
  color: #fff;
}
.botao--perigo:hover { background: color-mix(in srgb, var(--erro) 85%, #000); }

.botao--sucesso { background: var(--ok); color: #fff; }
.botao--sucesso:hover { background: color-mix(in srgb, var(--ok) 85%, #000); }

.botao--bloco { width: 100%; }

.botao--grande {
  min-height: 52px;
  padding-inline: var(--e-5);
  font-size: 1rem;
  border-radius: var(--r);
}

.botao--pequeno {
  min-height: 32px;
  padding: var(--e-1) var(--e-3);
  font-size: 0.8125rem;
}

/* Setas de navegação por mês. Quadradas e altas de propósito: o glifo é um
   caractere só, e numa pastilha estreita — que é o que `.botao--pequeno` dava
   — ele fica pequeno demais para mirar.

   Um degrau acima do controle comum (48 contra 44): o glifo de 1.75rem precisa
   do corpo para não encostar na borda, e a seta é o alvo que mais se erra na
   tela. O campo de mês ao lado acompanha a altura — ver `.navegacao-mes` —, e é
   isso que faz o trio ler como um controle só em vez de três peças soltas. */
.botao--seta {
  width: 48px;
  min-height: 48px;
  padding: 0;
  font-size: 1.75rem;
  line-height: 1;
}

/* O recorte escolhido no seletor do gráfico. Aqui `aria-current` sozinho não
   resolve: os três botões levam à **mesma** tela, mudando só o tamanho do
   recorte, então a marca precisa ser visível — quem não vê a tela não perde
   nada, mas quem vê precisa saber qual dos três está valendo. */
.botao--ativo {
  background: var(--primaria);
  color: var(--primaria-contraste);
  border-color: var(--primaria);
}
.botao--ativo:hover {
  background: var(--primaria-forte);
}

/* --------------------------------------------------------------------------
   9. Etiquetas de status
   -------------------------------------------------------------------------- */

.etiqueta {
  display: inline-flex;
  align-items: center;
  gap: 0.375em;
  padding: 0.185em 0.6em;
  border-radius: var(--r-full);
  font-size: 0.75rem;
  font-weight: 570;
  line-height: 1.6;
  white-space: nowrap;
}

.etiqueta::before {
  content: "";
  width: 0.4em;
  height: 0.4em;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

.etiqueta--ok       { background: var(--ok-suave);      color: var(--ok); }
.etiqueta--atencao  { background: var(--atencao-suave); color: var(--atencao); }
.etiqueta--erro     { background: var(--erro-suave);    color: var(--erro); }
.etiqueta--info     { background: var(--info-suave);    color: var(--info); }
.etiqueta--neutra   { background: var(--superficie-alt); color: var(--texto-suave); }

.etiqueta--neutra::before { display: none; }

/* --------------------------------------------------------------------------
   10. Formulários
   -------------------------------------------------------------------------- */

.campo { display: flex; flex-direction: column; gap: var(--e-2); }

.campo__rotulo {
  font-size: 0.875rem;
  font-weight: 570;
  color: var(--texto);
}

.campo__ajuda {
  font-size: 0.8125rem;
  color: var(--texto-suave);
}

.campo__erro {
  font-size: 0.8125rem;
  font-weight: 520;
  color: var(--erro);
}

.entrada,
.selecao,
.area {
  width: 100%;
  min-height: var(--altura-controle);
  padding: var(--e-2) var(--e-3);
  border: 1px solid var(--borda-forte);
  border-radius: var(--r-sm);
  background: var(--superficie);
  color: var(--texto);
  transition: border-color var(--transicao), box-shadow var(--transicao);
}

.area { min-height: 96px; resize: vertical; }

.entrada:hover, .selecao:hover, .area:hover { border-color: var(--texto-fraco); }

.entrada:focus, .selecao:focus, .area:focus {
  outline: none;
  border-color: var(--primaria);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primaria) 18%, transparent);
}

.entrada[aria-invalid="true"], .selecao[aria-invalid="true"] {
  border-color: var(--erro);
}

/* Placa e chassi aparecem sempre em maiúscula, digite-se como for.

   São identificadores, e o servidor já os grava assim. Sem isto, quem digita
   em minúscula no teclado do computador vê "abc1234" no campo e não tem como
   saber em que caixa aquilo foi salvo — a dúvida é razoável e a resposta está
   do outro lado da tela.

   O `autocapitalize="characters"` que os widgets já tinham não resolve: ele
   age no teclado do celular, e nada no do computador.

   É `text-transform`, e não JavaScript, de propósito: o que está no campo
   continua sendo exatamente o que a pessoa digitou, e quem normaliza é o
   servidor — `clean_placa` e `clean_chassi`. Assim a regra vale antes de
   qualquer script carregar, e não há um segundo lugar decidindo a caixa. */
.entrada[data-maiuscula] {
  text-transform: uppercase;
}

.entrada--numerica {
  font-family: var(--fonte-num);
  font-variant-numeric: tabular-nums;
  font-size: 1.125rem;
  font-weight: 550;
  letter-spacing: 0.01em;
}

.campo--destaque .entrada {
  min-height: 56px;
  font-size: 1.375rem;
}

/* Rótulo **ao lado** do controle, e não por cima dele. É o oposto do
   `.campo__rotulo`, que empilha: formulário de cadastro lê melhor em coluna,
   porque cada campo tem ajuda e erro embaixo. Aqui são dois controles de
   recorte, e empilhar gastaria duas linhas de altura para dizer "Veículo" e
   "Mês".

   A altura mínima é a do controle, e existe por causa do alinhamento: com o
   `.linha-filtros` alinhando pela base, um rótulo de uma linha só assentaria no
   fundo do campo em vez de na linha de base do texto dele. Ocupando a altura
   inteira e centrando o texto dentro, o rótulo bate com o valor do `select`. */
.rotulo-campo {
  display: inline-flex;
  align-items: center;
  min-height: var(--altura-controle);
  font-size: 0.875rem;
  font-weight: 570;
  color: var(--texto-suave);
  white-space: nowrap;
}

/* A barra de recorte do painel: rótulo, controle, rótulo, controle, botão.
   Pela base porque os controles têm a mesma altura e os rótulos não; é o fundo
   comum que faz a linha parecer uma linha.

   E os campos ficam com a largura do **conteúdo**, desfazendo o `width: 100%`
   do `.entrada`/`.selecao`: num flex de linha, o `100%` viraria a barra inteira
   para o primeiro deles, e o segundo sairia empurrado para a linha de baixo.
   Placa e mês têm largura previsível, então caber no conteúdo cabe na tela.

   O `--altura-controle` redefinido aqui é o que põe a barra inteira em 48px — o
   degrau das setas. Redefinido, e não repetido em três regras, porque o rótulo
   e o botão leem a mesma variável do campo: um número só, e não há como o
   botão ficar 4px mais baixo que a seta ao lado. */
.linha-filtros {
  --altura-controle: 48px;
  align-items: flex-end;
  gap: var(--e-2) var(--e-3);
}
.linha-filtros .selecao,
.linha-filtros .entrada { width: auto; }

/* O grupo do mês: seta, campo, seta. O vão interno é menor que o da linha de
   fora, para o trio se ler como um controle só, e o campo fica no meio dele. */
.navegacao-mes { gap: var(--e-2); }

/* Interruptor (ex.: "tanque cheio") */
.interruptor {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  cursor: pointer;
  min-height: 44px;
}
.interruptor input {
  appearance: none;
  width: 46px;
  height: 26px;
  flex-shrink: 0;
  border-radius: var(--r-full);
  background: var(--borda-forte);
  position: relative;
  transition: background var(--transicao);
}
.interruptor input::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--sombra-1);
  transition: transform var(--transicao);
}
.interruptor input:checked { background: var(--primaria); }
.interruptor input:checked::after { transform: translateX(20px); }

/* --------------------------------------------------------------------------
   11. Tabelas
   -------------------------------------------------------------------------- */

.tabela-envolucro {
  overflow-x: auto;
  /* O `position: relative` aqui não é decoração, é o que faz o `overflow-x`
     valer para todo mundo. Um filho `position: absolute` sem ancestral
     posicionado tem como bloco de contenção o bloco inicial, e então ele
     **escapa** do recorte do wrapper e alarga a página inteira. É o caso do
     `<span class="sr-apenas">` que rotula a última coluna de três tabelas
     (auditoria, detalhe do abastecimento, detalhe do veículo): medido numa
     janela de 390px, o documento saía com 630px e o span terminava em 629 —
     escondendo só ele, o documento voltava a 390. Com o wrapper posicionado, o
     absoluto fica preso ao recorte, que é onde ele sempre deveria ter estado. */
  position: relative;
  border-radius: var(--r);
}

.tabela {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}

.tabela th {
  text-align: left;
  padding: var(--e-3);
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--texto-fraco);
  background: var(--superficie-alt);
  border-bottom: 1px solid var(--borda);
  white-space: nowrap;
}

.tabela td {
  padding: var(--e-3);
  border-bottom: 1px solid var(--borda);
  vertical-align: middle;
}

.tabela tbody tr:last-child td { border-bottom: none; }
.tabela tbody tr:hover { background: var(--superficie-alt); }

.tabela .num { text-align: right; }

/* --------------------------------------------------------------------------
   12. Mensagens e alertas
   -------------------------------------------------------------------------- */

.mensagens {
  display: flex;
  flex-direction: column;
  gap: var(--e-2);
  margin-bottom: var(--e-4);
}

.aviso {
  display: flex;
  align-items: flex-start;
  gap: var(--e-3);
  padding: var(--e-3) var(--e-4);
  border: 1px solid;
  border-radius: var(--r-sm);
  font-size: 0.875rem;
}

.aviso--ok      { background: var(--ok-suave);      border-color: color-mix(in srgb, var(--ok) 30%, transparent);      color: var(--ok); }
.aviso--atencao { background: var(--atencao-suave); border-color: color-mix(in srgb, var(--atencao) 30%, transparent); color: var(--atencao); }
.aviso--erro    { background: var(--erro-suave);    border-color: color-mix(in srgb, var(--erro) 30%, transparent);    color: var(--erro); }
.aviso--info    { background: var(--info-suave);    border-color: color-mix(in srgb, var(--info) 30%, transparent);    color: var(--info); }

/* --------------------------------------------------------------------------
   13. Estado vazio
   -------------------------------------------------------------------------- */

.vazio {
  padding: var(--e-8) var(--e-4);
  text-align: center;
  color: var(--texto-suave);
}

.vazio__titulo {
  font-size: 1rem;
  font-weight: 600;
  color: var(--texto);
  margin-bottom: var(--e-2);
}

.vazio__texto {
  font-size: 0.875rem;
  max-width: 42ch;
  margin-inline: auto;
  margin-bottom: var(--e-4);
}

/* QR do convite do Telegram. O SVG vem com largura fixa em pixels; a regra
   faz ele caber no cartão e escalar junto com a página, e o fundo branco
   mantém o contraste na impressão em preto e branco. */
.qr-convite {
  max-width: 260px;
  padding: var(--e-3);
  background: #fff;
  border-radius: var(--raio-2);
}

.qr-convite svg {
  display: block;
  width: 100%;
  height: auto;
}

/* --------------------------------------------------------------------------
   14. Fluxo do motorista
   -------------------------------------------------------------------------- */

/* Referência do odômetro: o número que o motorista precisa enxergar antes
   de digitar o km atual. Por isso fica grande e fixo no topo do formulário. */
.referencia {
  padding: var(--e-4);
  background: var(--primaria-suave);
  border: 1px solid color-mix(in srgb, var(--primaria) 22%, transparent);
  border-radius: var(--r);
}

.referencia__rotulo {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--primaria);
}

.referencia__valor {
  font-family: var(--fonte-num);
  font-variant-numeric: tabular-nums;
  font-size: 1.75rem;
  font-weight: 680;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--texto);
}

.referencia__nota {
  font-size: 0.8125rem;
  color: var(--texto-suave);
}

/* Cartão de veículo na seleção */
.veiculo-opcao {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  width: 100%;
  padding: var(--e-4);
  background: var(--superficie);
  border: 1px solid var(--borda);
  border-radius: var(--r);
  text-align: left;
  box-shadow: var(--sombra-1);
  transition: border-color var(--transicao), box-shadow var(--transicao);
}
.veiculo-opcao:hover {
  border-color: var(--primaria);
  box-shadow: var(--sombra-2);
  text-decoration: none;
}

.veiculo-opcao__placa {
  font-family: var(--fonte-num);
  font-weight: 680;
  font-size: 1.0625rem;
  letter-spacing: 0.02em;
}

/* Bloco de foto */
.foto-item {
  border: 1px solid var(--borda);
  border-radius: var(--r);
  background: var(--superficie);
  overflow: hidden;
}

.foto-item__cabecalho {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-2);
  padding: var(--e-3) var(--e-4);
  font-size: 0.875rem;
  font-weight: 570;
}

.foto-item__acoes {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--e-2);
  padding: 0 var(--e-4) var(--e-4);
}

.foto-item__previa {
  position: relative;
  aspect-ratio: 4 / 3;
  background: var(--superficie-alt);
  display: grid;
  place-items: center;
  overflow: hidden;
  border-top: 1px solid var(--borda);
}
.foto-item__previa img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A miniatura solta na fila de conferência: a mesma imagem, sem o cartão em
   volta, num tamanho que caiba ao lado do texto da linha. A borda superior
   some porque aqui não há cabeçalho acima dela. */
.foto-item__previa--fila {
  flex: 0 0 104px;
  width: 104px;
  height: 78px;
  aspect-ratio: auto;
  border: 1px solid var(--borda);
  border-radius: var(--r-sm);
}

/* Barra de progresso do upload — uma por foto, para que a falha de uma
   não esconda o andamento das outras. */
.progresso {
  height: 4px;
  background: var(--superficie-alt);
  overflow: hidden;
}
.progresso__barra {
  height: 100%;
  width: 0;
  background: var(--primaria);
  transition: width 200ms ease-out;
}
.progresso--ok .progresso__barra    { background: var(--ok); }
.progresso--erro .progresso__barra  { background: var(--erro); }

/* Ação fixa no rodapé: o polegar alcança sem rolar a tela */
.barra-fixa {
  position: sticky;
  bottom: 0;
  z-index: 40;
  margin-top: var(--e-5);
  padding: var(--e-3) var(--e-4);
  padding-bottom: max(var(--e-3), env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--superficie) 92%, transparent);
  backdrop-filter: blur(12px);
  border-top: 1px solid var(--borda);
}

/* --------------------------------------------------------------------------
   15. Passos do assistente
   -------------------------------------------------------------------------- */

.passos {
  display: flex;
  align-items: center;
  gap: var(--e-2);
  list-style: none;
  padding: 0;
  font-size: 0.8125rem;
}

.passos__item {
  display: flex;
  align-items: center;
  gap: var(--e-2);
  color: var(--texto-fraco);
}

.passos__numero {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--superficie-alt);
  color: var(--texto-fraco);
  font-size: 0.6875rem;
  font-weight: 650;
  flex-shrink: 0;
}

.passos__item--atual { color: var(--texto); font-weight: 570; }
.passos__item--atual .passos__numero {
  background: var(--primaria);
  color: var(--primaria-contraste);
}

.passos__item--feito .passos__numero {
  background: var(--ok);
  color: #fff;
}

/* --------------------------------------------------------------------------
   16. Gráficos
   -------------------------------------------------------------------------- */

.grafico {
  position: relative;
  width: 100%;
  height: 260px;
}

.grafico--alto { height: 340px; }

/* A miniatura de consumo na lista de veículos. SVG e não canvas: são vinte e
   cinco linhas de sessenta pixels, e vinte e cinco instâncias de Chart.js para
   isso seria caro pelo nada. `display: block` porque o SVG é inline por padrão
   e ficaria com a folga da linha de base embaixo. */
.sparkline {
  display: block;
  margin-left: auto;
  color: var(--serie-1);
}

/* --------------------------------------------------------------------------
   17. Utilitários
   -------------------------------------------------------------------------- */

.sr-apenas {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* O esconderijo acima recorta o elemento em 1×1, e o link de pular do
   `base.html` é alcançado pelo Tab: para ele, ficar recortado é o defeito. O
   Tab pousa num elemento que ninguém vê, e o atalho de teclado passa a existir
   no HTML sem existir na tela.

   O seletor é `a.sr-apenas`, e **não** `.sr-apenas`, e a diferença é medida:
   o `.sr-apenas` também veste os `<input type="file">` de
   `mobile/lancamento_fotos.html`, que são focáveis. Um seletor largo daria a
   eles `position: fixed` em (8,8) com fundo, borda e sombra — o controle
   nativo de arquivo apareceria flutuando solto sobre a página, descolado do
   label-botão a que pertence, em vez de continuar escondido atrás dele. Quem
   precisa voltar a aparecer é o **link**; o resto, não.

   O `position: fixed` não é enfeite: em fluxo, o link apareceria
   **empurrando a página inteira para baixo** no instante do foco. O `z-index`
   precisa passar o da `.topo-barra`, que é 50. */
a.sr-apenas:focus-visible {
  position: fixed;
  top: var(--e-2); left: var(--e-2);
  z-index: 100;
  width: auto; height: auto;
  padding: var(--e-2) var(--e-3);
  margin: 0;
  overflow: visible;
  clip: auto;
  white-space: normal;
  background: var(--superficie);
  color: var(--primaria);
  border: 1px solid var(--borda-forte);
  border-radius: var(--r-sm);
  box-shadow: var(--sombra-3);
  font-size: 0.875rem;
  font-weight: 600;
}

.oculto { display: none !important; }

.so-mobile { display: block; }
.so-desktop { display: none; }

@media (min-width: 768px) {
  .so-mobile { display: none; }
  .so-desktop { display: block; }
}

/* A escala é a mesma em cima e embaixo, e os templates contam com ela inteira:
   `mt-4` sozinha aparece dezoito vezes. Faltavam justamente `mt-4`, `mt-5` e
   `mb-0` — e classe que o CSS não define não dá erro em lugar nenhum, só não
   espaça. Foi assim que o interruptor "Ativo" e o campo Observações ficaram
   colados no grid acima, nos formulários. `mt-3` e `mb-3` entraram com a OS do
   motorista e o painel de demandas, e o teste `tests.py` do core varre os
   templates atrás de qualquer utilitária que falte. */
.mt-0 { margin-top: 0; }
.mt-3 { margin-top: var(--e-3); }
.mt-4 { margin-top: var(--e-4); }
.mt-5 { margin-top: var(--e-5); }

.mb-0 { margin-bottom: 0; }
.mb-2 { margin-bottom: var(--e-2); }
.mb-3 { margin-bottom: var(--e-3); }
.mb-4 { margin-bottom: var(--e-4); }
.mb-5 { margin-bottom: var(--e-5); }

.texto-centro { text-align: center; }
.texto-direita { text-align: right; }

/* Ocupa a sobra da linha em que está. Usado pelo campo de busca, que divide a
   linha com um botão. Sem isto, o controle encolhe até o tamanho do conteúdo. */
.cresce { flex: 1 1 12rem; min-width: 0; }

/* --------------------------------------------------------------------------
   18. Auditoria
   -------------------------------------------------------------------------- */

/* Seis filtros lado a lado não cabem no celular; esta grade vira uma coluna
   abaixo de 768px e duas a partir daí, antes de abrir para seis no desktop. */
.grade--filtros {
  grid-template-columns: 1fr;
  gap: var(--e-3);
}

@media (min-width: 640px) {
  .grade--filtros { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1024px) {
  .grade--filtros { grid-template-columns: repeat(3, 1fr); }
}

.agorarapido { white-space: nowrap; font-variant-numeric: tabular-nums; }

.tabela--compacta { font-size: 0.8125rem; }
.tabela--compacta th { padding: var(--e-2) var(--e-3); }
.tabela--compacta td { padding: var(--e-2) var(--e-3); }

.detalhe > summary {
  display: inline-block;
  cursor: pointer;
  list-style: none;
}

.detalhe > summary::-webkit-details-marker { display: none; }
.detalhe[open] > summary { margin-bottom: var(--e-3); }

.detalhe__conteudo {
  display: grid;
  gap: var(--e-3);
  padding: var(--e-3);
  margin-top: var(--e-2);
  background: var(--superficie-alt);
  border: 1px solid var(--borda);
  border-radius: var(--r-sm);
  /* Valores de campo podem ser longos (mensagens, observações) e não podem
     empurrar a tabela para fora da tela. */
  overflow-wrap: anywhere;
}

.par {
  display: flex;
  gap: var(--e-3);
  justify-content: space-between;
  font-size: 0.8125rem;
}

.par > span { color: var(--texto-fraco); }

.lista-campos {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: var(--e-1) var(--e-3);
  margin-top: var(--e-2);
  font-size: 0.8125rem;
}

.lista-campos > div { display: contents; }
.lista-campos dt { color: var(--texto-fraco); }
.lista-campos dd { margin: 0; overflow-wrap: anywhere; }

/* --------------------------------------------------------------------------
   19. Impressão
   -------------------------------------------------------------------------- */

/* A lista é de esconder, e não de mostrar — então o que não está aqui continua
   saindo no papel. É o caso do rodapé: uma lista de abastecimentos impressa
   carrega a data e o usuário, mas não de qual versão do sistema ela saiu, e
   essa é a hora em que a marca faz falta. */
@media print {
  .topo, .barra-fixa, .botao, .menu { display: none !important; }
  body { background: #fff; }
  .cartao { border: 1px solid #ccc; box-shadow: none; break-inside: avoid; }

  /* Os tokens do tema escuro **não** são desfeitos pela impressão: o
     `prefers-color-scheme` continua valendo ao imprimir, e o `body` acima força
     a folha para branco sem trocar a cor do texto. Quem imprime com o sistema
     em escuro manda `--texto: #e9ecf1` para o papel — 1,09:1, ou seja, uma
     folha quase em branco, e não só o rodapé: a tabela inteira.

     Isto é um defeito que já existia, e que só apareceu quando a marca de
     versão passou a depender da impressão. Consertá-lo levou **três** tentativas,
     e vale registrar por quê: as duas primeiras afirmaram ter consertado "a
     folha toda" sem ter consertado, e nenhuma das duas levantou erro.

     1. só o `body` para branco — o texto continuou claro (1,09:1);
     2. mais os tokens de texto — o cartão ficou `#171c23` com texto `#101724`,
        a **1,05:1**;
     3. mais os tokens **semânticos**, que é este bloco — link em `#4b8dfa`
        (3,24:1), `.campo__erro` em `#f87171` (2,77:1), aviso de sucesso em
        `#4ade80` (1,74:1). Medido no papel, rasterizando o PDF e lendo o pixel.

     Por isso a lista abaixo é **a lista inteira**, e não as cores que parecem
     importar: são todos os 29 tokens que os dois temas redefinem. Quem
     acrescentar um token ao tema tem de acrescentá-lo aqui também — e não
     precisa lembrar disso, porque `apps/core/tests_css.py` compara os dois
     `:root` de tema com este bloco e falha se faltar algum. Foi a única das
     três correções que veio acompanhada de uma trava.

     Duas notas sobre a ordem. Os tokens vêm antes de `color-scheme` porque é
     ele que manda pintar os **controles nativos** que o app não reestiliza —
     `<select>`, checkbox, campo de data: sem esta linha eles continuam escuros
     na folha branca mesmo com todos os tokens certos. E as sombras vão para
     `none` em vez de receberem o valor do tema claro, porque sombra em papel não
     é profundidade, é sujeira: o cinza da mancha sai impresso como borrão. */
  /* O seletor do tema escuro entra na lista: ele tem **mais especificidade**
     que um `:root` sozinho (`:root[data-tema="escuro"]` é atributo mais
     pseudoclasse), e sem esta repetição todos os tokens abaixo perderiam a
     disputa — a folha sairia escura para quem escolheu o escuro. */
  :root, :root[data-tema="escuro"] {
    color-scheme: light;

    --fundo:          #ffffff;
    --superficie:     #ffffff;
    --superficie-alt: #eef1f7;
    --borda:          #e3e7ef;
    --borda-forte:    #c8cfdb;
    --texto:          #101724;
    --texto-suave:    #4d5666;
    --texto-fraco:    #626b7a;

    --primaria:           #2563eb;
    --primaria-forte:     #1d4ed8;
    --primaria-suave:     #eef3ff;
    --primaria-contraste: #ffffff;

    --ok:            #0f7b3f;
    --ok-suave:      #e6f6ec;
    --atencao:       #93610a;
    --atencao-suave: #fdf3e0;
    --erro:          #b42318;
    --erro-suave:    #fdecea;
    --info:          #0b6b8f;
    --info-suave:    #e6f3f8;

    --serie-1: #2a78d6;
    --serie-2: #e07b39;
    --serie-3: #0f9d8f;
    --serie-4: #8b5cf6;
    --serie-5: #d6455d;
    --serie-6: #7a8290;
    /* No papel o verde claro do tema escuro some (2,3:1 sobre o branco);
       o escuro é o que sobrevive à impressão. */
    --serie-7: #15803d;

    --sombra-1: none;
    --sombra-2: none;
    --sombra-3: none;
  }
}

/* --------------------------------------------------------------------------
   20. Modal do lançamento
   --------------------------------------------------------------------------
   Seção no fim, e não junto das outras de componente, porque renumerar de 14 a
   19 para abrir espaço sujaria o `git blame` de centenas de linhas por causa de
   um comentário. A ordem do arquivo é uma conveniência de leitura; a seção
   continua achável pelo nome.
   -------------------------------------------------------------------------- */

.modal {
  width: calc(100% - var(--e-4));
  max-width: 720px;
  max-height: calc(100vh - var(--e-6));
  padding: 0;
  border: 1px solid var(--borda);
  border-radius: var(--r);
  background: var(--superficie);
  color: var(--texto);
  box-shadow: var(--sombra-3);
  /* O `margin: auto` não é decoração: é ele que centraliza o `<dialog>` modal,
     e vem do estilo do navegador — que o `* { margin: 0 }` do reset apaga.
     Sem esta linha o popup nasce colado no canto superior esquerdo. */
  margin: auto;
  /* Coluna, para o cabeçalho ficar parado e só o corpo rolar. Sem isto o corpo
     precisaria de um `max-height` próprio calculado à mão, que erra sempre que
     o cabeçalho muda de altura. */
  flex-direction: column;
  /* `overflow: hidden` para o conteúdo rolado não escapar dos cantos
     arredondados, e para o `max-height` acima de fato valer. */
  overflow: hidden;
  /* A rolagem que chega ao fim do modal **para ali**, em vez de atravessar e
     rolar a página atrás dele. Sem isto, quem está no celular e continua
     arrastando depois do último campo move a lista do fundo — e ao fechar o
     modal a tela está noutro lugar, sem que nada explique por quê. */
  overscroll-behavior: contain;
}

/* O `display` só vale com o diálogo aberto, e a guarda não é zelo: **um
   `display` de autor sobrepõe o do navegador**. Um `<dialog>` fechado é
   escondido pelo user agent com `display: none`, e escrever `display: flex` na
   regra `.modal` crua apaga esse `none` — o popup passa a existir desde o
   carregamento, no fluxo normal, no lugar em que ele está no HTML (o fim do
   `<body>`, ou seja, o rodapé da página), e o `close()` não o esconde: só tira
   o atributo `open`, que já não decidia mais nada.

   O defeito passa por qualquer conferência que abra o modal: com
   `showModal()` o diálogo vai para a *top layer* e aparece centrado **mesmo**
   estando posicionado no fim do documento. Quem denuncia é o estado fechado.
   Daí a asserção de `display` no `tests/js/modal.html`. */
.modal[open] {
  display: flex;
}

/* O `:focus-visible` global desenha o anel azul em quem recebe foco. Com o
   foco no próprio diálogo — o que acontece quando nenhum filho o pega —, o
   anel contornaria o modal inteiro. O diálogo não é alvo de interação; quem
   precisa do anel são os campos dentro dele, e esses continuam tendo. */
.modal:focus-visible { outline: none; }

.modal::backdrop {
  background: color-mix(in srgb, #000 55%, transparent);
}

.modal__topo {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-3);
  padding: var(--e-4) var(--e-5);
  border-bottom: 1px solid var(--borda);
}

.modal__titulo {
  margin: 0;
  font-size: 1.05rem;
}

/* O corpo rola, o cabeçalho não: num formulário longo, o "Fechar" precisa
   continuar à vista em vez de subir junto com o conteúdo. `min-height: 0` é o
   que permite a um filho de flex encolher abaixo do próprio conteúdo — sem
   ele, o `overflow-y` nunca entra em ação e o modal cresce além da tela. */
.modal__corpo {
  flex: 1;
  min-height: 0;
  padding: var(--e-5);
  overflow-y: auto;
}

@media (max-width: 640px) {
  .modal {
    width: 100%;
    max-width: none;
    max-height: 100vh;
    /* A folga do `margin: auto` vira borda morta em tela pequena: a altura já
       é 100vh, e um respiro em volta só encolheria o formulário. */
    margin: 0;
    border-radius: 0;
  }
  .modal__topo { padding: var(--e-3) var(--e-4); }
  .modal__corpo { padding: var(--e-4); }
}

/* --------------------------------------------------------------------------
   21. Rodapé
   --------------------------------------------------------------------------
   No fim do arquivo pelo mesmo motivo que a seção do modal: abrir espaço no
   meio custaria renumerar as seções seguintes por causa de um comentário. A
   ordem do arquivo é conveniência de leitura, não dependência — nada aqui
   depende de vir depois de coisa alguma. */

/* Fica no fim do `.app`, que é uma coluna flex de altura mínima da janela, com
   o `.conteudo` em `flex: 1`. É isso que o prende ao pé da tela quando a página
   é curta, em vez de flutuar no meio do conteúdo.

   Os dois cinzas usados aqui — `--texto-fraco` no conjunto, `--texto-suave` no
   SHA — são os mesmos do resto da interface, e ambos passam AA sobre `--fundo`
   nos dois temas (medido: 4,97:1 e 6,84:1 no claro, 5,63:1 e 8,54:1 no escuro).
   Não há tom novo só para o rodapé: uma cor exclusiva para "texto de rodapé" é
   como se começa a ter uma escala de cinzas que ninguém mais consegue manter. */
.rodape {
  border-top: 1px solid var(--borda);
  padding-block: var(--e-3);
}

.rodape__interno {
  display: flex;
  justify-content: flex-end;
}

.rodape__versao {
  font-size: 0.75rem;
  color: var(--texto-fraco);
  /* O `title` é a explicação do `+` e do `?`. Sem ele os sinais não se
     explicam, e um cursor de interrogação é o aviso mais barato de que há algo
     a ler ali. */
  cursor: help;
}

.rodape__sha,
.rodape__data {
  font-family: var(--fonte-num);
}

.rodape__sha {
  font-weight: 600;
  color: var(--texto-suave);
}
