﻿/* ============================================================
   base.css — OS COMPONENTES-BASE DO EMBARQUE (05/09/2026).

   A fundacao visual, extraida da Horizonte Viagens (o ambiente demo)
   e consolidada. Nao e um visual novo: e o mesmo, com os buracos
   tapados. O que mudou em relacao a tela de origem esta anotado em
   cada componente, com o motivo.

   ------------------------------------------------------------
   O PREFIXO

   Todo componente daqui comeca com `ds-`. Nao e enfeite: os nomes de
   casa (.cartao, .selo, .b, .vazio) ja existem dentro do <style> de
   agenda.html, e `emb-` — que seria o prefixo natural do Embarque —
   ja e da placa de embarques. Duas definicoes da mesma classe em dois
   arquivos e o defeito mais silencioso que existe em CSS: ganha quem
   carregar por ultimo, sem erro nenhum.

   No dia em que agenda.html migrar, o prefixo cai junto com o <style>
   local dela. Ate la ele e a garantia de que os dois convivem.

   ------------------------------------------------------------
   O QUE ESTE ARQUIVO NAO FAZ

   Nao define cor, tamanho, espaco, raio, duracao nem sombra: tudo vem
   de tokens.css, sem excecao. Um valor cru aqui e um valor que nao
   troca no tema escuro — e o defeito so aparece para quem usa o
   escuro, que nao e quem escreveu a regra.

   testes/test_tokens.js varre este arquivo e reprova valor cru.
   ============================================================ */

/* ============================================================
   1. FUNDAMENTO
   ============================================================ */

/* ESCONDIDO E ESCONDIDO, PONTO.
   O `hidden` do HTML vale por uma regra do navegador de prioridade
   minima: qualquer classe daqui que declare `display` vence, e o
   elemento fica na tela com hidden=true — sem erro, sem log. Ja
   aconteceu duas vezes neste projeto (o esqueleto fantasma e o botao
   "Pausar o assistente"). Uma regra resolve para o arquivo inteiro. */
[hidden] { display: none !important; }

.ds-corpo {
  margin: 0;
  background: var(--surface-page);
  color: var(--text-primary);
  font-family: var(--font);
  /* O CORPO DA PAGINA E 13 desde 07/09 (era 14). A secao 2 chama 13 de
     corpo e 14 nao existe mais na escala — e o padrao do <body> e o
     tamanho que TODA regra sem font-size herda, entao ele e o primeiro
     lugar a acertar, nao o ultimo. */
  font-size: var(--fs-13);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
.ds-corpo * { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
/* O CONTROLE NAO HERDA A FONTE SOZINHO (07/09/2026). button, input,
   select e textarea nascem com a fonte do SISTEMA OPERACIONAL, e nao
   com a da pagina: no Chrome isso e Arial 13,3333px. Medido na tela —
   depois de a escala inteira ir para os cinco degraus da secao 2, o
   navegador ainda devolvia 13.3333px em todo botao de icone, um sexto
   de pixel fora de qualquer degrau que alguem tenha escolhido.

   Nao e visivel num botao so de icone; e visivel no dia em que um
   botao ganhar texto e ninguem entender por que ele desalinha do
   vizinho. Herdar e o padrao certo: quem quiser outro tamanho declara.

   SO O BOTAO, E ISSO E DELIBERADO. Incluir input/select/textarea aqui
   REINTRODUZIRIA o zoom do iPhone, e pelo caminho que o proprio
   test_zoom_ios.js descreve: ".ds-corpo input" tem especificidade
   (0,1,1) e venceria o "input { font-size: 16px }" solto dentro do
   @media (pointer: coarse), que tem (0,0,1) — media query nao
   acrescenta especificidade. O campo cairia para 13px no toque, e o
   teste continuaria verde, porque ele confere o TEXTO da regra e nao a
   cascata. Escrito aqui porque eu escrevi a regra errada primeiro. */
.ds-corpo button { font-family: inherit; font-size: inherit; line-height: inherit; }
/* O <b> CRU FICAVA EM 700, FORA DA ESCALA (07/09/2026 — checklist 8).
   Medido na bancada: "Luana Ferreira" na caixa, "12 vendas" em Vendas,
   "Relacionamento" no Inicio e a receita de cada linha em Clientes
   saiam todos em 13/700, enquanto a secao 2 so tem 400 e 500. Sao 137
   'el(b, null, ...)' no arquivo — nenhum deles pediu 700; eles
   herdaram o padrao do NAVEGADOR, que a escala nao governa.

   E o mesmo caso do 'button' da linha de cima: o reset da fundacao
   existe para que o padrao do navegador nao decida tipografia. O <b>
   continua sendo <b> — ele marca o que importa na linha, e agora marca
   com o peso que o sistema tem. */
.ds-corpo b, .ds-corpo strong { font-weight: var(--peso-medio); }

/* <small> SEM CLASSE CAI NO DEGRAU DE APOIO (07/09/2026). O navegador
   da a ele 0,8333em — 10,83px em cima de um corpo de 13 — e isso nao e
   degrau nenhum: e uma fracao que ninguem escolheu. Medido na tela Hoje,
   onde tres <small> sem classe ("Clientes em atraso", "8 parcelas",
   "R$ 9.520 de acrescimo") sairam em 10,83px depois de a escala inteira
   ja estar nos cinco degraus.

   E <small> E APOIO POR DEFINICAO — e o papel de 11px da secao 2 — entao
   o padrao certo e 11, e nao uma fracao do que vier por cima. Quem
   quiser outro tamanho declara na tela, que vem depois deste arquivo na
   cascata e tem a mesma especificidade. */
.ds-corpo small { font-size: var(--fs-11); }

/* O FOCO VISIVEL E GLOBAL, e nao por componente.
   Escrever :focus-visible em cada peca garante que a proxima peca
   nasca sem ele. Aqui todo elemento focavel ganha o anel; um
   componente que precise de outro desenho sobrescreve, e isso fica
   explicito na regra dele.

   O `border-radius` SAIU DAQUI EM 06/09/2026, e ele estava mudando a
   forma das pecas. Medido com o teclado na propria /design: um Tab ate
   o `.ds-cartao--clicavel` derrubava o raio de 12px para 8px, e o
   cartao PULAVA de forma no instante do foco. A conta e de
   especificidade — `.ds-corpo :focus-visible` e (0,2,0) e ganha de
   qualquer componente escrito com uma classe so, entao o raio do anel
   virava o raio do objeto. O mesmo valia para tudo que e pilula: selo,
   badge e filtro ficavam quadrados enquanto focados.

   Nao era enfeite: a intencao era dar canto macio ao anel de um link,
   que nao tem raio proprio. Mas `outline` JA acompanha o
   `border-radius` do elemento em todo navegador atual — entao o anel
   continua seguindo a forma de quem ele contorna, e agora sem impor
   forma a ninguem. Link sem raio ganha anel de canto vivo, que e o
   preco certo por nenhum componente do sistema mudar de silhueta ao
   ser alcancado pelo teclado. */
.ds-corpo :focus-visible {
  outline: 2px solid var(--accent-fill);
  outline-offset: 2px;
}

/* QUEM PEDIU MENOS MOVIMENTO RECEBE MENOS MOVIMENTO.
   As transicoes daqui sao sutis, mas "sutil" nao e o criterio de quem
   sente enjoo com movimento na tela. */
@media (prefers-reduced-motion: reduce) {
  .ds-corpo *, .ds-corpo *::before, .ds-corpo *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ============================================================
   1b. LAYOUT — a regra da goteira e da largura de leitura

   Escrita em 05/09/2026, depois de a propria /design errar as duas
   pontas: `margin: 0 auto` num miolo de 940px dentro de uma janela
   larga abria um corredor vazio entre o menu e o texto, e ainda
   sobrava faixa morta na direita.

   A regra tem tres partes, e a do meio e a que quase todo mundo
   esquece:

   a) GOTEIRA de --space-9 (48px) entre a coluna de navegacao e o
      conteudo. Menos que isso cola o texto no menu; mais que isso
      comeca a parecer margem de livro.

   b) TEXTO CORRIDO para em ~72 caracteres. Nao e estetica: linha
      longa demais faz o olho perder a volta ao inicio da proxima.
      Vale para paragrafo, e SO para paragrafo.

   c) O RESTO USA A LARGURA. Amostra de componente, tabela, grade de
      cartao e formulario nao sao texto corrido — espremer uma tabela
      de parcelas em 72 caracteres para "respeitar a leitura" e usar
      uma regra de prosa contra um objeto que nao e prosa. Teto de
      1240px, que e onde uma linha de tabela ainda se le de ponta a
      ponta sem mover a cabeca.

   d) O CONTEUDO SE CENTRA NA AREA UTIL — e "area util" e a janela JA
      SEM o menu. Esta parte levou tres versoes, e as duas erradas
      erraram por motivos opostos:

      v1: `margin: 0 auto` num teto de 940px, centrado na janela
          inteira. Errado, e feio: a sobra ficava grande demais dos
          dois lados, e o texto fugia do menu para morar no meio da
          tela, longe de onde a pessoa acabou de clicar.
      v2: ancorado a esquerda, sem centrar. Consertou a fuga e criou
          assimetria: a 1920px sobravam 48px de um lado e 250 do
          outro, e o conteudo parecia empurrado para o canto.
      v3, esta: centrado DENTRO DA AREA UTIL, com teto e minimo de
          48px de cada lado. A sobra e sempre dividida em duas, e
          nunca cai abaixo dos 48.

      A diferenca entre a v1 e a v3 nao e o `auto`, e o TETO: com 940
      de teto sobra meia tela para repartir, e o resultado e um
      conteudo perdido no meio; com o teto da fundacao, a sobra e
      pequena o bastante para que centrar signifique so equilibrar as
      margens. Foi o teto que estava errado na v1, nao a centralizacao.

   O TETO E 1560px DESDE 08/09/2026, decisao do dono sobre seis prints
   de producao: o 1440 anterior deixava faixa morta dos dois lados numa
   tela de 1920, e a "excecao larga" de 1560 cobria so cinco secoes —
   tela nova nascia espremida por sorteio de familia. O teto da excecao
   virou o padrao de todos, e a excecao morreu. Acima de 1560 o teto
   segue existindo de proposito: passando disso, uma linha de tabela
   deixa de ser lida de ponta a ponta sem virar a cabeca.

   A CONTA, por extenso: a largura e a area util menos duas goteiras,
   limitada ao teto; o que sobrar vira margem automatica dos dois
   lados. Abaixo do teto as margens SAO as goteiras (48 cada); acima
   dele, elas crescem juntas.

     1920px de janela -> util 1688 -> conteudo 1560 ->  64 de cada lado
     1440px de janela -> util 1208 -> conteudo 1112 ->  48 de cada lado

   ATENCAO ao aplicar: isto NAO funciona num item com `flex: 1`. A base
   do `flex: 1` e zero, e ela ignora `width` no eixo principal — a
   regra vai numa caixa DENTRO da coluna flexivel, nunca na coluna.
   ============================================================ */

.ds-conteudo {
  /* O `* 2` mantem a goteira ligada ao token: 96px cru aqui e um
     numero que ninguem liga ao --space-9 no dia de mexer nele. */
  width: min(100% - var(--space-9) * 2, 1560px);
  margin-inline: auto;
  padding: var(--space-6) 0 var(--space-8);
}
/* ------------------------------------------------------------
   A EXCECAO LARGA MORREU AQUI (08/09/2026)

   Ela nasceu em 06/09 com lista de cinco telas e motivo por tela
   ("truncar nome de cliente e pior que sobra de margem"). Dois dias
   depois o dono marcou de azul, em seis prints de producao, a faixa
   morta que as OUTRAS secoes carregavam — e o teto da excecao (1560)
   virou o padrao de todas. `.ds-conteudo--largo` sumiu porque virou
   sinonimo do `.ds-conteudo`, e classe-sinonimo e onde a proxima
   divergencia se esconde; nenhum markup a usava.
   ------------------------------------------------------------ */

/* Paragrafo, e so paragrafo. */
.ds-leitura { max-width: 72ch; }
/* Coluna de formulario: nem 72ch (o par rotulo/controle se perde) nem
   1240 (o campo vira uma faixa). Os campos se distribuem dentro dela. */
.ds-form {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 0 var(--space-4); align-items: start;
}
.ds-form--largo { grid-column: 1 / -1; }

@media (max-width: 900px) {
  /* Sem coluna de navegacao ao lado, a goteira vira margem de tela — e
     48px de cada lado num telefone comeriam metade da linha. */
  .ds-conteudo { width: min(100% - var(--space-4) * 2, 1440px); }
}

/* ============================================================
   2. TIPOGRAFIA — os cinco papeis da secao 2 (07/09/2026)
   ============================================================
   Cada classe e UM PAPEL da tabela, e o nome diz o papel. A escala
   anterior (display 34, titulo 22, subtitulo 18, texto 14, apoio 13)
   era um degrau maior em cada linha, e foi ela que produziu a queixa
   de "letras de tamanho diferente": duas telas escritas em meses
   diferentes escolhiam degraus vizinhos para o mesmo papel, e a
   diferenca de um ponto le como erro de renderizacao.

   ".ds-display" virou ".ds-numero" de proposito. Chamar de "display"
   um numero de 18px seria o nome mentindo sobre o tamanho — e nome que
   mente e como a escala de nove degraus volta. */

.ds-numero    { font-size: var(--fs-18); font-weight: var(--peso-medio); line-height: 1.15; letter-spacing: -.01em; }
.ds-titulo    { font-size: var(--fs-17); font-weight: var(--peso-medio); line-height: 1.3;  letter-spacing: -.01em; margin: 0; }
.ds-subtitulo { font-size: var(--fs-13); font-weight: var(--peso-medio); line-height: 1.35; margin: 0; }
.ds-texto     { font-size: var(--fs-13); font-weight: var(--peso-normal); line-height: 1.5; margin: 0; }
.ds-apoio     { font-size: var(--fs-11); font-weight: var(--peso-normal); line-height: 1.45; color: var(--text-secondary); margin: 0; }

/* O TABULAR E OBRIGATORIO EM DINHEIRO E EM DATA.
   Sem ele os algarismos tem larguras diferentes e uma coluna de
   valores fica serrilhada — o olho perde a casa decimal, que e a
   unica coisa que ele procura numa coluna de dinheiro. */
.ds-num { font-variant-numeric: tabular-nums; }

/* ============================================================
   3. BOTAO

   Vem do `button.b` da Horizonte, com tres coisas que faltavam:

   a) FOCO. O `.b` de la nao tinha :focus-visible proprio; dependia de
      uma regra global escrita 1.100 linhas depois. Aqui ele e do
      componente.

   b) DESABILITADO DE VERDADE. La era `opacity: .55`, que apaga o
      texto junto com o fundo e derruba o contraste do rotulo abaixo
      de qualquer limite legivel. Aqui o desabilitado tem cor propria:
      continua legivel, e nao parece clicavel.

   c) CARREGANDO. Nao existia em lugar nenhum das 4.636 linhas de CSS
      da tela. Um botao que envia mensagem e nao diz que esta enviando
      e clicado duas vezes — foi assim que "cliquei e nao aconteceu
      nada" virou um teste inteiro neste projeto.
   ============================================================ */

.ds-bt {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  height: var(--alt);
  /* 24px, e nao 16px. O acolchoamento horizontal e o que RESERVA o
     lugar da roda de carregando — ver o bloco de `aria-busy` abaixo.
     E simetrico de proposito: reservar so a direita empurraria o
     rotulo 8px para fora do centro em todo botao do sistema. */
  padding: 0 var(--space-6);
  border: 1px solid transparent; border-radius: var(--radius-bt);
  font: inherit; font-size: var(--fs-13); font-weight: var(--peso-medio);
  text-decoration: none; white-space: nowrap; cursor: pointer;
  background: var(--accent-fill); color: var(--on-accent);
  transition: filter var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease),
              background-color var(--dur-fast) var(--ease);
  position: relative;
}
.ds-bt:hover:not(:disabled):not([aria-busy="true"]) { filter: brightness(1.08); }
.ds-bt:active:not(:disabled):not([aria-busy="true"]) { transform: scale(.975); filter: brightness(.93); }

/* ---- secundario: o botao que acompanha, e nao lidera ---- */
.ds-bt--secundario {
  background: var(--surface-card); color: var(--text-primary);
  border-color: var(--border-campo);
}
.ds-bt--secundario:hover:not(:disabled):not([aria-busy="true"]) {
  filter: none; background: var(--surface-sunken);
}

/* ---- perigo: apaga, desliga, derruba alguem ----
   Tinta clara, e nao vermelho solido: um botao vermelho cheio dentro
   de um dialogo puxa o olho antes da pergunta, e a pessoa confirma
   sem ler. O vermelho vive no texto e na borda. */
.ds-bt--perigo {
  background: var(--danger-bg); color: var(--danger-text);
  border-color: var(--danger-fill);
}
.ds-bt--perigo:hover:not(:disabled):not([aria-busy="true"]) {
  filter: none; background: var(--danger-fill); color: var(--on-danger);
}

/* ---- discreto: acao terciaria, sem moldura ---- */
.ds-bt--discreto {
  background: transparent; color: var(--text-secondary); border-color: transparent;
}
.ds-bt--discreto:hover:not(:disabled):not([aria-busy="true"]) {
  filter: none; background: var(--surface-sunken); color: var(--text-primary);
}

.ds-bt--largo { width: 100%; }

/* ---- desabilitado ----
   Cor propria em vez de opacidade. O rotulo continua a 4,5:1 do fundo:
   quem nao pode clicar ainda precisa LER o que nao pode clicar. */
.ds-bt:disabled,
.ds-bt[aria-disabled="true"] {
  cursor: not-allowed;
  background: var(--surface-sunken);
  color: var(--text-muted);
  border-color: var(--border);
  filter: none; transform: none;
}

/* ---- carregando ----
   `aria-busy` e o estado, e nao uma classe: o leitor de tela precisa
   saber que o botao esta ocupado, e uma classe nao diz isso a ninguem.

   TRES EXIGENCIAS AO MESMO TEMPO, e as duas primeiras versoes deste
   bloco cumpriram duas e quebraram a terceira:

     1. o rotulo CONTINUA LEGIVEL — quem clicou precisa saber o que
        esta esperando, e "Carregando…" nao diz qual acao foi;
     2. a roda fica AO LADO, e nao por cima do texto;
     3. a largura do botao NAO MUDA — botao que cresce no clique faz a
        linha inteira dancar e quem estava lendo perde o lugar.

     v1: roda ao lado com `flex` e margem. Cumpriu 1 e 2, quebrou 3 —
         o botao pulava de 184 para 215 pixels, medido na bancada.
     v2: roda absoluta no centro e rotulo em `opacity: 0`. Cumpriu 3,
         quebrou 1 — o rotulo sumia, contra a decisao escrita.
     v3: roda absoluta dentro de um acolchoamento de 16px. Sobravam
         2px entre a ultima letra e a roda — que nao e folga, e colisao
         visual. E os 2px eram os mesmos para qualquer rotulo: a conta
         nunca dependeu do comprimento, so PARECIA maior no rotulo
         curto porque havia mais ar em volta.
     v4, esta: O ACOLCHOAMENTO RESERVA O LUGAR DA RODA, sempre. Sao
         24px de cada lado; a roda tem 12px e fica a 4px da borda,
         ocupando de 4 a 16 — entao entre ela e a ultima letra sobram
         8px, com o minimo exigido em 6. E a conta nao depende do
         rotulo: a distancia da ultima letra ate a borda e o
         acolchoamento, e ele e constante.

   O CUSTO ACEITO: todo botao do sistema ficou 16px mais largo,
   inclusive os que nunca carregam. E o preco de o comportamento ser o
   mesmo em todos — previsivel vale mais que compacto. Simetrico
   porque reservar so um lado tira o rotulo do centro.

   A OPACIDADE DE 0,72 NAO E ESTETICA, E MEDIDA. Opacidade compoe em
   sRGB, entao o rotulo esmaecido e uma mistura dele com o fundo do
   proprio botao. Medido nas quatro variantes e nos dois temas, o pior
   caso e o primario no escuro: 3,18:1 a 0,70 e 3,29:1 a 0,72 — acima
   do minimo de 3:1 da WCAG para texto grande. Abaixo de 0,70 reprova,
   e reprova primeiro no escuro, que e onde ninguem olha. */
.ds-bt[aria-busy="true"] {
  cursor: progress; pointer-events: none;
  /* Nenhum padding extra AQUI: o lugar da roda ja esta reservado no
     `.ds-bt`. Mexer no acolchoamento neste estado e o que faria o
     botao pular no clique. */
}
.ds-bt[aria-busy="true"] .ds-bt-rotulo { opacity: .72; }
.ds-bt[aria-busy="true"]::after {
  content: "";
  position: absolute; top: 50%; right: 4px;
  width: 12px; height: 12px; margin-top: -6px;
  border: 2px solid currentColor; border-right-color: transparent;
  border-radius: var(--radius-pill);
  animation: ds-girar .62s linear infinite;
}
@keyframes ds-girar { to { transform: rotate(1turn); } }

/* ============================================================
   4. CAMPO — rotulo, controle, ajuda, erro

   O contorno usa --border-campo (3:1), e nao --border (1,25:1): a
   WCAG 1.4.11 exige que o contorno de um campo se distinga do fundo,
   porque ele e a unica coisa que diz onde o campo comeca.

   O erro NUNCA e so a borda vermelha. Cor sozinha nao e informacao
   (WCAG 1.4.1): quem nao distingue vermelho ve um campo igual aos
   outros. Vem sempre com texto, e o texto e ligado ao controle por
   aria-describedby para o leitor de tela ouvir junto.
   ============================================================ */

.ds-campo { display: flex; flex-direction: column; gap: var(--space-1); margin-bottom: var(--space-4); }

.ds-campo > label {
  font-size: var(--fs-13); font-weight: var(--peso-medio);
  color: var(--text-secondary);
}
.ds-campo > label .ds-campo-opcional {
  font-weight: var(--peso-normal); color: var(--text-muted);
}

.ds-campo input,
.ds-campo select,
.ds-campo textarea {
  width: 100%; padding: 10px 12px;
  font: inherit;
  /* 16px no controle e regra do iOS, nao gosto: abaixo disso o Safari
     do iPhone da zoom sozinho ao focar, e a tela inteira sai do lugar
     no meio de um cadastro. E a excecao documentada em tokens.css. */
  font-size: var(--fs-16);
  color: var(--text-primary); background: var(--surface-card);
  border: 1px solid var(--border-campo); border-radius: var(--radius-bt);
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.ds-campo textarea { min-height: 84px; resize: vertical; line-height: 1.45; }

.ds-campo input:hover:not(:disabled),
.ds-campo select:hover:not(:disabled),
.ds-campo textarea:hover:not(:disabled) { border-color: var(--text-muted); }

.ds-campo input:focus-visible,
.ds-campo select:focus-visible,
.ds-campo textarea:focus-visible {
  outline: none;
  border-color: var(--accent-fill);
  box-shadow: 0 0 0 3px var(--accent-bg);
}

.ds-campo input:disabled,
.ds-campo select:disabled,
.ds-campo textarea:disabled {
  background: var(--surface-sunken); color: var(--text-muted);
  border-color: var(--border); cursor: not-allowed;
}

.ds-campo-ajuda { font-size: var(--fs-13); color: var(--text-secondary); }

/* ---- erro ---- */
.ds-campo--erro input,
.ds-campo--erro select,
.ds-campo--erro textarea { border-color: var(--danger-fill); }
.ds-campo--erro input:focus-visible,
.ds-campo--erro select:focus-visible,
.ds-campo--erro textarea:focus-visible {
  border-color: var(--danger-fill); box-shadow: 0 0 0 3px var(--danger-bg);
}
.ds-campo-erro {
  display: flex; align-items: flex-start; gap: var(--space-1);
  font-size: var(--fs-13); font-weight: var(--peso-medio); color: var(--danger-text);
}
/* O simbolo antes do texto e o que faz o erro nao depender so da cor. */
.ds-campo-erro::before { content: "!"; flex: none;
  width: 15px; height: 15px; margin-top: 1px;
  border-radius: var(--radius-pill);
  background: var(--danger-fill); color: var(--on-danger);
  font-size: var(--fs-11); line-height: 15px; text-align: center; font-weight: var(--peso-medio);
}

/* ============================================================
   5. CARTAO
   ============================================================ */

.ds-cartao {
  background: var(--surface-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-4);
  box-shadow: var(--elevacao-1);
}
.ds-cartao--fundo { box-shadow: none; }
.ds-cartao--flutua { box-shadow: var(--elevacao-2); }

.ds-cartao-topo {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--space-3); margin-bottom: var(--space-3);
}
.ds-cartao-rotulo {
  font-size: var(--fs-13); color: var(--text-secondary);
  margin: 0 0 var(--space-1);
}
.ds-cartao-valor {
  font-size: var(--fs-18); font-weight: var(--peso-medio);
  font-variant-numeric: tabular-nums; letter-spacing: -.015em;
}

/* Um cartao clicavel e um botao: precisa dizer isso ao teclado. */
.ds-cartao--clicavel { cursor: pointer; text-align: left; width: 100%; font: inherit; color: inherit;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease); }
.ds-cartao--clicavel:hover { border-color: var(--border-strong); box-shadow: var(--elevacao-2); }

/* ============================================================
   6. BADGE — a limpeza do .selo

   A Horizonte tinha SEIS variantes para quatro papeis, e tres delas
   se contradiziam:
     .selo            (sem variante) vermelho, com --danger-fill
     .selo.critico    vermelho, com --danger-text — outro vermelho
     .selo.calmo      afundado/secundario
     .selo.neutro     IDENTICO ao .calmo, nome diferente
     .selo.neutra     accent-bg — terceiro nome, quase homonimo, outra cara
     .selo.boa        SOLIDO, enquanto todos os outros sao tinta clara

   Aqui sao CINCO papeis e um desenho so: tinta clara de fundo, texto
   forte do mesmo papel. Solido fica reservado para o que precisa
   gritar, e nada num badge precisa gritar.

   E nao ha margem embutida. A margem de 9px que o .selo carregava e
   layout de quem o usa, nao propriedade do que ele e — foi ela que
   obrigou `.forn-numeros .selo` a existir so para desfaze-la.
   ============================================================ */

.ds-badge {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 2px 9px;
  /* 8px, e nao circulo: a secao 2 chama isto de "etiqueta de status
     (pilula)" e da 8px a ela. O --radius-pill continua existindo para
     quem e redondo de verdade — avatar, trilho, bolinha de erro. */
  border-radius: var(--radius-control);
  font-size: var(--fs-12); font-weight: var(--peso-medio); line-height: 18px;
  letter-spacing: .02em; white-space: nowrap;
  background: var(--surface-sunken); color: var(--text-secondary);
}
.ds-badge--info    { background: var(--accent-bg); color: var(--accent-text); }
.ds-badge--ok      { background: var(--ok-bg);     color: var(--ok-text); }
.ds-badge--atencao { background: var(--warn-bg);   color: var(--warn-text); }
.ds-badge--critico { background: var(--danger-bg); color: var(--danger-text); }

/* Badge que e botao (filtro ligado/desligado) — mesma cara, teclado. */
button.ds-badge { border: 0; font-family: inherit; cursor: pointer; }
button.ds-badge:hover { filter: brightness(.96); }

/* ---- "Em desenvolvimento" ----
   Vem do .selo-obra da Horizonte, com uma unica mudanca: a cor. La ele
   era --text-muted a 2,96:1, reprovado em AA — e em 11px caixa alta,
   que e o pior caso possivel. Agora o token esta consertado, e o selo
   herda o conserto.

   O rotulo curto e o que cabe; a frase inteira mora no title e no
   aria-label, porque em menu estreito nao ha largura para ela e
   esconder a informacao do leitor de tela nao e uma opcao. */
.ds-obra {
  display: inline-block; flex: none;
  padding: 0 var(--space-1h); border-radius: var(--radius-control);
  background: var(--surface-sunken); color: var(--text-muted);
  border: 1px solid var(--border);
  /* 12px, e nao os 11px do `.selo-obra` da Horizonte: o piso da
     fundacao (05/09/2026) e 12px para rotulo curto. Os 11px de la
     nasceram de uma medida de menu — o subitem tem 184px e o rotulo
     come 140 — e essa restricao e daquela tela, nao da lingua. */
  /* A CAIXA ALTA SAIU EM 06/09/2026 (secao 2 do documento de regras
     visuais: "Nada em caixa alta. Rotulos em caixa baixa normal.").
     O espacamento de letra ia junto: ele existia para abrir a caixa
     alta, e sobre caixa baixa ele so afasta as letras. */
  font-size: var(--fs-12); font-weight: var(--peso-medio);
  line-height: 17px; white-space: nowrap;
}

/* ============================================================
   7. TABELA / LISTA

   Grade, e nao <table>: as linhas do sistema tem celulas que quebram
   em duas alturas (nome em cima, subtitulo embaixo) e uma <table> nao
   alinha isso sem gambiarra. A semantica de tabela fica nos papeis
   ARIA, que o leitor de tela le igual.

   O cabecalho clicavel ordena. A seta e desenhada em CSS a partir do
   aria-sort, entao o estado visual e o estado anunciado sao a MESMA
   informacao — nao ha como um mudar sem o outro.
   ============================================================ */

.ds-tabela {
  background: var(--surface-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
}
/* O que e largo rola dentro de si — a pagina nunca rola para o lado. */
.ds-tabela-rolo { overflow-x: auto; }

.ds-tabela-cab, .ds-tabela-linha {
  display: grid; gap: var(--space-3);
  align-items: center;
  padding: var(--space-3) var(--space-4);
  min-width: max-content;
}
.ds-tabela-cab {
  background: var(--surface-sunken);
  border-bottom: 1px solid var(--divisoria);
  font-size: var(--fs-12); font-weight: var(--peso-medio);
  color: var(--text-secondary);
}
/* DIVISORIA, e nao borda estrutural (secao 2): o fio entre duas linhas
   de uma lista e mais leve que o fio que fecha o cartao em volta. */
.ds-tabela-linha { border-bottom: 1px solid var(--divisoria); font-size: var(--fs-13); }
.ds-tabela-linha:last-child { border-bottom: 0; }
.ds-tabela-linha:hover { background: var(--surface-sunken); }

/* ---- cabecalho que ordena ---- */
.ds-tabela-ordena {
  display: inline-flex; align-items: center; gap: var(--space-1);
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; letter-spacing: inherit; text-transform: inherit;
  color: inherit; cursor: pointer;
}
.ds-tabela-ordena:hover { color: var(--text-primary); }
.ds-tabela-ordena::after {
  content: "↕"; font-size: var(--fs-11); opacity: .45;
}
[aria-sort="ascending"]  .ds-tabela-ordena::after { content: "↑"; opacity: 1; color: var(--accent-fill); }
[aria-sort="descending"] .ds-tabela-ordena::after { content: "↓"; opacity: 1; color: var(--accent-fill); }

.ds-tabela-num { text-align: right; font-variant-numeric: tabular-nums; }

/* ============================================================
   8. MODAL

   <dialog> nativo. Duas armadilhas deste elemento, ja pagas em
   26/08 e 01/09 neste projeto, e por isso escritas aqui:

   a) `display: flex` incondicional deixa a janela VISIVEL o tempo
      todo — o estado fechado vive no display:none do navegador. So
      `dialog[open]`.

   b) `max-height` em `vh` no celular e a viewport GRANDE: ela ignora
      a barra do navegador e nao encolhe quando o teclado sobe. `dvh`
      e a altura visivel agora.
   ============================================================ */

.ds-modal {
  border: 0; padding: 0;
  width: min(94vw, 460px);
  max-height: min(86dvh, 680px);
  background: var(--surface-card); color: var(--text-primary);
  border-radius: var(--radius-card);
  box-shadow: var(--elevacao-3);
  font: inherit;
}
.ds-modal[open] { display: flex; flex-direction: column; animation: ds-entrar var(--dur-base) var(--ease); }
@keyframes ds-entrar { from { opacity: 0; transform: translateY(8px) scale(.99); } to { opacity: 1; transform: none; } }
.ds-modal::backdrop { background: var(--veu); }

.ds-modal-topo {
  flex: none; display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-5) var(--space-5) var(--space-3);
  border-bottom: 1px solid var(--border);
}
.ds-modal-meio {
  /* `flex: 1` tem base ZERO, e com base zero o miolo nao contribui com
     altura nenhuma: no Safari a faixa vira uma fresta de uma linha.
     `1 1 auto` deixa a base ser o proprio conteudo. */
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  padding: var(--space-4) var(--space-5);
  scrollbar-gutter: stable;
}
.ds-modal-pe {
  flex: none; display: flex; gap: var(--space-2); justify-content: flex-end;
  padding: var(--space-3) var(--space-5) var(--space-5);
  border-top: 1px solid var(--border);
}
.ds-modal-x {
  flex: none; width: 32px; height: 32px;
  border: 0; border-radius: var(--radius-control);
  background: var(--surface-sunken); color: var(--text-secondary);
  font-size: var(--fs-18); line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.ds-modal-x:hover { background: var(--accent-bg); color: var(--accent-text); }

@media (max-width: 640px) {
  /* No celular a janela vem de baixo e encosta nas bordas: uma caixinha
     centralizada de 460px num telefone e uma janela flutuando no meio
     do nada, com o polegar longe do botao. */
  .ds-modal {
    width: 100%; max-width: none; margin: auto 0 0;
    border-radius: var(--radius-card) var(--radius-card) 0 0;
    max-height: 90dvh;
  }
  .ds-modal-pe { padding-bottom: calc(var(--space-5) + env(safe-area-inset-bottom)); }
  .ds-modal-pe .ds-bt { flex: 1; }
}

/* ============================================================
   9. TOAST

   Vem do `.toque` da Horizonte. Duas mudancas:

   a) A regiao e `aria-live="polite"`, e ela existe VAZIA no HTML
      desde o inicio. Um live region criado junto com a mensagem nao
      e anunciado — o leitor de tela precisa ja estar observando o
      elemento quando o texto entra. E o erro classico deste padrao.

   b) Ganhou os papeis semanticos. O toast da Horizonte era so escuro:
      "salvo" e "falhou ao salvar" chegavam com a mesma cara.
   ============================================================ */

.ds-toasts {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(var(--space-6) + env(safe-area-inset-bottom));
  z-index: 90;
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
  width: max-content; max-width: min(92vw, 460px);
  pointer-events: none;
}
.ds-toast {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-pill);
  background: var(--text-primary); color: var(--surface-card);
  border: 1px solid transparent;
  font-size: var(--fs-13); font-weight: var(--peso-medio);
  box-shadow: var(--elevacao-3);
  animation: ds-surgir var(--dur-base) var(--ease);
  pointer-events: auto;
}
@keyframes ds-surgir { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.ds-toast--ok      { background: var(--ok-bg);     color: var(--ok-text);     border-color: var(--ok-fill); }
.ds-toast--atencao { background: var(--warn-bg);   color: var(--warn-text);   border-color: var(--warn-fill); }
.ds-toast--erro    { background: var(--danger-bg); color: var(--danger-text); border-color: var(--danger-fill); }

/* ============================================================
   10. BLOCO DE ALERTA

   O `.aviso` da Horizonte, com os quatro papeis em vez de so o ambar.
   Fica no fluxo da pagina — nao e o toast, que passa. Alerta e o que
   continua verdadeiro depois que a pessoa parar de olhar.
   ============================================================ */

.ds-alerta {
  display: flex; gap: var(--space-3);
  padding: var(--space-4);
  border: 1px solid var(--warn-fill); border-radius: var(--radius-card);
  background: var(--warn-bg); color: var(--warn-text);
  font-size: var(--fs-13); line-height: 1.5;
}
.ds-alerta--info    { background: var(--accent-bg); color: var(--accent-text); border-color: var(--accent-fill); }
.ds-alerta--ok      { background: var(--ok-bg);     color: var(--ok-text);     border-color: var(--ok-fill); }
.ds-alerta--critico { background: var(--danger-bg); color: var(--danger-text); border-color: var(--danger-fill); }
.ds-alerta b { display: block; font-weight: var(--peso-medio); margin-bottom: 2px; }
.ds-alerta-corpo { flex: 1; min-width: 0; }
.ds-alerta .ds-bt { margin-top: var(--space-3); }

/* ============================================================
   11. VAZIO

   Frase e acao. Vazio sem acao e um beco: a pessoa chegou onde nao ha
   nada e a tela nao diz o que fazer com isso.

   Nunca diz so "Nenhum resultado". Diz o que estaria aqui e como
   colocar o primeiro.
   ============================================================ */

.ds-vazio {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
  text-align: center;
  padding: var(--space-8) var(--space-6);
  background: var(--surface-page);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-card);
}
.ds-vazio-icone { width: 36px; height: 36px; color: var(--text-muted); stroke-width: 1.4; }
.ds-vazio b { font-size: var(--fs-13); font-weight: var(--peso-medio); }
.ds-vazio p { margin: 0; max-width: 44ch; color: var(--text-secondary); font-size: var(--fs-13); }
.ds-vazio .ds-bt { margin-top: var(--space-2); }

/* ============================================================
   12. ESQUELETO — o carregando que nao e botao
   ============================================================ */

.ds-esqueleto {
  background: var(--surface-sunken);
  border-radius: var(--radius-control);
  animation: ds-pulsar 1.5s ease-in-out infinite;
}
@keyframes ds-pulsar { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }

/* ============================================================
   13. AUXILIARES
   ============================================================ */

/* Visivel so para o leitor de tela. */
.ds-so-leitor {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.ds-pilha { display: flex; flex-direction: column; gap: var(--space-3); }
.ds-linha { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
