/*
 * 03_movimento.css — ONDA S, bloco MOTOR · docs/MARCA.md §2
 * ═══════════════════════════════════════════════════════════════════════════
 * A LÍNGUA DE MOVIMENTO da Elevô. Fica no número 03 — depois de 02_base.css e
 * antes de 10_componentes.css — porque `stylesheet_link_tag :app` emite um
 * <link> por folha em ordem de NOME, e as curvas e os tempos precisam existir
 * antes de qualquer componente pedir `var(--curva-entrada)`.
 *
 * Carregar ANTES da 10 tem uma consequência que este arquivo respeita de
 * propósito: em empate de especificidade, a 10 vence. Então tudo que aqui
 * refina um componente dela sobe a especificidade DE PROPÓSITO e de um jeito
 * que também descreve a intenção — `.botao:not(:disabled)` diz "isto é sobre
 * o botão que responde ao dedo", que é exatamente o assunto. O que a 10
 * decidiu e tem razão escrita ao lado (o hover em anel do botão cheio, com a
 * conta de contraste da §15.2) continua intocado: não é meu para mexer.
 *
 * ── AS TRÊS DECISÕES QUE ESTA FOLHA TOMA ──────────────────────────────────
 *
 * 1. CONTEÚDO NASCE VISÍVEL. A revelação ao rolar é o único efeito do sistema
 *    que pode ESCONDER informação, e por isso é o único que exige JS para
 *    começar: `.revelar` sozinho não esconde nada. Quem esconde é a classe
 *    `.revelar--oculto`, que só o `revelar_controller` põe, e só depois de
 *    confirmar que existe `IntersectionObserver`. Sem JS, com JS quebrado, com
 *    o importmap falhando no 4G do pátio, ou num leitor que não roda script: a
 *    página é a página inteira. `@starting-style` foi considerado e recusado —
 *    ele dispara na INSERÇÃO do elemento, não na rolagem, então resolveria a
 *    entrada de seção e não resolveria a revelação, que é o pedido; e para
 *    esconder o de baixo ainda seria preciso um estado inicial no CSS, que é
 *    exatamente o risco que se quer evitar.
 *
 * 2. SÓ `transform` E `opacity`. Nenhuma animação desta folha toca `width`,
 *    `height`, `top`, `left`, `margin` ou `filter`: essas três primeiras
 *    disparam layout a cada quadro e é assim que 60fps vira 12fps num celular
 *    de entrada, que é o aparelho do aluno.
 *
 * 3. ANIMAÇÃO NUNCA ATRASA LEITURA. O contador já nasce com o número final
 *    escrito no HTML; o JS só o percorre. Nada espera meio segundo para dizer
 *    o que já sabe. Cada revelação dura no máximo `--tempo-lento` (0,42 s), e
 *    o escalonamento de uma lista tem teto PRÓPRIO de 0,3 s — somados, o
 *    último item de qualquer lista termina de aparecer em 0,72 s, com
 *    escalonamento de 60 ms ou de 200 ms. O teto antigo era em ITENS (6), o
 *    que com 90 ms punha o último item começando aos 540 ms e terminando aos
 *    960 ms: uma lista que ainda estava chegando quase um segundo depois de
 *    entrar na tela. Teto medido em tempo é o único que descreve a promessa.
 *
 * `prefers-reduced-motion` já é desligado no atacado por 02_base.css (o bloco
 * com `!important` no fim daquela folha). O que esta folha acrescenta é o
 * SIMULADOR — `[data-movimento="reduzido"]` — para que /estilo consiga provar
 * a degradação sem pedir para ninguém mexer na configuração do sistema.
 */

:root {
  /* ── As quatro curvas da marca ───────────────────────────────────────────
     Uma curva é uma frase: ela diz de onde a energia vem e para onde vai.

     --curva-entrada   coisa que CHEGA e fica: seção revelada, modal abrindo,
                       gaveta subindo, item de lista entrando. Desacelera
                       forte no fim, então o elemento "assenta" em vez de
                       bater. É a curva mais usada do sistema.
     --curva-saida     coisa que VAI EMBORA: modal fechando, flash sumindo,
                       item removido. Acelera para fora — o olho não precisa
                       acompanhar o que já não importa, e por isso a saída é
                       sempre mais curta que a entrada.
     --curva-padrao    coisa que MUDA sem chegar nem sair: cor de hover, borda
                       de foco, altura de acordeão. Simétrica de propósito.
     --curva-elastica  CONFIRMAÇÃO, e só confirmação: aula concluída, resposta
                       certa, certificado emitido, medidor batendo 100%. O
                       excesso é de 1,35 — passa do ponto e volta, uma vez. É
                       o único lugar do sistema onde algo pode ultrapassar o
                       destino, porque é o único lugar em que isso significa
                       alguma coisa ("deu certo"). Em hover elástico vira
                       gelatina, e gelatina é a diferença entre um produto que
                       parece vivo e um que parece brinquedo. */
  --curva-entrada: cubic-bezier(0.16, 1, 0.3, 1);
  --curva-saida: cubic-bezier(0.45, 0, 0.9, 0.25);
  --curva-padrao: cubic-bezier(0.4, 0, 0.2, 1);
  --curva-elastica: cubic-bezier(0.34, 1.35, 0.64, 1);

  /* ── Três durações, e nada entre elas ────────────────────────────────────
     --tempo-rapido  120ms  resposta a DEDO: hover, foco, press, seleção. Acima
                            de ~150 ms o toque começa a parecer que "não pegou".
     --tempo-normal  220ms  mudança de ESTADO no lugar: aba trocando, selo
                            virando, modal abrindo no desktop, contador curto.
     --tempo-lento   420ms  entrada e saída de BLOCO: revelação ao rolar,
                            gaveta subindo, transição de página. É o teto: nada
                            no produto passa disso, porque a partir daí o
                            usuário espera a animação em vez de ler a tela. */
  --tempo-rapido: 120ms;
  --tempo-normal: 220ms;
  --tempo-lento: 420ms;

  /* Quanto a revelação desloca. Fica em variável porque o herói usa mais e a
     linha de tabela usa quase nada. */
  --revelar-desloca: 1.25rem;
}

/* ═══ REVELAÇÃO AO ROLAR ═══════════════════════════════════════════════════
   `.revelar` SOZINHO NÃO ESCONDE NADA — ver decisão 1 no topo. Ele só
   declara a transição, que sem mudança de estado não custa quadro nenhum.   */

/* ── E A REVELAÇÃO HORIZONTAL NÃO PODE ROLAR A PÁGINA DE LADO ──────────────
   `.revelar--esquerda` começa 20 px À DIREITA do lugar (é de lá que ele vem).
   Num bloco que já encosta na margem do celular, esses 20 px passam da borda
   da tela e a página INTEIRA ganha rolagem horizontal — que se cura sozinha
   quando o bloco é revelado, e é justamente por se curar que o defeito é
   difícil de achar e fácil de deixar passar. Medido em 390 px: `scrollWidth`
   394 antes, 390 depois.

   `clip` e não `hidden`: `hidden` deixa a página ROLÁVEL POR SCRIPT (é um
   scrollport sem barra), e um `scrollTo` ou um `focus()` num elemento colado
   na margem passa a deslocar a tela inteira de lado sem ninguém ter pedido;
   `clip` não é scrollport nenhum. Navegador que não conhece `clip` ignora a
   declaração e fica como estava — nenhuma tela piora.

   MEDIDO a 390x844, com um bloco de largura cheia em `.revelar--esquerda`
   escondido, girando a roda 300 px para o lado: sem esta linha a página anda
   20 px; com ela, 0. O `scrollWidth` continua dizendo 395 (ele mede o
   conteúdo, não o que rola) — o que mudou é que nada mais se mexe debaixo do
   dedo. E o cabeçalho grudado continua grudado (topo 0 depois de rolar
   1.500 px), que é o que se perderia com `hidden` num elemento que não fosse
   a raiz.                                                                   */
html { overflow-x: clip }

.revelar {
  transition:
    opacity var(--tempo-lento) var(--curva-entrada) var(--revelar-atraso, 0ms),
    transform var(--tempo-lento) var(--curva-entrada) var(--revelar-atraso, 0ms);
}

/* Quem põe esta classe é o `revelar_controller`, e só depois de achar o
   IntersectionObserver. `translate3d` e não `translateY`: força camada de
   composição e tira o texto do caminho do layout durante a subida. */
.revelar--oculto {
  opacity: 0;
  transform:
    translate3d(var(--revelar-x, 0px), var(--revelar-y, var(--revelar-desloca)), 0)
    scale(var(--revelar-escala, 1));
}

/* As quatro direções. Todas terminam em `transform: none`, então compor duas
   por engano não trava nada — só deixa de fazer sentido. */
.revelar--sobe   { --revelar-y: var(--revelar-desloca) }
.revelar--desce  { --revelar-y: calc(var(--revelar-desloca) * -1) }
.revelar--direita { --revelar-x: calc(var(--revelar-desloca) * -1); --revelar-y: 0px }
.revelar--esquerda { --revelar-x: var(--revelar-desloca); --revelar-y: 0px }
/* Cartão e número grande: cresce de dentro, sem deslocar (deslocar um número
   grande faz a página inteira parecer que tremeu). */
.revelar--cresce { --revelar-y: 0px; --revelar-escala: 0.965 }
/* Só opacidade: para texto corrido e tabela, onde qualquer deslocamento
   atrapalha quem está lendo enquanto rola. */
.revelar--suave  { --revelar-y: 0px }

/* ═══ CONTADOR ════════════════════════════════════════════════════════════
   Números tabulares são obrigatórios aqui, e não estética: sem eles a largura
   do número muda a cada quadro e o rótulo ao lado fica pulando. */

.contador { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1 }
.contador__valor { display: inline-block }

/* ═══ MICRO-INTERAÇÃO ═════════════════════════════════════════════════════
   O que a folha 10 já resolveu fica lá. Aqui entra só o que faltava para o
   toque parecer intencional em vez de padrão de navegador.                  */

/* Botão: a folha 10 dá hover de anel + elevação e `active: translateY(1px)`.
   Faltavam a ELEVAÇÃO no hover (o anel sozinho não diz "clicável" a dedo) e a
   curva — `--transicao-rapida` é a genérica; press quer a curva de saída. */
.botao:not(:disabled) {
  transition:
    background-color var(--tempo-rapido) var(--curva-padrao),
    border-color var(--tempo-rapido) var(--curva-padrao),
    box-shadow var(--tempo-rapido) var(--curva-padrao),
    transform var(--tempo-rapido) var(--curva-saida);
}
@media (hover: hover) {
  .botao:hover:not(:disabled):not([aria-disabled="true"]) { transform: translateY(-1px) }
}
.botao:active:not(:disabled) { transform: translateY(1px) }

/* Carregando: a folha 10 desenha o disco com `::after`. A rotação linear é o
   único movimento do sistema que NÃO usa curva da marca — espera tem que
   parecer constante, e uma curva com aceleração faz o disco parecer travar. */
.botao.botao--carregando::after { animation-timing-function: linear }

/* Campo: o anel de foco da folha 10 aparece de uma vez. Aqui ele CRESCE, que
   é o que faz o teclado parecer guiado em vez de teleportado. */
.campo__controle:not(:disabled) {
  transition:
    border-color var(--tempo-rapido) var(--curva-padrao),
    box-shadow var(--tempo-rapido) var(--curva-entrada),
    background-color var(--tempo-rapido) var(--curva-padrao);
}

/* Cartão que ABRE MODAL. É o componente central do padrão card → modal
   (MARCA.md §3) e não existia: `.cartao` é uma superfície, `.cartao--acionavel`
   é uma superfície que promete que clicar faz alguma coisa.

   `:has(:focus-visible)` e não `:focus-within`: o cartão inteiro se acende
   quando o link de dentro recebe foco DE TECLADO, e continua quieto quando o
   foco chegou por clique — que é o comportamento que `:focus-visible` define
   e `:focus-within` não sabe distinguir. */
.cartao--acionavel {
  cursor: pointer;
  transition:
    transform var(--tempo-normal) var(--curva-entrada),
    box-shadow var(--tempo-normal) var(--curva-entrada),
    border-color var(--tempo-rapido) var(--curva-padrao);
}
@media (hover: hover) {
  .cartao--acionavel:hover {
    transform: translateY(-3px);
    box-shadow: var(--sombra-2);
    border-color: var(--cor-marca-tenue);
  }
}
.cartao--acionavel:active { transform: translateY(0); box-shadow: var(--sombra-1) }
.cartao--acionavel:has(:focus-visible) { border-color: var(--cor-marca) }

/* O cartão inteiro vira alvo de clique sem aninhar um <a> gigante: o link do
   título estica um pseudo-elemento por cima de tudo (mesma técnica de
   `.lista__titulo a::after`, folha 10). Só o link é focável, só o link é
   anunciado — a área clicável é que é grande. */
.cartao--acionavel { position: relative }
/* Herdar a cor e tirar o sublinhado é METADE da técnica, não acabamento: o
   alvo é o CARTÃO, e um título azul sublinhado no meio dele devolve a página a
   1999 — o olho lê "link no meio do texto" onde a promessa é "esta caixa
   inteira abre". A primeira versão copiou só o `::after` de `.lista__titulo a`
   (10_componentes.css) e esqueceu a linha de cima, e o bloco da landing teve
   de remendar isso duas vezes na folha dele. Com a regra aqui, todo consumidor
   do padrão nasce certo. */
.cartao--acionavel .cartao__gatilho { color: inherit; text-decoration: none }
.cartao--acionavel .cartao__gatilho::after { content: ""; position: absolute; inset: 0 }
/* Qualquer controle real dentro do cartão precisa ficar ACIMA do gatilho,
   senão o botão "Baixar certificado" abre o modal do cartão. */
.cartao--acionavel .cartao__rodape,
.cartao--acionavel .cartao__acoes { position: relative; z-index: 1 }

/* ═══ PULSO DE CONFIRMAÇÃO ════════════════════════════════════════════════
   A curva elástica, uma vez, em quem acabou de dar certo: aula concluída,
   resposta correta, medidor batendo 100%, certificado emitido. Aplicar pela
   classe e remover depois (o helper `pulso` devolve o data-attribute pronto). */

.pulso { animation: pulso-confirma var(--tempo-lento) var(--curva-elastica) both }
@keyframes pulso-confirma {
  0%   { transform: scale(1) }
  45%  { transform: scale(1.06) }
  100% { transform: scale(1) }
}

/* Entrada de bloco sem rolagem: o que já está na tela quando ela abre. Usado
   pelo modal, pela gaveta e pelo flash. */
@keyframes entra-de-baixo { from { opacity: 0; transform: translate3d(0, 0.75rem, 0) } }
@keyframes some-no-lugar  { to   { opacity: 0; transform: scale(0.98) } }

/* ═══ TRANSIÇÃO DE PÁGINA ═════════════════════════════════════════════════
   Dois caminhos, e os dois degradam para "nada acontece", que é uma navegação
   normal:

   · `@view-transition` cobre a navegação CRUZANDO documento — o que sobra
     depois que o Turbo pega o resto (redirect de formulário com
     `data-turbo="false"`, link para outro host do tenant, primeiro carregamento).
     Este é o caminho LIGADO, e está medido: depois de um recarregamento com
     transição, o modal aberto em seguida continua clicável ponto a ponto.
   · `::view-transition-*` cobriria também a transição do Turbo DENTRO do
     documento — mas só quando o <head> tem
     `<meta name="view-transition" content="same-origin">`.

   ── NÃO PEÇA ESSA <meta> AO INTEGRADOR AINDA, E ISTO ESTÁ MEDIDO ──────────
   A primeira versão deste arquivo pedia a <meta> como "pedido nº 1". Com ela
   no <head>, um `<dialog>` que chega pelo `<turbo-frame id="modal">` fica
   MORTO AO PONTEIRO no desktop: `document.elementFromPoint` no meio da folha
   devolve `HTML`, nenhum `pointerdown` chega ao puxador, e arrastar não faz
   nada. Com a <meta> fora, o mesmo ponto devolve `DIV.modal__puxador` e o
   arrasto anda 180 px. Reproduzido a 1440x900, na gaveta, sempre; no modal
   comum, de forma intermitente (depende de ter havido um modal antes); a
   390 px não reproduziu. E não é destas regras: tirando as duas animações
   abaixo e deixando o cross-fade padrão do navegador, o defeito continua —
   é a transição do Turbo e o `<dialog>` na camada superior.

   Então o bloco abaixo fica valendo para a navegação cruzando documento, que
   é onde ele foi medido funcionando, e a transição dentro do documento fica
   para o dia em que alguém provar num navegador que ela não come o clique do
   modal. Um cross-fade não vale um modal que não responde ao dedo.          */

@view-transition { navigation: auto }

::view-transition-old(root) {
  animation: some-no-lugar var(--tempo-rapido) var(--curva-saida) both;
}
::view-transition-new(root) {
  animation: entra-de-baixo var(--tempo-normal) var(--curva-entrada) both;
}

/* `@view-transition` fora de qualquer `@media`, de propósito: aninhar uma
   regra de nível superior dentro de uma consulta é suporte irregular, e o que
   precisa obedecer ao movimento reduzido não é a existência da transição — é
   a ANIMAÇÃO dela. Sem animação, a troca de tela é instantânea, que é
   exatamente o que quem pediu movimento reduzido quer. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none }
}

/* ═══ SIMULADOR DE MOVIMENTO REDUZIDO ═════════════════════════════════════
   Espelha, palavra por palavra, o bloco `prefers-reduced-motion` de
   02_base.css — mas por ATRIBUTO, para que /estilo (e qualquer tela em teste)
   consiga provar a degradação sem depender da configuração do sistema de quem
   está olhando. O `!important` é o mesmo daquele bloco e pela mesma razão:
   isto precisa vencer de qualquer componente.

   A revelação é o caso especial: desligar a transição sem desligar o estado
   deixaria o conteúdo escondido para sempre. Por isso `--oculto` é anulado
   aqui, e não só desacelerado. */
[data-movimento="reduzido"],
[data-movimento="reduzido"] *,
[data-movimento="reduzido"] *::before,
[data-movimento="reduzido"] *::after {
  animation-duration: 0.01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
  transition-delay: 0ms !important;
  scroll-behavior: auto !important;
}
[data-movimento="reduzido"] .revelar--oculto { opacity: 1 !important; transform: none !important }
[data-movimento="reduzido"] .cartao--acionavel:hover { transform: none !important }

/* E o mesmo cuidado com a revelação no movimento reduzido DE VERDADE: o bloco
   da 02 zera a duração, o que revelaria o conteúdo num salto. Aqui ele nem
   chega a ser escondido. */
@media (prefers-reduced-motion: reduce) {
  .revelar--oculto { opacity: 1; transform: none }
  .pulso { animation: none }
}
