/*
 * 10_componentes.css — ONDA D
 * ═══════════════════════════════════════════════════════════════════════════
 * BEM de verdade: `bloco`, `bloco__elemento`, `bloco--modificador`. Nada de
 * utilitário solto — quem precisa de espaçamento usa o bloco certo, e se o
 * bloco certo não existe ele nasce aqui, com nome de domínio em português.
 *
 * Mobile-first sem exceção: a regra base é a do celular, e o desktop entra
 * sempre em `@media (min-width:)`. O aluno deste produto é motorista com o
 * telefone na mão, muitas vezes de luva — por isso NENHUM controle tem altura
 * menor que `--alvo` (48px).
 *
 * Catálogo vivo em /estilo (fora de produção). Se um estado não está lá, ele
 * não existe.
 */

/* ═══ ESTRUTURA ═══════════════════════════════════════════════════════════ */

.secao { margin-bottom: var(--esp-8) }
.secao__titulo {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--esp-3);
  margin-bottom: var(--esp-4);
}
.secao__titulo h2 { margin: 0 }

.pilha { display: flex; flex-direction: column; gap: var(--esp-4) }
.pilha--apertada { gap: var(--esp-2) }
.pilha--solta { gap: var(--esp-6) }

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

.grade {
  display: grid;
  gap: var(--esp-4);
  grid-template-columns: 1fr;
}
@media (min-width: 40rem) { .grade--2 { grid-template-columns: repeat(2, 1fr) } }
@media (min-width: 48rem) { .grade--3 { grid-template-columns: repeat(3, 1fr) } }
@media (min-width: 64rem) { .grade--4 { grid-template-columns: repeat(4, 1fr) } }

.leitura { max-width: var(--largura-leitura) }

/* Superfície temática: pinta fundo e texto com os tokens do tema VIGENTE no
   elemento. É o que um contêiner com `data-theme="light"` precisa para não
   ficar com texto claro sobre fundo claro. */
.superficie { background: var(--fundo); color: var(--texto) }

/* Ponte com o layout da Onda 0: `layouts/application.html.erb` escreve
   `<p class="aviso aviso--notice">` direto dentro de `.avisos`, que é um
   contêiner fixo com `pointer-events: none`. Até o integrador trocar aquele
   bloco por `render "shared/flash"`, estes parágrafos ganham aparência de
   toast e voltam a receber clique. */
.avisos > .aviso {
  pointer-events: auto;
  background: var(--fundo-elevado);
  box-shadow: var(--sombra-3);
  font-size: var(--texto-sm);
}
.avisos > .aviso--notice, .avisos > .aviso--success { border-color: var(--cor-aprovado) }
.avisos > .aviso--alert, .avisos > .aviso--error { border-color: var(--cor-reprovado) }
.avisos > .aviso--warning { border-color: var(--cor-descanso) }

/* ═══ BOTÃO ═══════════════════════════════════════════════════════════════
   Um bloco, cinco variantes, cinco estados. Altura mínima = --alvo.         */

.botao {
  --botao-fundo: var(--fundo-elevado);
  --botao-texto: var(--texto);
  --botao-borda: var(--borda-forte);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--esp-2);
  min-height: var(--alvo);
  padding: var(--esp-3) var(--esp-5);
  border: 1px solid var(--botao-borda);
  border-radius: var(--raio-2);
  background: var(--botao-fundo);
  color: var(--botao-texto);
  font-size: var(--texto-md);
  font-weight: var(--peso-medio);
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  position: relative;
  transition: background-color var(--transicao-rapida),
              border-color var(--transicao-rapida),
              transform var(--transicao-rapida),
              box-shadow var(--transicao-rapida);
}
/* HOVER NÃO MEXE NO PREENCHIMENTO DE BOTÃO CHEIO.
   Medido com `Tenant::Paleta.contraste`, que é a conta da §15.2:

     .botao--primario   branco sobre #4462f2 (marca Elevô)  4,91:1  ✅
                        o mesmo clareado 15% com branco     3,76:1  ❌
     .botao--perigo     branco sobre #cf3a32                4,88:1  ✅
                        o mesmo clareado 12% com o texto    4,19:1  ❌

   Não é caso de escolher outra porcentagem: um preenchimento derivado para
   parar EXATAMENTE em 4,5:1 (é o que `Tenant::Paleta` faz, de propósito, para
   não desbotar a cor do cliente) reprova ao primeiro passo em qualquer
   direção que o CSS escolha — e a direção certa depende de `--cor-marca-
   contraste` ser claro ou escuro, que é decisão do HEX DO TENANT e não dá
   para ler daqui. Nas seis paletas prontas o defeito pegava justamente as
   duas de contraste branco: o índigo da Elevô e a da Upper, as da demo.

   Então o feedback de hover em botão cheio é ANEL (a borda assume a cor do
   próprio rótulo) e ELEVAÇÃO — nenhum dos dois tem razão de contraste a
   perder. Preenchimento TRANSPARENTE pode ganhar fundo: lá o rótulo é
   `--texto` sobre um token neutro já medido, não sobre a cor do cliente. */
.botao:hover { border-color: var(--botao-texto); box-shadow: var(--sombra-2) }
.botao:active { transform: translateY(1px) }

.botao--primario {
  --botao-fundo: var(--cor-marca-forte);
  --botao-texto: var(--cor-marca-contraste);
  --botao-borda: transparent;
  box-shadow: var(--sombra-marca);
}
.botao--primario:hover { box-shadow: var(--sombra-marca), var(--sombra-2) }

.botao--secundario {
  --botao-fundo: transparent;
  --botao-texto: var(--texto);
  --botao-borda: var(--borda-forte);
}
.botao--secundario:hover { background: var(--fundo-elevado) }

.botao--fantasma {
  --botao-fundo: transparent;
  --botao-texto: var(--texto-suave);
  --botao-borda: transparent;
}
.botao--fantasma:hover { --botao-texto: var(--texto); background: var(--fundo-fundo) }

.botao--perigo {
  --botao-fundo: var(--cor-reprovado-forte);
  --botao-texto: var(--cor-reprovado-contraste);
  --botao-borda: transparent;
}

.botao--largo { width: 100%; }
/* O "pequeno" existe só para linha densa de tabela do admin, no desktop —
   NUNCA para a tela do aluno. 40 px continua acima do piso da WCAG 2.5.8 e
   bem acima do que um dedo erra numa lista com espaçamento; abaixo disso o
   gestor com o celular na mão passa a errar o alvo. */
.botao--pequeno { min-height: 2.5rem; padding: var(--esp-2) var(--esp-3); font-size: var(--texto-sm) }
.botao--grande { min-height: var(--alvo-grande); padding: var(--esp-4) var(--esp-8); font-size: var(--texto-lg) }

/* Desabilitado: legível, mas obviamente inerte. Nunca `opacity: .4` sozinho —
   no escuro isso derruba o contraste do rótulo abaixo de 3:1. */
.botao:disabled,
.botao[aria-disabled="true"],
.botao--desabilitado {
  --botao-fundo: var(--fundo-fundo);
  --botao-texto: var(--texto-tenue);
  --botao-borda: var(--borda);
  box-shadow: none;
  cursor: not-allowed;
  pointer-events: none;
}

/* Carregando: o rótulo FICA (sumir com ele muda a largura e o usuário perde a
   referência), some só a opacidade, e o giro entra por cima. */
.botao--carregando { pointer-events: none; cursor: progress }
.botao--carregando .botao__rotulo { opacity: 0.35 }
.botao--carregando::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 1.15rem;
  height: 1.15rem;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: girar 720ms linear infinite;
}
@keyframes girar { to { transform: rotate(360deg) } }

.botao__icone { width: 1.15rem; height: 1.15rem; flex: none }

.botoes { display: flex; flex-direction: column; gap: var(--esp-3) }
@media (min-width: 30rem) {
  .botoes { flex-direction: row; flex-wrap: wrap; align-items: center }
  .botoes .botao--largo { width: auto }
}

/* ═══ CAMPO ═══════════════════════════════════════════════════════════════ */

.campo { display: block; margin-bottom: var(--esp-5) }
.campo__rotulo {
  display: block;
  margin-bottom: var(--esp-2);
  font-size: var(--texto-sm);
  font-weight: var(--peso-medio);
  color: var(--texto-suave);
}
.campo__obrigatorio { color: var(--cor-reprovado); margin-left: 0.15em }

.campo__controle {
  display: block;
  width: 100%;
  min-height: var(--alvo);
  padding: var(--esp-3) var(--esp-4);
  background: var(--fundo-fundo);
  border: 1px solid var(--borda-forte);
  border-radius: var(--raio-2);
  color: var(--texto);
  transition: border-color var(--transicao-rapida), box-shadow var(--transicao-rapida);
}
.campo__controle:hover:not(:disabled) { border-color: var(--cor-marca) }
.campo__controle:focus-visible {
  outline: none;
  border-color: var(--cor-marca);
  box-shadow: 0 0 0 3px var(--cor-marca-tenue);
}
.campo__controle:disabled {
  background: var(--fundo-cartao);
  color: var(--texto-tenue);
  cursor: not-allowed;
}
select.campo__controle { appearance: none; padding-right: var(--esp-10) }

.campo__ajuda {
  margin: var(--esp-2) 0 0;
  font-size: var(--texto-sm);
  color: var(--texto-tenue);
}

/* O erro é TEXTO com ícone, nunca só a borda vermelha: 8% dos homens têm
   alguma deficiência de visão de cor, e este produto é de motoristas. */
.campo__erro {
  display: flex;
  align-items: flex-start;
  gap: var(--esp-2);
  margin: var(--esp-2) 0 0;
  font-size: var(--texto-sm);
  font-weight: var(--peso-medio);
  color: var(--cor-reprovado);
}
.campo__erro::before { content: "▲"; font-size: 0.8em; line-height: 1.6 }

.campo--com-erro .campo__controle { border-color: var(--cor-reprovado) }
.campo--com-erro .campo__controle:focus-visible {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--cor-reprovado) 30%, transparent);
}

.campo--opcao {
  display: flex;
  align-items: flex-start;
  gap: var(--esp-3);
  padding: var(--esp-3);
  min-height: var(--alvo);
  border: 1px solid var(--borda);
  border-radius: var(--raio-2);
  cursor: pointer;
  margin-bottom: var(--esp-2);
  transition: border-color var(--transicao-rapida), background-color var(--transicao-rapida);
}
.campo--opcao:hover { border-color: var(--borda-forte); background: var(--fundo-fundo) }
.campo--opcao:has(:checked) {
  border-color: var(--cor-marca);
  background: var(--cor-marca-suave);
}
.campo--opcao > span { font-size: var(--texto-md) }

.campo__grupo { display: grid; gap: var(--esp-3) }
@media (min-width: 40rem) {
  .campo__grupo--2 { grid-template-columns: 1fr 1fr }
  .campo__grupo--cep { grid-template-columns: 10rem 1fr }
}

/* ═══ CARTÃO ══════════════════════════════════════════════════════════════ */

.cartao {
  background: var(--fundo-cartao);
  border: 1px solid var(--borda);
  border-radius: var(--raio-3);
  padding: var(--esp-5);
  box-shadow: var(--sombra-1);
}
.cartao--elevado { background: var(--fundo-elevado); box-shadow: var(--sombra-2) }
.cartao--marca { border-color: var(--cor-marca-tenue); background: var(--cor-marca-suave) }
.cartao--sem-padding { padding: 0; overflow: hidden }

.cartao__cabecalho {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--esp-3);
  margin-bottom: var(--esp-4);
}
.cartao__titulo { margin: 0; font-size: var(--texto-lg) }
.cartao__meta { margin: var(--esp-1) 0 0; font-size: var(--texto-sm); color: var(--texto-tenue) }
.cartao__corpo > :last-child { margin-bottom: 0 }
.cartao__rodape {
  margin-top: var(--esp-5);
  padding-top: var(--esp-4);
  border-top: 1px solid var(--borda);
}

/* Número grande do painel: 6 números no /admin, extrato no /painel. */
.numero { display: flex; flex-direction: column; gap: var(--esp-1) }
.numero__valor {
  font-size: var(--texto-3xl);
  font-weight: var(--peso-forte);
  line-height: 1;
  letter-spacing: var(--espacamento-titulo);
  font-variant-numeric: tabular-nums;
}
.numero__rotulo {
  font-size: var(--texto-sm);
  color: var(--texto-tenue);
}
.numero--marca .numero__valor { color: var(--cor-marca) }
.numero--aprovado .numero__valor { color: var(--cor-aprovado) }
.numero--descanso .numero__valor { color: var(--cor-descanso) }
.numero--pontos .numero__valor { color: var(--cor-pontos) }

/* ═══ CARTÃO DE CURSO ═════════════════════════════════════════════════════
   O bloco mais visto do produto. No celular é uma faixa alta e tocável
   inteira; no desktop entra na grade.                                       */

.curso {
  display: flex;
  flex-direction: column;
  background: var(--fundo-cartao);
  border: 1px solid var(--borda);
  border-radius: var(--raio-3);
  overflow: hidden;
  transition: border-color var(--transicao), transform var(--transicao), box-shadow var(--transicao);
}
.curso:hover { border-color: var(--cor-marca-tenue); box-shadow: var(--sombra-2) }
.curso:focus-within { border-color: var(--cor-marca) }

.curso__capa {
  position: relative;
  aspect-ratio: 16 / 9;
  background: linear-gradient(135deg, var(--cor-marca-suave), var(--fundo-fundo));
  display: grid;
  place-items: center;
  border-bottom: 1px solid var(--borda);
}
.curso__capa img { width: 100%; height: 100%; object-fit: cover }
.curso__capa-vazia { color: var(--cor-marca); opacity: 0.5; width: 3rem; height: 3rem }

.curso__selo-canto {
  position: absolute;
  top: var(--esp-3);
  left: var(--esp-3);
}

.curso__corpo { padding: var(--esp-4); display: flex; flex-direction: column; gap: var(--esp-3); flex: 1 }
.curso__titulo { margin: 0; font-size: var(--texto-lg); line-height: 1.25 }
.curso__titulo a { color: inherit; text-decoration: none }
/* O alvo de toque é o cartão inteiro, não as três palavras do título. */
.curso__titulo a::after { content: ""; position: absolute; inset: 0 }
.curso { position: relative }

.curso__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--esp-1) var(--esp-3);
  font-size: var(--texto-sm);
  color: var(--texto-tenue);
  font-variant-numeric: tabular-nums;
}
.curso__meta > * { display: inline-flex; align-items: center; gap: var(--esp-1) }
.curso__rodape { margin-top: auto; padding-top: var(--esp-1) }

/* ═══ LISTA ═══════════════════════════════════════════════════════════════ */

.lista { list-style: none; margin: 0; padding: 0; border-radius: var(--raio-3); overflow: hidden; border: 1px solid var(--borda) }
.lista__item {
  display: flex;
  align-items: center;
  gap: var(--esp-3);
  padding: var(--esp-4);
  min-height: var(--alvo-grande);
  background: var(--fundo-cartao);
  border-bottom: 1px solid var(--borda);
  position: relative;
}
.lista__item:last-child { border-bottom: 0 }
.lista__item--clicavel:hover { background: var(--fundo-elevado) }
.lista__item--atual { background: var(--cor-marca-suave) }

.lista__marca {
  flex: none;
  width: 2.5rem; height: 2.5rem;
  display: grid; place-items: center;
  border-radius: var(--raio-2);
  background: var(--fundo-fundo);
  color: var(--texto-tenue);
  font-weight: var(--peso-forte);
  font-variant-numeric: tabular-nums;
}
.lista__marca--aprovado { background: var(--cor-aprovado-suave); color: var(--cor-aprovado) }
.lista__marca--descanso { background: var(--cor-descanso-suave); color: var(--cor-descanso) }
.lista__marca--marca { background: var(--cor-marca-suave); color: var(--cor-marca) }

.lista__texto { flex: 1; min-width: 0 }
.lista__titulo {
  margin: 0;
  font-size: var(--texto-md);
  font-weight: var(--peso-medio);
  overflow: hidden;
  text-overflow: ellipsis;
}
.lista__titulo a { color: inherit; text-decoration: none }
.lista__titulo a::after { content: ""; position: absolute; inset: 0 }
.lista__meta { margin: var(--esp-1) 0 0; font-size: var(--texto-sm); color: var(--texto-tenue) }
.lista__fim { flex: none; display: flex; align-items: center; gap: var(--esp-2); position: relative; z-index: 1 }

/* ═══ TABELA ══════════════════════════════════════════════════════════════
   No celular cada LINHA vira um cartão: o `td` mostra o rótulo da coluna a
   partir de `data-rotulo`. É por isso que toda `td` do produto carrega esse
   atributo — sem ele a versão móvel fica uma coluna de números sem nome.      */

 /* `min-width: 0` não é enfeite: dentro de um contêiner flex o item nasce com
   `min-width: auto` e se recusa a encolher abaixo do conteúdo — a tabela
   "rolável" passa a empurrar a PÁGINA para o lado em vez de rolar dentro de
   si, e a barra horizontal aparece no celular. Foi exatamente o que o
   Playwright pegou no catálogo. */
.tabela-rolagem { overflow-x: auto; -webkit-overflow-scrolling: touch; min-width: 0; max-width: 100% }

.tabela { width: 100%; border-collapse: collapse; font-size: var(--texto-sm) }
.tabela caption { text-align: left; color: var(--texto-tenue); padding-bottom: var(--esp-3) }
.tabela th, .tabela td { text-align: left; padding: var(--esp-3) var(--esp-4) }
.tabela thead th {
  font-size: var(--texto-xs);
  font-weight: var(--peso-forte);
  letter-spacing: var(--espacamento-etiqueta);
  color: var(--texto-tenue);
  border-bottom: 1px solid var(--borda-forte);
  white-space: nowrap;
}
.tabela tbody tr { border-bottom: 1px solid var(--borda) }
.tabela tbody tr:hover { background: var(--fundo-elevado) }
.tabela td { vertical-align: top }
.tabela .numerico { text-align: right; font-variant-numeric: tabular-nums }

@media (max-width: 47.999rem) {
  .tabela--cartao thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%) }
  .tabela--cartao tbody tr {
    display: block;
    background: var(--fundo-cartao);
    border: 1px solid var(--borda);
    border-radius: var(--raio-3);
    margin-bottom: var(--esp-3);
    padding: var(--esp-2) var(--esp-3);
  }
  .tabela--cartao td {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--esp-4);
    padding: var(--esp-2) 0;
    border-bottom: 1px solid var(--borda-tenue);
  }
  .tabela--cartao tr td:last-child { border-bottom: 0 }
  .tabela--cartao td::before {
    content: attr(data-rotulo);
    flex: none;
    font-size: var(--texto-xs);
    letter-spacing: var(--espacamento-etiqueta);
    color: var(--texto-tenue);
    font-weight: var(--peso-medio);
  }
  .tabela--cartao .numerico { text-align: right }
}

/* ═══ MEDIDOR ═════════════════════════════════════════════════════════════
   `<progress>` de verdade. Não é preciosismo: a CSP do projeto põe nonce em
   `style-src`, o que mata `style="width: 62%"` em produção — largura dinâmica
   sem inline style só existe com o elemento nativo. De quebra, o leitor de
   tela anuncia o valor sozinho.                                              */

.medidor { display: flex; flex-direction: column; gap: var(--esp-2) }
.medidor__topo {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--esp-3);
  font-size: var(--texto-sm);
}
.medidor__rotulo { color: var(--texto-suave); font-weight: var(--peso-medio) }
.medidor__valor { color: var(--texto); font-weight: var(--peso-forte); font-variant-numeric: tabular-nums }

.medidor__barra {
  --medidor-cor: var(--cor-marca-forte);
  width: 100%;
  height: 0.625rem;
  border-radius: var(--raio-redondo);
  background: var(--fundo-fundo);
  overflow: hidden;
}
.medidor__barra::-webkit-progress-bar { background: var(--fundo-fundo); border-radius: var(--raio-redondo) }
.medidor__barra::-webkit-progress-value {
  background: var(--medidor-cor);
  border-radius: var(--raio-redondo);
  transition: inline-size var(--transicao-lenta);
}
.medidor__barra::-moz-progress-bar { background: var(--medidor-cor); border-radius: var(--raio-redondo) }

.medidor--grosso .medidor__barra { height: 1rem }
.medidor--fino .medidor__barra { height: 0.3125rem }
.medidor--aprovado .medidor__barra { --medidor-cor: var(--cor-aprovado-forte) }
.medidor--descanso .medidor__barra { --medidor-cor: var(--cor-descanso-forte) }
.medidor--reprovado .medidor__barra { --medidor-cor: var(--cor-reprovado-forte) }
.medidor--ritmo .medidor__barra { --medidor-cor: var(--cor-ritmo-forte) }
.medidor--pontos .medidor__barra { --medidor-cor: var(--cor-pontos-forte) }
.medidor__ajuda { font-size: var(--texto-sm); color: var(--texto-tenue) }

/* ═══ SELO ════════════════════════════════════════════════════════════════ */

.selo {
  display: inline-flex;
  align-items: center;
  gap: var(--esp-2);
  padding: 0.25rem var(--esp-3);
  border-radius: var(--raio-redondo);
  background: var(--fundo-fundo);
  color: var(--texto-suave);
  border: 1px solid transparent;
  font-size: var(--texto-xs);
  font-weight: var(--peso-forte);
  letter-spacing: var(--espacamento-etiqueta);
  line-height: 1.6;
  white-space: nowrap;
  vertical-align: middle;
}
.selo__ponto { width: 0.5rem; height: 0.5rem; border-radius: 50%; background: currentColor; flex: none }

/* SEIS cores e uma lista de APELIDOS pendurada em cada uma. Os apelidos são os
   estados do domínio (`pronto`, `falhou`, `publicado`, …) para quem escreve o
   HTML direto; `UiHelper::SELOS` faz o mesmíssimo mapa do lado do Ruby, e é
   dele que `selo(…, estado:)` sai. O que NÃO pode existir é a lista dos
   apelidos duas vezes com a decisão de cor repetida junto: era assim que
   `.selo--cancelado` vinha declarado em separado enquanto o helper mandava
   `cancelado` para `--reprovado`. Aqui o apelido não carrega decisão nenhuma;
   ele só se encosta na semântica que já decidiu. */
.selo--aprovado,
.selo--pronto     { background: var(--cor-aprovado-suave);   color: var(--cor-aprovado) }
.selo--reprovado,
.selo--falhou,
.selo--cancelado  { background: var(--cor-reprovado-suave);  color: var(--cor-reprovado) }
.selo--descanso   { background: var(--cor-descanso-suave);   color: var(--cor-descanso) }
.selo--aguardando,
.selo--normalizando,
.selo--recebendo,
.selo--rascunho,
.selo--arquivado  { background: var(--cor-aguardando-suave); color: var(--cor-aguardando) }
.selo--ritmo      { background: var(--cor-ritmo-suave);      color: var(--cor-ritmo) }
.selo--pontos     { background: var(--cor-pontos-suave);     color: var(--cor-pontos) }
.selo--marca,
.selo--publicado  { background: var(--cor-marca-suave);      color: var(--cor-marca) }
.selo--contorno   { background: transparent; border-color: var(--borda-forte); color: var(--texto-suave) }

.selo--grande { font-size: var(--texto-sm); padding: var(--esp-2) var(--esp-4) }

/* Selo derivado da gamificação (§10.4): tem ícone e legenda. */
.conquista {
  display: flex;
  align-items: center;
  gap: var(--esp-3);
  padding: var(--esp-3);
  border: 1px solid var(--borda);
  border-radius: var(--raio-2);
  background: var(--fundo-cartao);
}
.conquista--bloqueada { opacity: 0.55 }
.conquista__icone {
  flex: none;
  width: 2.75rem; height: 2.75rem;
  display: grid; place-items: center;
  border-radius: var(--raio-2);
  background: var(--cor-pontos-suave);
  color: var(--cor-pontos);
}
.conquista--bloqueada .conquista__icone { background: var(--fundo-fundo); color: var(--texto-tenue) }
.conquista__nome { font-weight: var(--peso-medio); font-size: var(--texto-sm) }
.conquista__desc { font-size: var(--texto-xs); color: var(--texto-tenue) }

/* ═══ AVISO ═══════════════════════════════════════════════════════════════
   Bloco de mensagem dentro da página (não confundir com o toast de flash).
   `--cor-descanso` é âmbar e o bloqueio NUNCA é vermelho (§15.3).            */

.aviso {
  display: flex;
  gap: var(--esp-3);
  padding: var(--esp-4);
  border: 1px solid var(--borda-forte);
  border-left-width: 4px;
  border-radius: var(--raio-2);
  background: var(--fundo-cartao);
  color: var(--texto);
}
.aviso__icone { flex: none; width: 1.25rem; height: 1.25rem; margin-top: 0.15rem }
.aviso__corpo > :last-child { margin-bottom: 0 }
.aviso__titulo { margin: 0 0 var(--esp-1); font-size: var(--texto-md); font-weight: var(--peso-forte) }
.aviso__texto { margin: 0; font-size: var(--texto-sm); color: var(--texto-suave) }
.aviso__acoes { margin-top: var(--esp-3) }

.aviso--sucesso  { border-color: var(--cor-aprovado);   background: var(--cor-aprovado-suave) }
.aviso--sucesso .aviso__icone { color: var(--cor-aprovado) }
.aviso--erro     { border-color: var(--cor-reprovado);  background: var(--cor-reprovado-suave) }
.aviso--erro .aviso__icone { color: var(--cor-reprovado) }
.aviso--atencao  { border-color: var(--cor-descanso);   background: var(--cor-descanso-suave) }
.aviso--atencao .aviso__icone { color: var(--cor-descanso) }
.aviso--info     { border-color: var(--cor-marca);      background: var(--cor-marca-suave) }
.aviso--info .aviso__icone { color: var(--cor-marca) }

/* ═══ TOAST DE FLASH ══════════════════════════════════════════════════════
   Mora no alto no celular (o polegar está embaixo e taparia a mensagem) e
   some sozinho por `flash_controller.js`. Em `prefers-reduced-motion` a
   entrada é instantânea — a regra de 02_base cuida disso.                    */

.avisos {
  position: fixed;
  z-index: var(--z-aviso);
  top: calc(env(safe-area-inset-top) + var(--esp-3));
  left: var(--esp-3);
  right: var(--esp-3);
  display: flex;
  flex-direction: column;
  gap: var(--esp-2);
  pointer-events: none;
}
.avisos:empty { display: none }
@media (min-width: 40rem) {
  .avisos { left: auto; right: var(--esp-6); top: var(--esp-6); width: min(26rem, calc(100vw - 3rem)) }
}

.flash {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: var(--esp-3);
  padding: var(--esp-4);
  border-radius: var(--raio-2);
  border: 1px solid var(--borda-forte);
  background: var(--fundo-elevado);
  color: var(--texto);
  box-shadow: var(--sombra-3);
  animation: flash-entra var(--transicao-lenta) both;
}
@keyframes flash-entra {
  from { opacity: 0; transform: translateY(-0.75rem) }
  to   { opacity: 1; transform: none }
}
.flash--saindo { animation: flash-sai var(--transicao) both }
@keyframes flash-sai {
  to { opacity: 0; transform: translateY(-0.5rem) }
}
.flash__icone { flex: none; width: 1.25rem; height: 1.25rem; margin-top: 0.15rem }
.flash__texto { flex: 1; margin: 0; font-size: var(--texto-sm) }
.flash__fechar {
  flex: none;
  width: 2rem; height: 2rem;
  display: grid; place-items: center;
  border-radius: var(--raio-1);
  color: var(--texto-tenue);
}
.flash__fechar:hover { color: var(--texto); background: var(--fundo-fundo) }

.flash--notice, .flash--sucesso { border-color: var(--cor-aprovado) }
.flash--notice .flash__icone, .flash--sucesso .flash__icone { color: var(--cor-aprovado) }
.flash--alert, .flash--erro { border-color: var(--cor-reprovado) }
.flash--alert .flash__icone, .flash--erro .flash__icone { color: var(--cor-reprovado) }
.flash--atencao { border-color: var(--cor-descanso) }
.flash--atencao .flash__icone { color: var(--cor-descanso) }
.flash--info { border-color: var(--cor-marca) }
.flash--info .flash__icone { color: var(--cor-marca) }

/* ═══ MODAL ═══════════════════════════════════════════════════════════════
   `<dialog>` nativo: foco preso, Esc fecha, sem JS de biblioteca. No celular
   sobe do rodapé (folha), no desktop é caixa centrada.                       */

.modal {
  padding: 0;
  border: 0;
  background: transparent;
  max-width: 100%;
  max-height: 100%;
  color: var(--texto);
}
.modal::backdrop { background: rgb(3 5 9 / 0.7); backdrop-filter: blur(2px) }

.modal__caixa {
  background: var(--fundo-elevado);
  border: 1px solid var(--borda-forte);
  box-shadow: var(--sombra-3);
  border-radius: var(--raio-4) var(--raio-4) 0 0;
  padding: var(--esp-5);
  padding-bottom: calc(var(--esp-5) + env(safe-area-inset-bottom));
  width: 100vw;
  max-height: 85dvh;
  overflow-y: auto;
}
.modal[open] { position: fixed; inset: auto 0 0; margin: 0; animation: folha-sobe var(--transicao-lenta) both }
@keyframes folha-sobe { from { transform: translateY(100%) } to { transform: none } }

.modal__puxador {
  width: 2.5rem; height: 0.25rem;
  border-radius: var(--raio-redondo);
  background: var(--borda-forte);
  margin: 0 auto var(--esp-4);
}
.modal__titulo { margin: 0 0 var(--esp-3); font-size: var(--texto-xl) }
.modal__corpo { font-size: var(--texto-md); color: var(--texto-suave) }
.modal__corpo > :last-child { margin-bottom: 0 }
.modal__acoes { margin-top: var(--esp-6) }

@media (min-width: 40rem) {
  .modal[open] { inset: 0; margin: auto; animation: caixa-entra var(--transicao) both }
  @keyframes caixa-entra { from { opacity: 0; transform: scale(0.97) } to { opacity: 1; transform: none } }
  .modal__caixa { width: min(34rem, calc(100vw - 3rem)); border-radius: var(--raio-4); padding-bottom: var(--esp-5) }
  .modal__puxador { display: none }
}

/* ═══ ABAS ════════════════════════════════════════════════════════════════
   Aguardando / Ativos / Bloqueados / Recusados (§12.4). No celular rola na
   horizontal com o indicador grudado embaixo.                                */

.abas {
  display: flex;
  gap: var(--esp-1);
  overflow-x: auto;
  min-width: 0;        /* ver a nota em .tabela-rolagem */
  max-width: 100%;
  scrollbar-width: none;
  border-bottom: 1px solid var(--borda);
  margin-bottom: var(--esp-5);
}
.abas::-webkit-scrollbar { display: none }
.abas__item {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: var(--esp-2);
  min-height: var(--alvo);
  padding: 0 var(--esp-4);
  color: var(--texto-tenue);
  font-size: var(--texto-sm);
  font-weight: var(--peso-medio);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
  transition: color var(--transicao-rapida), border-color var(--transicao-rapida);
}
.abas__item:hover { color: var(--texto) }
.abas__item[aria-current="page"], .abas__item--atual {
  color: var(--cor-marca);
  border-bottom-color: var(--cor-marca);
}
.abas__contagem {
  padding: 0 var(--esp-2);
  border-radius: var(--raio-redondo);
  background: var(--fundo-fundo);
  color: var(--texto-suave);
  font-size: var(--texto-xs);
  font-variant-numeric: tabular-nums;
}
.abas__item[aria-current="page"] .abas__contagem { background: var(--cor-marca-suave); color: var(--cor-marca) }

/* ═══ PAGINAÇÃO ═══════════════════════════════════════════════════════════ */

.paginacao {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--esp-2);
  margin-top: var(--esp-6);
}
.paginacao__resumo { width: 100%; text-align: center; font-size: var(--texto-sm); color: var(--texto-tenue); margin-bottom: var(--esp-2) }
.paginacao__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--alvo);
  min-height: var(--alvo);
  padding: 0 var(--esp-3);
  border: 1px solid var(--borda);
  border-radius: var(--raio-2);
  background: var(--fundo-cartao);
  color: var(--texto-suave);
  text-decoration: none;
  font-variant-numeric: tabular-nums;
}
.paginacao__item:hover { border-color: var(--borda-forte); color: var(--texto) }
.paginacao__item--atual {
  background: var(--cor-marca-forte);
  border-color: transparent;
  color: var(--cor-marca-contraste);
  font-weight: var(--peso-forte);
}
.paginacao__item--inerte { opacity: 0.45; pointer-events: none }
.paginacao__reticencias { color: var(--texto-tenue); padding: 0 var(--esp-1) }

/* ═══ ESTADO VAZIO ════════════════════════════════════════════════════════
   §15.3: "estados vazios são desenhados junto com as telas". Uma frase e UMA
   ação — duas ações num estado vazio é indecisão empurrada para o usuário.    */

.vazio {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--esp-3);
  padding: var(--esp-10) var(--esp-5);
  border: 1px dashed var(--borda-forte);
  border-radius: var(--raio-3);
  background: var(--fundo-cartao);
}
.vazio__icone {
  width: 3rem; height: 3rem;
  color: var(--texto-tenue);
  opacity: 0.7;
}
.vazio__titulo { margin: 0; font-size: var(--texto-lg) }
.vazio__texto { margin: 0; max-width: 30rem; color: var(--texto-tenue); font-size: var(--texto-sm) }
.vazio__acao { margin-top: var(--esp-2) }

/* ═══ BLOCO DE AÇÃO DA AULA (§12.3, seis estados) ═════════════════════════
   O ponto da tela onde o produto fala com o aluno. Fica grudado no rodapé do
   palco no celular, acima da navegação inferior.                             */

.acao {
  display: flex;
  flex-direction: column;
  gap: var(--esp-3);
  padding: var(--esp-5);
  border: 1px solid var(--borda-forte);
  border-radius: var(--raio-3);
  background: var(--fundo-cartao);
}
.acao--assistindo { border-color: var(--borda-forte) }
.acao--liberado { border-color: var(--cor-marca); background: var(--cor-marca-suave) }
.acao--precisa-reassistir { border-color: var(--cor-aguardando) }
.acao--perfil-incompleto { border-color: var(--cor-descanso) }
.acao--em-descanso { border-color: var(--cor-descanso); background: var(--cor-descanso-suave) }
.acao--aprovado { border-color: var(--cor-aprovado); background: var(--cor-aprovado-suave) }

.acao__estado {
  display: flex;
  align-items: center;
  gap: var(--esp-2);
  font-size: var(--texto-sm);
  font-weight: var(--peso-forte);
  letter-spacing: var(--espacamento-etiqueta);
}
.acao__titulo { margin: 0; font-size: var(--texto-lg) }
.acao__texto { margin: 0; font-size: var(--texto-sm); color: var(--texto-suave) }
.acao__contagem {
  font-variant-numeric: tabular-nums;
  font-weight: var(--peso-forte);
  font-size: var(--texto-2xl);
  letter-spacing: var(--espacamento-titulo);
}

/* ═══ DIVERSOS DE DOMÍNIO ═════════════════════════════════════════════════ */

/* CPF, protocolo, matrícula: monoespaçado para conferir dígito a dígito. */
.codigo {
  font-family: var(--fonte-mono);
  font-size: 0.95em;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.avatar {
  width: 2.5rem; height: 2.5rem;
  border-radius: 50%;
  background: var(--cor-marca-suave);
  color: var(--cor-marca);
  display: grid; place-items: center;
  font-weight: var(--peso-forte);
  font-size: var(--texto-sm);
  flex: none;
  overflow: hidden;
}
.avatar img { width: 100%; height: 100%; object-fit: cover }
.avatar--grande { width: 4rem; height: 4rem; font-size: var(--texto-lg) }

.chaves {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--esp-2) var(--esp-4);
  margin: 0;
  font-size: var(--texto-sm);
}
.chaves dt { color: var(--texto-tenue) }
.chaves dd { margin: 0; font-weight: var(--peso-medio) }

/* Faixa de "entrar como" (§12.5). Nunca discreta: é a única coisa que impede
   o super-admin de esquecer que está dentro da conta de um cliente. */
.faixa-impersonacao {
  position: sticky;
  top: 0;
  z-index: var(--z-faixa);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--esp-3);
  padding: var(--esp-2) var(--gutter);
  background: var(--cor-descanso-forte);
  color: var(--cor-descanso-contraste);
  font-size: var(--texto-sm);
  font-weight: var(--peso-medio);
}
.faixa-impersonacao a { color: inherit }
