/* A tela de entrada — desenho Mission Engage (v3, entrega de 2026-09).
 *
 * O QUE MUDOU E O QUE NÃO MUDOU. O visual é o da entrega: Hope Blue, Mona Sans
 * no título e Source Sans 3 no corpo, cartão de 12px, campos de 44px com borda
 * que se vê, um painel azul compacto com a marca. A MÁQUINA DE ESTADOS é a de
 * sempre — cada caminho de entrada, cada `hidden`, cada marca que o servidor
 * substitui. Ver docs/REGRAS-UI-LOGIN.md §12 para o que mudou e por quê.
 *
 * TUDO SAI DA ESCALA ABAIXO. Clara de propósito, como as telas do console atrás
 * dela. O que não é escolha e sim requisito: o campo em 16px (abaixo disso o iOS
 * dá zoom ao focar e a tela salta), 44px de altura de controle (48px no dedo), e
 * o anel de foco visível também em modo de alto contraste.
 *
 * ESTA FOLHA É AUTÔNOMA: a tela de entrada não carrega a folha do console,
 * porque é servida antes de haver sessão. Os tokens repetem os valores de lá, e
 * as duas folhas concordam — inclusive no vermelho de homologação.
 */

/* AS FONTES SÃO DAQUI, NÃO DE FORA. A política é `font-src 'self'`, e um
   recurso de fora nesta tela não só não carrega como conta a um terceiro que
   esta URL existe. Os arquivos são os mesmos do console (OFL 1.1, com a licença
   ao lado deles). */
@font-face {
  font-family: "Mona Sans";
  src: url("/static/fonts/MonaSansVF.woff2") format("woff2");
  font-weight: 200 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Source Sans 3";
  src: url("/static/fonts/SourceSans3VF-Upright.woff2") format("woff2");
  font-weight: 200 900; font-style: normal; font-display: swap;
}

:root {
  /* ESPAÇAMENTO — base 4, unidade de trabalho 8. */
  --sp-2: 4px;  --sp-3: 6px;  --sp-4: 8px;  --sp-5: 12px; --sp-6: 16px;
  --sp-7: 20px; --sp-8: 24px; --sp-9: 32px; --sp-10: 40px;

  /* TIPOGRAFIA — o piso é 14px; 11px só no rótulo em caixa alta do painel,
     que é decorativo e mudo para leitor de tela. */
  --fs-2xs: 11px; --lh-2xs: 14px;
  --fs-sm: 14px;  --lh-sm: 20px;
  --fs-md: 16px;  --lh-md: 24px;   /* PISO do campo. Não é estética. */
  /* O TÍTULO DA TELA DE ENTRADA tem nome próprio (CSS-18): `--fs-xl` valia 26px
     aqui e 20px em toda tela do console, e quem lia o nome não sabia qual dos
     dois estava lendo. Esta folha não carrega app.css, então declara o que usa;
     o que é SÓ dela não empresta o nome do degrau que app.css já usa. */
  --fs-login-title: 26px;  --lh-login-title: 30px;
  /* O CÓDIGO DE VERIFICAÇÃO precisa de um degrau próprio: maior que o corpo
     para ler letra por letra, menor que o título para caber na chave de
     reserva (onze caracteres). Nomeado, e não um número solto no meio da
     regra — a mesma razão de todo o resto desta escala. */
  --fs-code: 20px;

  --r-ctrl: 8px;
  --r-card: 12px;
  --r-tag: 6px;

  --h-control: 44px;

  /* ---- MOVIMENTO ----
     O teto desta tela é 300 ms (decisão D6). Duas curvas: `--glide` para o que
     vai de um ponto a outro e para, `--shake` para o que é recusado. Nada se
     move sozinho e nada repete — o único laço é o giro do botão, e ele só
     existe enquanto uma chamada de rede corre (no máximo 10 s). */
  --glide: cubic-bezier(.16, 1, .3, 1);
  --shake: cubic-bezier(.36, .07, .19, .97);
  --d-fast: 120ms;
  --d-enter: 240ms;
  --d-rise: 280ms;
  --d-shake: 300ms;

  --sans: "Source Sans 3", -apple-system, BlinkMacSystemFont, "Segoe UI", ui-sans-serif, system-ui, sans-serif;
  --display: "Mona Sans", var(--sans);
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* Os mesmos valores da folha do console, medidos lá: --ink-soft 8.40:1 sobre
     --bg, --ink-faint 5.65:1 sobre --bg. Nenhuma cor nova sem medir. */
  --bg: #f6f8fc;
  --card: #ffffff;
  --ink: #28272a;
  --ink-soft: #4a494d;
  --ink-faint: #626366;
  --line: #e2e0da;
  /* A borda de CAMPO não é a de cartão: #C9C7C1, o da entrega, dá 1.69:1
     sobre branco — um campo cujo contorno não se vê (WCAG 1.4.11 pede 3:1).
     #8D8B85 é o mesmo cinza pedra escurecido até 3.41:1. */
  --line-strong: #8d8b85;

  /* Hope Blue. Branco sobre ele dá 8.03:1, e ele como texto passa em todos os
     fundos desta tela. */
  --accent: #034ea2;
  --accent-down: #023a7a;
  --accent-up: #1e63b8;        /* topo do gradiente do botão primário */
  --accent-down-2: #0b56ac;    /* topo do gradiente com o ponteiro em cima */
  /* O TEXTO DE APOIO SOBRE O ACENTO (A11Y-03), como em app.css: 86% de branco
     dá 6,36:1 sobre o azul e 3,73:1 sobre o carmim de homologação; lá vira
     branco inteiro (4,70:1). */
  --on-accent-2: rgba(255, 255, 255, .86);
  --accent-text: #034ea2;
  --accent-soft: #e6eef8;
  --accent-ink: #ffffff;
  /* O ACENTO TAMBÉM COMO TRÊS NÚMEROS, porque brilho e sombra usam a mesma cor
     com alfa variável. Sem isto, o botão vermelho de homologação projetaria um
     brilho AZUL. */
  --accent-rgb: 3, 78, 162;

  /* Fire Orange: só o ponto do logotipo. "Laranja não decora." */
  --human: #fe5442;

  --bad-bg: #ffe9e6;
  --bad-line: rgba(179, 58, 44, .30);
  --bad-ink: #8f2e23;
  --ok-bg: #e8f3ec;
  --ok-line: rgba(27, 107, 58, .24);
  --ok-ink: #1b6b3a;
  --neutral-bg: #eef1f6;
  --neutral-line: rgba(40, 39, 42, .14);
}

/* ═══ HOMOLOGAÇÃO É VERMELHA. PRODUÇÃO É AZUL. ═══
   É NESTA TELA QUE A REGRA MAIS IMPORTA. As duas instalações servem o mesmo
   código contra o mesmo banco, e uma campanha criada por engano em homologação
   vira SMS de verdade pelo worker de produção. Quem abre esta página ainda não
   tem nada além do endereço na barra para saber onde está.

   O servidor carimba `data-ambiente` no <html> quando o ambiente não é
   produção. Produção não recebe atributo nenhum e por isso não tem como ficar
   vermelha por engano. Os valores são os mesmos da folha do console. O painel
   da marca é pintado pelo acento, então fica vermelho junto. */
:root[data-ambiente="staging"],
:root[data-ambiente="dev"] {
  --accent: #e4143d;
  --accent-rgb: 228, 20, 61;
  --accent-down: #b8203f;
  /* CRÍTICO: `#ef2f55` dava 4,03:1 para o branco de "Sign in" — o rótulo cruza
     a parada de CIMA do gradiente do botão (`--accent-up` → `--accent`).
     `#d31f42` é o mesmo valor que `app.css` já adotou pelo mesmo motivo:
     branco sobre ele fecha 5,19:1 (parada de baixo, `--accent` #e4143d: 4,70:1). */
  --accent-up: #d31f42;
  --accent-down-2: #e31f46;
  --on-accent-2: #ffffff;
  --accent-text: #b8203f;
  --accent-soft: #feeff2;
  --accent-ink: #ffffff;
}

/* A FAIXA DE AMBIENTE, em palavras, como em toda tela do console: cor nunca é
   o único canal. Só existe sob o carimbo do servidor. */
:root[data-ambiente] { --faixa-h: 32px; }
:root[data-ambiente="staging"] body::before,
:root[data-ambiente="dev"] body::before {
  display: block; height: var(--faixa-h); padding: 0 var(--sp-6);
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  text-align: center; background: var(--accent); color: var(--accent-ink);
  font-size: var(--fs-sm); line-height: var(--faixa-h); font-weight: 600;
}
:root[data-ambiente="staging"] body::before {
  content: "Staging \2014  this copy reads the production database. A batch authorised here really goes out.";
}
:root[data-ambiente="dev"] body::before {
  content: "Development \2014  local copy.";
}

* { box-sizing: border-box; }

/* O ATRIBUTO `hidden` TEM DE GANHAR DE TODO `display` DAQUI. LEIA ANTES DE
 * MEXER — esta tela já foi para o ar quebrada exatamente por não ter esta regra.
 *
 * `hidden` esconde porque a folha do NAVEGADOR traz `[hidden]{display:none}`. E
 * folha de navegador perde de qualquer regra de autor: no instante em que esta
 * folha escreveu `.note{display:flex}` e `.field{display:flex}`, o `hidden` dos
 * elementos com essas classes deixou de fazer efeito. O resultado foi a tela de
 * entrada mostrando, ao mesmo tempo e do lado da senha, a faixa de erro vazia, a
 * faixa de motivo vazia e o campo do autenticador — os três estados que só
 * deveriam existir em momentos diferentes, empilhados de uma vez.
 *
 * Este é o único `!important` fora do bloco de movimento reduzido, e ele existe
 * para que a próxima classe com `display` não reabra o mesmo buraco.
 *
 * O `hidden` é o mecanismo de estado desta tela inteira. Não é detalhe de CSS;
 * é a máquina de estados. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--card);
  color: var(--ink);
  font: 400 var(--fs-md)/var(--lh-md) var(--sans);
  -webkit-font-smoothing: antialiased;
}

/* ─────────────────────────────────────────────── a página: painel + coluna */
/* `dvh` depois de `vh`: em mobile a barra do navegador entra na conta de `vh`
   e a tela ganha uma rolagem que não existe. */
.page {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  grid-template-columns: minmax(260px, 32%) minmax(0, 1fr);
  background: linear-gradient(180deg, var(--card) 0%, var(--bg) 100%);
}
:root[data-ambiente] .page { min-height: calc(100dvh - var(--faixa-h)); }

/* O PAINEL DA MARCA (D6). Compacto, estático, sem número nenhum. É o acento
   que o pinta, então homologação o deixa vermelho sem regra própria. */
.panel {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; justify-content: space-between;
  gap: var(--sp-8);
  padding: var(--sp-10) clamp(24px, 4vw, 48px);
  background: linear-gradient(160deg, var(--accent) 0%, var(--accent-down) 70%, var(--ink) 100%);
  color: #ffffff;
}
.panel-ring {
  position: absolute; border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .2);
  pointer-events: none;
}
.panel-ring.r1 { width: 420px; height: 420px; right: -150px; top: -150px; }
.panel-ring.r2 { width: 280px; height: 280px; right: -80px;  top: -80px;
                 border-color: rgba(255, 255, 255, .28); }
.panel-kicker, .panel-foot {
  position: relative;
  font-size: var(--fs-2xs); line-height: var(--lh-2xs);
  font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
  color: var(--on-accent-2);
}

.wrap {
  width: 100%; max-width: 448px;
  margin: auto;
  padding: var(--sp-10) var(--sp-7);
  animation: rise var(--d-rise) var(--glide) both;
}
@keyframes rise {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

/* Só para leitor de tela: o anúncio de "senha visível" não precisa ocupar
   espaço, mas precisa existir. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; /* 1px/-1px: o padrão de texto só para leitor de tela */
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ─────────────────────────────────────────────────────────── marca */
.brand {
  display: flex; align-items: center; gap: var(--sp-5);
  margin: 0 0 var(--sp-8);
}
/* O LOGOTIPO: disco no acento e o ponto laranja com aro branco — o mesmo
   desenho do ícone da aba. Desenhado aqui, e não num arquivo, para o disco
   seguir o acento: em homologação ele fica vermelho junto com o resto. */
.brand .mark {
  position: relative; flex: 0 0 auto;
  width: 44px; height: 44px; border-radius: 50%;
  background: var(--accent);
}
.brand .mark::after {
  content: ""; position: absolute; right: -4px; bottom: -4px;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--human); border: 3px solid var(--card);
}
/* A IMAGEM ESCOLHIDA POR UM ADMINISTRADOR ocupa o lugar inteiro, e o disco sai
   de baixo dela: um logotipo com fundo próprio sobre o disco ganharia uma
   moldura que ninguém pediu. `contain` porque o arquivo pode não ser quadrado,
   e cortar o logotipo de alguém para caber é pior do que deixar uma folga. */
.brand .mark.propria { background: none; border-radius: 0; }
.brand .mark.propria::after { content: none; }
.brand .mark img { width: 44px; height: 44px; object-fit: contain; display: block; }
.brand h1 {
  margin: 0;
  font-family: var(--display);
  font-size: var(--fs-login-title); line-height: var(--lh-login-title);
  font-weight: 700; letter-spacing: -.035em;
}
.brand .h1-acc { color: var(--accent-text); }
.brand p {
  margin: var(--sp-2) 0 0;
  font-size: var(--fs-sm); line-height: var(--lh-sm);
  color: var(--ink-soft);
}

/* ─────────────────────────────────────────────────────────── card */
/* O PADDING LATERAL É DO CARD, não de cada filho. Um filho novo que entre sem
   padding próprio não pode nascer colado na borda. */
.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: var(--sp-8);
  box-shadow: 0 1px 2px rgba(40, 39, 42, .06),
              0 24px 56px -28px rgba(var(--accent-rgb), .35);
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
}
.card > * { min-width: 0; }

/* ─────────────────────────────────────────────────── faixa de mensagem */
.note {
  display: flex;
  gap: var(--sp-4);
  align-items: flex-start;
  padding: var(--sp-5);
  border-radius: var(--r-ctrl);
  font-size: var(--fs-sm);
  line-height: var(--lh-sm);
  background: var(--bad-bg);
  border: 1px solid var(--bad-line);
  color: var(--bad-ink);
}
/* A FAIXA APARECE, E A RECUSA OSCILA.
   Numa tela em que a faixa de erro e a de sucesso ocupam o MESMO lugar,
   aparecer sem se mover significa que trocar "link enviado" por "senha
   incorreta" é uma troca de cor e mais nada. O tremor é o segundo canal, além
   da cor e do ícone.

   `.shaking` é uma classe que o script põe e tira, e não um seletor sobre
   `.note:not(.ok)`: a segunda recusa seguida NÃO reanimaria — o nó já está
   visível, a animação já rodou, e o CSS sozinho não sabe que houve outro erro.
   Digitar o código errado duas vezes é exatamente o caso comum desta tela.
   O tremor é CURTO (300 ms) e pequeno: um sinal, não um terremoto. */
.note { animation: note-in var(--d-enter) var(--glide) both; }
.note.shaking { animation: note-in var(--d-enter) var(--glide) both, shake-x var(--d-shake) var(--shake) both; }
@keyframes note-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}
@keyframes shake-x {
  15%, 85%      { transform: translateX(-1px); }
  30%, 70%      { transform: translateX(3px); }
  45%, 55%      { transform: translateX(-3px); }
}
.note p { margin: 0; }
.note-ico { flex: 0 0 auto; font-weight: 700; line-height: var(--lh-sm); }
.note-ico::before { content: "\2715"; }
/* Ícone MAIS texto, nunca só cor: vermelho e verde são quase iguais para uma
   parte das pessoas. */
.note.neutral {
  background: var(--neutral-bg); border-color: var(--neutral-line);
  color: var(--ink-soft);
}
.note.neutral .note-ico::before { content: ""; }
.note.ok {
  background: var(--ok-bg); border-color: var(--ok-line); color: var(--ok-ink);
}
.note.ok .note-ico::before { content: "\2713"; }

.nota-porta {
  margin: 0;
  padding: var(--sp-4) var(--sp-5);
  background: var(--neutral-bg);
  border: 1px solid var(--neutral-line);
  border-radius: var(--r-tag);
  font-size: var(--fs-sm); line-height: var(--lh-sm);
  color: var(--ink-soft);
}

/* ─────────────────────────────────────────────────────────── formulário */
form { display: flex; flex-direction: column; gap: var(--sp-6); margin: 0; }
.field { display: flex; flex-direction: column; gap: var(--sp-3); }

.rotulo {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-4);
}
label {
  font-size: var(--fs-sm); line-height: var(--lh-sm);
  font-weight: 600; color: var(--ink);
}
.hint {
  margin: 0;
  font-size: var(--fs-sm); line-height: var(--lh-sm);
  color: var(--ink-faint);
}
.hint:empty { display: none; }
/* #recover-aviso E #ver-senha-estado SÃO REGIÕES `role="status"` — ficam
   presentes desde o primeiro render, mesmo vazias, em vez de aparecerem já
   com o texto dentro. Um leitor de tela não confia em anunciar um nó que
   nasce e se preenche no mesmo instante; aqui não há nó nascendo, só um
   parágrafo sem texto (sem borda, sem fundo — vazio não pesa no olho) que
   ganha texto depois. */
#recover-aviso, #ver-senha-estado { display: block; }

input[type="email"],
input[type="password"],
input[type="text"] {
  width: 100%;
  min-height: var(--h-control);
  /* Vertical da escala (--sp-4, 8px): 8+8 + a linha (24) + a borda (2) fecha
     em 42px, 2px abaixo de --h-control — o navegador centraliza o texto
     nesses 2px de folga, e o campo continua com a mesma altura do botão. Um
     valor que fechasse os 44px em cheio (--sp-5, 12px) estourava a soma para
     50px e desalinhava campo e botão de novo. */
  padding: var(--sp-4) var(--sp-6);
  font: inherit;
  font-size: var(--fs-md);          /* PISO. Abaixo disso o iOS dá zoom. */
  line-height: var(--lh-md);
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-ctrl);
  box-shadow: inset 0 1px 2px rgba(40, 39, 42, .06);
  transition: border-color var(--d-fast) ease;
}
input::placeholder { color: var(--ink-faint); }
input[readonly] { background: var(--bg); }
/* O ANEL DE FOCO É UM `outline`, e não um `box-shadow`: sobrevive ao alto
   contraste, e é instantâneo — foco animado atrasa a leitura. */
input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-color: var(--accent);
}
/* O CÓDIGO EM MONOESPAÇADA DE VERDADE. A entrega escrevia `font:` num valor
   inválido e o campo caía em Arial; aqui ele é mono, com números tabulares e
   folga entre os caracteres — seis dígitos ou uma chave de onze se leem letra
   por letra. */
input.code {
  font-family: var(--mono);
  font-size: var(--fs-code);
  letter-spacing: .16em;
  font-variant-numeric: tabular-nums;
  text-align: center;
}
/* O CAMPO RECUSADO TREME COM A FAIXA — e passa a DIZER que foi recusado.
   `aria-invalid` é o que liga, para quem ouve, a faixa ao campo; a mesma
   classe `.shaking` faz campo e faixa oscilarem JUNTOS — dois movimentos em
   tempos diferentes leriam como dois problemas, e não um. */
input[aria-invalid="true"] { border-color: var(--bad-ink); }
input.shaking { animation: shake-x var(--d-shake) var(--shake) both; }

/* ─────────────────────────────────────────────────────────── botões */
.primary, .ghost {
  /* MESMO TOKEN DO CAMPO — --h-control, e não um número próprio: botão e
     campo desta tela precisam fechar na mesma altura, porque é o mesmo
     `--h-control` que os alinharia se algum dia ficassem lado a lado, e um
     "quase igual" hoje é o tipo de folga que ninguém nota até precisar dela.
     O padding vertical segue a mesma conta do campo (--sp-4, 8px): a soma com
     a linha e a borda fica abaixo de --h-control, e quem centraliza o rótulo
     nos pixels que sobram é o `align-items: center` do flex abaixo — não o
     padding. */
  min-height: var(--h-control);
  padding: var(--sp-4) var(--sp-6);
  font: inherit;
  font-size: var(--fs-md);
  font-weight: 600;
  line-height: var(--lh-md);
  border-radius: var(--r-ctrl);
  border: 1px solid transparent;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center; gap: var(--sp-4);
  transition: background-color var(--d-fast) ease, transform 90ms var(--glide);
}
.primary {
  width: 100%;
  color: var(--accent-ink);
  background: linear-gradient(180deg, var(--accent-up), var(--accent));
  border-color: var(--accent);
  box-shadow: 0 8px 20px -10px rgba(var(--accent-rgb), .7);
}
.primary:hover:not(:disabled) {
  background: linear-gradient(180deg, var(--accent-down-2), var(--accent-down));
}
.primary:active:not(:disabled) { transform: scale(.98); }
/* `opacity` NUM BOTÃO APAGA O TEXTO JUNTO, E ISSO TEM UM NÚMERO.
   Medido em 05/09/2026: o botão primário desabilitado a `opacity: .6` dava
   1,74:1, contra os 4,5:1 que a lei pede. E o estado em que isso acontece é o
   pior possível: enquanto a chamada corre, o rótulo do botão ("Signing in…",
   "Verifying…") é a ÚNICA coisa na tela que diz o que está acontecendo.

   A separação é entre DOIS estados que `opacity` tratava como um:
   • OCUPADO (`.working`) — o botão está fazendo o que foi pedido. Contraste
     inteiro; o que muda é o cursor e o giro ao lado do rótulo.
   • INDISPONÍVEL (`:disabled` sem `.working`) — não há o que fazer aqui.
     Cinza declarado em vez de tinta desbotada. */
.primary:disabled {
  opacity: 1; background: var(--neutral-bg); color: var(--ink-soft);
  border-color: var(--line); box-shadow: none; cursor: not-allowed;
}
.primary.working:disabled {
  background: linear-gradient(180deg, var(--accent-up), var(--accent));
  border-color: var(--accent); color: var(--accent-ink);
  box-shadow: 0 8px 20px -10px rgba(var(--accent-rgb), .7);
  cursor: progress;
}
/* UM BOTÃO QUE TROCA DE PALAVRA E MAIS NADA parece um botão que travou. O giro
   é o que diz que ainda está correndo — e é o único movimento repetido da tela,
   que só existe enquanto a rede não responde (teto de 10 s no login). */
.primary.working::before {
  /* 14px, o mesmo do giro de `button.working::before` na folha do console —
     mesma decisão, mesmo número, nas duas folhas. */
  content: ""; width: 14px; height: 14px; flex: 0 0 auto;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, .35);
  border-top-color: var(--accent-ink);
  animation: spin 700ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.ghost {
  /* A altura já vem da regra `.primary, .ghost` acima (--h-control):
     repeti-la aqui era o mesmo valor escrito duas vezes, dando a entender
     que este botão precisava de algo próprio quando não precisa. */
  width: 100%;
  color: var(--ink);
  background: var(--card);
  border-color: var(--line-strong);
}
.ghost:hover:not(:disabled) { background: var(--bg); }
.ghost:active:not(:disabled) { transform: translateY(1px); }
.ghost:disabled { opacity: 1; color: var(--ink-soft); background: var(--bg); border-color: var(--line); cursor: not-allowed; }
.ghost.working:disabled { color: var(--ink); background: var(--card); cursor: progress; }

.primary:focus-visible, .ghost:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* "Show" / "Use a recovery key". Alvo de 24px de altura mínima (WCAG 2.5.8),
   44px no dedo, com o padding completando a área tocável sem empurrar o
   rótulo. */
.link-btn {
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--accent-text);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--r-tag);
  padding: var(--sp-2) var(--sp-5);
  /* [#53] 44px de alvo (era 34px de altura): a margem negativa devolve ao
     layout só a linha do rótulo, então a fileira da senha não cresce. */
  margin: -12px -12px -12px 0;
  min-height: 44px;
  cursor: pointer;
  white-space: nowrap;
}
.link-btn:hover { background: var(--accent-soft); }
/* Os quatro estados (R540): faltava o de pressão — `.primary`/`.ghost` já
   têm o próprio `:active`, e este botão ficava sem nenhum retorno de clique
   além do que o navegador dá de graça. */
.link-btn:active { background: var(--accent-soft); transform: scale(.97); }
.link-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ─────────────────────────────────────────────────── provedores e recuperação */
.rule { height: 1px; background: var(--line); }

.sso-buttons { display: grid; gap: var(--sp-4); }
.sso-or {
  display: flex; align-items: center; gap: var(--sp-5);
  color: var(--ink-soft);
  font-size: var(--fs-sm); line-height: var(--lh-sm);
}
.sso-or::before, .sso-or::after {
  content: ""; flex: 1; height: 1px; background: var(--line);
}

/* "FORGOT YOUR PASSWORD?" COMO BOTÃO, e "BACK TO SIGN IN" COMO VOLTA.
   Continua um `<details>` nativo — funciona sem script, e fechar é tão normal
   quanto abrir. Fechado, o `summary` é um botão contornado de largura inteira
   (a entrega); aberto, o formulário de entrada sai da tela e o mesmo `summary`
   vira o caminho de volta, no alto do que ficou. Os dois rótulos moram no HTML
   e a folha mostra o do estado atual.
   SÓ COM SCRIPT (`.js` no <html>, posto pelo próprio script): sem ele o
   formulário de entrada continua na tela com o painel aberto, e "voltar" para
   uma coisa que nunca saiu seria mentira — o `summary` fica como botão. */
.recover { font-size: var(--fs-sm); line-height: var(--lh-sm); }
.recover summary {
  list-style: none;
  cursor: pointer;
  min-height: var(--h-control);
  display: flex; align-items: center; justify-content: center;
  padding: 0 var(--sp-6);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-ctrl);
  background: var(--card);
  color: var(--accent-text);
  font-size: var(--fs-md);
  font-weight: 600;
  transition: background-color var(--d-fast) ease;
}
.recover summary::-webkit-details-marker { display: none; }
.recover summary:hover { background: var(--bg); }
.recover summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.recover .s-aberto { display: none; }
.js .recover[open] .s-fechado { display: none; }
.js .recover[open] .s-aberto { display: inline; }
.js .recover[open] summary {
  justify-content: flex-start;
  min-height: 24px;
  padding: var(--sp-2) 0;
  border-color: transparent;
  background: none;
  font-size: var(--fs-sm);
}
.js .recover[open] summary:hover { text-decoration: underline; }
.recover-body {
  margin-top: var(--sp-5);
  color: var(--ink-soft);
  display: flex; flex-direction: column; gap: var(--sp-4);
}
.recover-body > div { display: flex; flex-direction: column; gap: var(--sp-4); }
.recover-body p { margin: 0; }
.recover-body strong { color: var(--ink); }

/* O CAMPO E O BOTÃO PRECISAM DE AR. `#recover-link` é um `div` dentro do
 * `<form>`, e o `gap` do formulário só separa os filhos DELE — que aqui é um
 * só. O `gap` é o mesmo do formulário de entrada de propósito: são dois
 * formulários na mesma tela, e um respirar diferente do outro faz o de baixo
 * parecer um pedaço solto. */
#recover-link { display: flex; flex-direction: column; gap: var(--sp-6); }

/* A DICA PERTENCE AO BOTÃO ACIMA DELA, e por isso vem mais perto. */
#recover-link > .hint { margin-top: calc(var(--sp-4) - var(--sp-6)); }

footer {
  margin: var(--sp-7) 0 0;
  text-align: center;
  font-size: var(--fs-sm); line-height: var(--lh-sm);
  color: var(--ink-faint);
}

/* ─────────────────────────────────────────────────────────── mobile */
/* Capacidade de entrada, não largura: num touch os controles CRESCEM. */
@media (hover: none), (pointer: coarse) {
  :root { --h-control: 48px; }
  .link-btn { min-height: 44px; padding: var(--sp-5); }
  /* O "← Back to sign in" (o `summary` ABERTO) vira um link pequeno de
     propósito — mas pequeno não pode ser MENOR que o piso de toque. Fechado,
     `.recover summary` já cresce sozinho porque usa --h-control; este é o
     único estado da tela que ainda ficava preso nos 24px do WCAG 2.5.8. */
  .js .recover[open] summary { min-height: 44px; }
}
/* NO TELEFONE O PAINEL VIRA UMA FAIXA. Um painel de 280px acima do formulário
   empurraria o campo de e-mail para fora da primeira tela — e quem entra aqui
   tem, em boa parte, mais de 60 anos. A faixa guarda a marca e cede o resto. */
/* 760px, fora da régua: painel lateral de 260px + coluna do formulário. Medido a
   770px: a coluna do formulário tem 510px para um cartão de 408px. Com 700 o iPad
   mini em pé (744px) trocaria a faixa de marca pelo painel lateral. */
@media (max-width: 760px) {
  .page { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto 1fr; }
  .panel { flex-direction: row; align-items: center; padding: var(--sp-5) var(--sp-7); }
  .panel-ring.r1 { width: 200px; height: 200px; right: -80px; top: -120px; }
  .panel-ring.r2 { display: none; }
  .panel-foot { display: none; }
  .wrap { margin: 0 auto; padding: var(--sp-8) var(--sp-6) var(--sp-9); }
}
/* 400px, fora da régua: aperta o cartão só nos telefones de até 400px (320, 360,
   375, 393). Medido: puxar para 480 mexe também em 412/414/428/430 — o campo
   ganha 16px de largura e o cartão perde 8px de altura entre 401px e 480px. */
@media (max-width: 400px) {
  .card { padding: var(--sp-7) var(--sp-6); }
}

/* ────────────────────────────────────────────── movimento e alto contraste */
/* QUEM PEDIU MENOS MOVIMENTO NÃO RECEBE NENHUM. A entrada, o tremor e o giro
   param inteiros, e a faixa para VISÍVEL — a única forma de desligar movimento
   sem esconder conteúdo. */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
  .wrap, .note, .note.shaking, input.shaking { opacity: 1; transform: none; }
  .primary:active:not(:disabled), .ghost:active:not(:disabled), .link-btn:active { transform: none; }
  .primary.working::before { border-top-color: rgba(255, 255, 255, .35); }
}

/* O ANEL DE FOCO SOBREVIVE AO ALTO CONTRASTE. `box-shadow` é descartado em
 * `forced-colors: active` — o modo de alto contraste do Windows —, e aqui o
 * foco é `outline` exatamente por isso. O bloco força a cor do sistema. */
@media (forced-colors: active) {
  input:focus-visible,
  .primary:focus-visible,
  .ghost:focus-visible,
  .link-btn:focus-visible,
  .recover summary:focus-visible {
    outline: 2px solid CanvasText;
    outline-offset: 2px;
  }
  .note { border-width: 2px; }
  .brand .mark { forced-color-adjust: none; }
}
