/* =========================================================================
   Dotte PWA — design system
   Cores e tipografia espelham lib/core/presentation/theme do app Flutter.
   CSS escrito à mão de propósito: sem build step, deploy = copiar arquivo.
   ========================================================================= */

@font-face { font-family: 'Space Grotesk'; src: url('/fonts/SpaceGrotesk-Regular.ttf') format('truetype');  font-weight: 400; font-display: swap; }
@font-face { font-family: 'Space Grotesk'; src: url('/fonts/SpaceGrotesk-Medium.ttf') format('truetype');   font-weight: 500; font-display: swap; }
@font-face { font-family: 'Space Grotesk'; src: url('/fonts/SpaceGrotesk-SemiBold.ttf') format('truetype'); font-weight: 600; font-display: swap; }
@font-face { font-family: 'Space Grotesk'; src: url('/fonts/SpaceGrotesk-Bold.ttf') format('truetype');     font-weight: 700; font-display: swap; }

@font-face { font-family: 'Inter'; src: url('/fonts/Inter-Regular.ttf') format('truetype');  font-weight: 400; font-display: swap; }
@font-face { font-family: 'Inter'; src: url('/fonts/Inter-Medium.ttf') format('truetype');   font-weight: 500; font-display: swap; }
@font-face { font-family: 'Inter'; src: url('/fonts/Inter-SemiBold.ttf') format('truetype'); font-weight: 600; font-display: swap; }
@font-face { font-family: 'Inter'; src: url('/fonts/Inter-Bold.ttf') format('truetype');     font-weight: 700; font-display: swap; }

@font-face { font-family: 'Encode Sans'; src: url('/fonts/EncodeSans-Regular.ttf') format('truetype');  font-weight: 400; font-display: swap; }
@font-face { font-family: 'Encode Sans'; src: url('/fonts/EncodeSans-Medium.ttf') format('truetype');   font-weight: 500; font-display: swap; }
@font-face { font-family: 'Encode Sans'; src: url('/fonts/EncodeSans-SemiBold.ttf') format('truetype'); font-weight: 600; font-display: swap; }
@font-face { font-family: 'Encode Sans'; src: url('/fonts/EncodeSans-Bold.ttf') format('truetype');     font-weight: 700; font-display: swap; }

:root {
  --amarelo:        #FFD400;
  --amarelo-escuro: #FFBF00;
  --amarelo-claro:  #FFE5A4;
  --dourado:        #96740F;
  --preto:          #1A1600;
  --branco:         #FFFFFF;
  --fundo:          #F5F5F5;
  --cinza:          #3A5160;
  --cinza-medio:    #A8A8A8;
  --cinza-claro:    #E0E0E0;
  --texto:          #253840;
  --texto-suave:    #4A6572;
  --sucesso:        #4CAF50;
  --erro:           #FF6B6B;

  --raio:      8px;
  --raio-pill: 32px;
  --gap:       16px;

  --fonte-display: 'Space Grotesk', system-ui, sans-serif;
  --fonte-corpo:   'Inter', system-ui, sans-serif;
  --fonte-num:     'Encode Sans', system-ui, sans-serif;

  --topo:   calc(env(safe-area-inset-top, 0px));
  --rodape: calc(env(safe-area-inset-bottom, 0px));
  --altura-nav: 68px;

  /* Respiro ACIMA da barra no fim de toda tela — a altura da barra entra à
     parte, no padding de .conteudo. Precisa vencer a bolha da aba ativa, que
     sobe ~21px acima dela, senão o último botão da página fica encoberto
     justamente quando o usuário rola até o fim para tocá-lo. */
  --folga-rodape: 56px;
}

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

html, body {
  margin: 0;
  padding: 0;
  min-height: 100%;
  background: var(--fundo);
}

body {
  font-family: var(--fonte-corpo);
  font-size: 14px;
  color: var(--texto);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-y: none;
}

/* Só o app instalado bloqueia seleção: no navegador, copiar texto é útil. */
body.standalone {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
a { color: inherit; text-decoration: none; }

/*
  Foco é sempre preto firme, nunca amarelo — mesma regra do Dotte web, onde
  .form-floating-outline .form-control:focus fecha em #1A1600. O amarelo é a cor
  de MARCA (aba ativa, interruptor ligado, pastilha de entrada); usá-lo também
  para foco faz o usuário ler "isto está selecionado/ligado" onde só se quis
  dizer "o teclado está aqui".

  `:focus-visible` e não `:focus`: assim o anel aparece para teclado e leitor de
  tela e não pisca a cada toque no celular. Campos de texto sobrescrevem isto
  com o próprio tratamento, logo abaixo em .campo__entrada.
*/
:focus-visible { outline: 2px solid var(--preto); outline-offset: 2px; }

/* ------------------------------------------------------------------ shell */

/*
  A área de conteúdo ocupa a altura toda e a barra flutua POR CIMA dela — é o
  `extendBody: true` do Scaffold do Flutter. Só assim o recorte é transparente
  de fato: o que passa por trás dele é a página rolando, não uma cor de fundo.

  Enquanto o conteúdo parava na borda de cima da barra, o buraco mostrava
  sempre o mesmo pixel parado, e um pixel parado é uma cor chapada — dava no
  mesmo pintar o entalhe. O que faz o olho ler "vazado" é ver algo se mover
  ali dentro.

  A contrapartida — conteúdo escondido sob a barra ao rolar até o fim — é
  resolvida no padding de .conteudo, não tirando a barra do caminho.
*/
.shell {
  position: relative;         /* âncora da barra flutuante */
  height: 100dvh;
  display: flex;
  flex-direction: column;
  background:
    radial-gradient(circle at 50% 0%, rgba(255, 212, 0, 0) 40%, rgba(255, 212, 0, 0.20) 100%),
    var(--fundo);
}

.topo {
  flex: none;
  z-index: 30;
  padding: calc(var(--topo) + 10px) var(--gap) 10px;
  display: grid;
  grid-template-columns: 40px 1fr 40px;
  align-items: center;
  background: linear-gradient(to bottom, var(--fundo) 70%, rgba(245, 245, 245, 0));
}

.topo__logo {
  justify-self: center;
  /* Sem `gap`: o ponto da marca é um <span> próprio e viraria outro item do
     flex, ganhando espaço no meio de "dotte." O respiro do selo vem da margem
     dele, não da caixa. */
  display: inline-flex;
  align-items: center;
  font-family: var(--fonte-display);
  font-weight: 700;
  font-size: 24px;
  letter-spacing: -0.5px;
  color: var(--preto);
}

.icone-btn {
  width: 40px;
  height: 40px;
  border: 0;
  background: transparent;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--preto);
}
.icone-btn:active { background: rgba(0, 0, 0, 0.06); }
.icone-btn svg { width: 22px; height: 22px; }

.badge {
  position: absolute;
  transform: translate(10px, -10px);
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--erro);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  display: grid;
  place-items: center;
}

.conteudo {
  flex: 1 1 auto;
  min-height: 0;              /* sem isto o flex não deixa o filho rolar */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  /* A barra flutua sobre o conteúdo: o padding de baixo precisa vencer a
     altura dela ANTES de sobrar a folga de segurança. Sem isto o último botão
     da página fica sob a barra justamente quando se rola até o fim. */
  padding: 4px var(--gap) calc(var(--altura-nav) + var(--rodape) + var(--folga-rodape));
  max-width: 640px;
  width: 100%;
  margin: 0 auto;
}

/* Telas sem barra (layouts.avulso): não há o que descontar. */
.conteudo--sem-nav { padding-bottom: calc(var(--rodape) + var(--folga-rodape)); }

/* ------------------------------------------------------------- tipografia */

.saudacao {
  font-family: var(--fonte-display);
  font-size: 28px;
  font-weight: 700;
  line-height: 1.15;
  margin: 4px 0 2px;
  color: var(--preto);
}

.subtitulo {
  font-family: var(--fonte-num);
  font-size: 14px;
  color: var(--texto);
  margin: 0 0 20px;
}

.titulo-tela {
  font-family: var(--fonte-display);
  font-size: 20px;
  font-weight: 700;
  text-align: center;
  margin: 0 0 16px;
}

.overline {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--texto);
}

.apoio { font-size: 12px; color: var(--texto-suave); }

/* ------------------------------------------------------------------ card */

.card {
  background: var(--branco);
  border-radius: var(--raio);
  padding: var(--gap);
  margin-bottom: var(--gap);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}

.card--vazio {
  text-align: center;
  color: var(--texto-suave);
  padding: 28px var(--gap);
}

/*
  `minmax(0, 1fr)`, não `1fr`.

  `1fr` é atalho para `minmax(auto, 1fr)`, e esse `auto` é o min-content do
  item. O card dos últimos registros contém o trilho do slider, cujos itens são
  `flex: 0 0 100%` e não encolhem — o min-content dele é a soma de TODOS os
  itens. Com `1fr`, a coluna crescia para caber isso: medido, 545px num
  viewport de 390, empurrando o card de banco de horas para fora da tela.

  Zerar o mínimo deixa as colunas com metade cada e o excesso é recortado pelo
  `overflow: hidden` do palco, que é onde ele deve ser recortado.
*/
.grade-2 {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--gap);
  margin-bottom: var(--gap);
}
.grade-2 > .card { margin-bottom: 0; min-width: 0; }

/* ----------------------------------------------------------- relógio/data */

.relogio { text-align: center; }

.relogio__data {
  font-family: var(--fonte-num);
  font-size: 14px;
  color: var(--dourado);
  margin-bottom: 4px;
}

.relogio__hora {
  font-family: var(--fonte-num);
  font-weight: 500;
  font-size: 49px;
  line-height: 1;
  color: var(--preto);
}
.relogio__seg { font-size: 29px; font-weight: 500; }

/* ---------------------------------------------------------------- botões */

/*
  Hierarquia de botões — uma regra, três papéis.

  Antes cada tela decidia sozinha, e em "nova solicitação" o resultado era
  invertido: Cancelar aparecia preto e sólido, Salvar amarelo. A ação secundária
  dominava a tela.

    .btn              a ação que a tela existe para fazer   — amarelo da marca
    .btn--escuro      confirmar um formulário               — preto
    .btn--fantasma    todo o resto                          — sem peso nenhum

  O amarelo é o `--bs-primary` (#FFD500) do tema da aplicação web; o preto e o
  fantasma espelham o par `btn-primary` / `btn-text-dark` que a web usa nos
  modais de solicitação.

  MAIÚSCULAS vêm do CSS, não do texto escrito na view: assim ninguém precisa
  lembrar da convenção ao criar uma tela nova, e o rótulo continua legível no
  código e para leitor de tela.
*/
.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-height: 56px;
  border: 1px solid transparent;   /* reserva o espaço da borda do fantasma */
  border-radius: var(--raio-pill);
  background: var(--amarelo);
  color: var(--preto);
  font-family: var(--fonte-display);
  text-transform: uppercase;
  letter-spacing: .3px;
  font-size: 19px;
  font-weight: 700;
  transition: transform .08s ease, filter .15s ease;
}
.btn:active { transform: scale(0.985); filter: brightness(0.95); }
.btn:disabled { background: var(--cinza-claro); color: var(--cinza-medio); }

.btn--escuro { background: var(--preto); color: var(--amarelo); }

/*
  Fantasma: nada em repouso, afordância só no toque.

  Ele carregava uma borda cinza permanente, o que lhe dava peso de botão de
  verdade e competia com a ação principal. Agora a borda existe mas é
  transparente — assim ela pode aparecer no toque sem deslocar 1px do layout,
  que é o defeito clássico de "adicionar borda no :active".

  Sem MAIÚSCULAS de propósito: é o que separa visualmente o secundário do
  principal antes mesmo de olhar a cor.
*/
.btn--fantasma {
  background: transparent;
  border-color: transparent;
  color: var(--texto);
  font-size: 15px;
  min-height: 46px;
  text-transform: none;
  letter-spacing: 0;
}
.btn--fantasma:active {
  border-color: var(--cinza-claro);
  background: var(--fundo);
  filter: none;
}

/*
  Perigo: modificador de cor, não um botão novo. Combina com .btn--fantasma nas
  ações que apagam dado do aparelho (descartar a fila offline) — vermelho no
  texto basta, porque a confirmação é quem realmente segura o dedo.
*/
.btn--perigo { color: var(--erro); }
.btn--perigo:active { border-color: var(--erro); }

.btn--pequeno { min-height: 42px; font-size: 15px; }

/* Largura pelo conteúdo — substitui os `style="width:auto;padding:0 22px"`
   espalhados pelas telas, que eram a marca de não haver padrão. */
.btn--auto { width: auto; padding: 0 24px; }

.btn__apoio {
  display: block;
  font-family: var(--fonte-corpo);
  font-size: 11px;
  font-weight: 400;
  opacity: .75;
}

/* -------------------------------------------------------------- formulário */

.campo { margin-bottom: 14px; }

.campo__rotulo {
  display: block;
  font-size: 12px;
  color: var(--texto-suave);
  margin-bottom: 6px;
  transition: color .12s ease;
}
/* O rótulo escurece junto com a borda, como no web (.form-control:focus ~ label
   vira #1A1600 lá). `:focus-within` no .campo em vez de um seletor de irmão
   porque aqui o rótulo vem ANTES do input no HTML. */
.campo:focus-within .campo__rotulo { color: var(--preto); }

.campo__entrada {
  width: 100%;
  min-height: 52px;
  padding: 0 14px;
  border: 1px solid var(--cinza-claro);
  border-radius: var(--raio);
  background: var(--branco);
  font-size: 16px; /* 16px evita o zoom automático do Safari no foco */
  outline: none;
  transition: border-color .12s ease, box-shadow .12s ease;
}

/*
  Placeholder um degrau abaixo do valor digitado. Ele herdava os 16px do input e
  ficava contra um rótulo de 12px logo acima — dois textos vizinhos com 4px de
  diferença, e cada campo virava mais um degrau na escada de tamanhos da tela.

  O tamanho vai no ::placeholder e NÃO no input: o iOS dá zoom automático ao
  focar campo com fonte abaixo de 16px, e o que ele mede é a fonte do input. O
  valor digitado continua em 16px.

  `opacity: 1` porque o Firefox aplica .54 por conta própria — o cinza já vem do
  token e não precisa de mais um véu por cima.
*/
.campo__entrada::placeholder {
  font-size: 14px;
  color: var(--cinza-medio);
  opacity: 1;
}

/*
  Foco do campo, no espírito do shadcn: a borda assume a cor do anel e um halo
  se abre em volta. Duas sombras em vez de `border-width: 2px` porque engrossar
  a borda encolhe a caixa de conteúdo em 1px de cada lado e o texto digitado dá
  um pulo no instante do foco — a primeira sombra dobra a borda por fora, sem
  mexer no layout.
*/
.campo__entrada:focus {
  border-color: var(--preto);
  box-shadow: 0 0 0 1px var(--preto), 0 0 0 4px rgba(26, 22, 0, .10);
}

/*
  Campo travado sai hachurado, igual ao web (.form-control:disabled). Cinza liso
  se confunde com campo vazio; a hachura diz "este campo existe e não é seu para
  editar" — que num app de ponto é a diferença entre um campo que a política
  bloqueou e um que ninguém preencheu ainda.

  Vale só para [disabled]: [readonly] fica de fora de propósito, porque o textarea
  do diagnóstico é readonly justamente para o usuário ler e copiar o conteúdo.
*/
.campo__entrada[disabled] {
  color: var(--cinza-medio);
  border-color: var(--cinza-claro);
  background: repeating-linear-gradient(45deg,
      var(--fundo), var(--fundo) 10px,
      var(--cinza-claro) 10px, var(--cinza-claro) 20px);
  cursor: not-allowed;
  opacity: .6;
}

/*
  Escolha de ambiente, no rodapé do login.

  Fica pequena, cinza e longe do formulário de propósito. É uma função
  essencial para quem dá suporte e ruído puro para o colaborador — que não
  deve ser convidado a trocar o servidor de onde bate o ponto. Mesmo lugar e
  mesmo peso visual que o app Flutter dá a ela.
*/
.rodape-login {
  margin-top: 32px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.ambiente-discreto {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border: 0;
  border-radius: var(--raio-pill);
  background: transparent;
  color: var(--cinza-medio);
  font-size: 12px;
  line-height: 1;
}
.ambiente-discreto svg { width: 14px; height: 14px; flex: none; }
.ambiente-discreto:active { background: rgba(0, 0, 0, .05); }

/*
  Select com a mesma caixa dos inputs. A seta é desenhada aqui porque a nativa
  varia demais entre Android, iOS e desktop e destoava da altura de 52px; o
  menu que abre ao tocar continua sendo o do sistema.
*/
.campo__select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 42px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234A6572' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 18px 18px;
}

/*
  Selo de ambiente. Só aparece fora de produção — em produção não há o que
  avisar, e um selo permanente vira ruído que ninguém mais lê.
*/
.selo-ambiente {
  display: inline-flex;
  align-items: center;
  margin-left: 8px;
  padding: 2px 8px;
  border-radius: var(--raio-pill);
  background: var(--preto);
  color: var(--amarelo);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .5px;
  white-space: nowrap;
}

.campo__senha { position: relative; }
.campo__senha .campo__entrada { padding-right: 48px; }
.campo__olho {
  position: absolute;
  right: 6px; top: 50%;
  transform: translateY(-50%);
  border: 0; background: transparent;
  width: 40px; height: 40px;
  display: grid; place-items: center;
  color: var(--texto-suave);
}

.erro-form {
  background: #FFF1F1;
  border: 1px solid var(--erro);
  color: #A63535;
  border-radius: var(--raio);
  padding: 10px 12px;
  font-size: 13px;
  margin-bottom: 14px;
}

/* ------------------------------------------------------- rádio / opções */

.opcoes { display: flex; gap: 18px; flex-wrap: wrap; padding-top: 2px; }

.opcao { display: flex; align-items: center; gap: 8px; cursor: pointer; }

/*
  Rádio em preto, pelo mesmo motivo do foco: em amarelo ele competia com a aba
  ativa e com o interruptor ligado, que também são amarelos — três controles
  diferentes dizendo "ligado" na mesma cor.

  Em repouso a borda é neutra, ao marcar fecha em preto: é o comportamento do
  shadcn, e resolve de brinde o problema de o rádio VAZIO já parecer preenchido
  por estar contornado na cor de destaque.
*/
.opcao input { appearance: none; -webkit-appearance: none; margin: 0; width: 20px; height: 20px; flex: none;
                border: 2px solid var(--cinza-medio); border-radius: 50%; display: grid; place-items: center;
                transition: border-color .12s ease; }
.opcao input::after { content: ''; width: 10px; height: 10px; border-radius: 50%; background: transparent;
                transition: background .12s ease; }
.opcao input:checked { border-color: var(--preto); }
.opcao input:checked::after { background: var(--preto); }

/* -------------------------------------------------------------- interruptor */

.interruptor { position: relative; display: inline-block; width: 46px; height: 26px; flex: none; }
.interruptor input { position: absolute; opacity: 0; width: 0; height: 0; }

.interruptor span {
  position: absolute;
  inset: 0;
  border-radius: 13px;
  background: var(--cinza-claro);
  transition: background .18s ease;
}
.interruptor span::before {
  content: '';
  position: absolute;
  left: 3px; top: 3px;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--branco);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .25);
  transition: transform .18s ease;
}
.interruptor input:checked + span { background: var(--amarelo); }
.interruptor input:checked + span::before { transform: translateX(20px); }

/* Preferência do aparelho: desliga o espelhamento da pré-visualização. */
.sem-espelho .camera__video,
.sem-espelho .totem__video { transform: none; }

/* ------------------------------------------------------- editor de eventos */

.caixa-eventos {
  border: 1px solid var(--cinza-claro);
  border-radius: var(--raio);
  padding: 12px;
  min-height: 96px;
}

.evento-linha {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
}
.evento-linha__tipo { width: 24px; flex: none; display: grid; place-items: center; }
.evento-linha__tipo svg { width: 22px; height: 22px; }
.evento-linha__hora { flex: 1; max-width: 130px; min-height: 44px; }
.evento-linha__hora[readonly] { background: var(--inputDisabled, #F5F5F5); color: var(--texto-suave); }

.linha-subtotal {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: var(--fundo);
  border-radius: 6px;
  padding: 8px 12px;
  margin-top: 10px;
  font-size: 13px;
}

.btn-adicionar {
  display: block;
  margin: 12px auto 0;
  border: 0;
  background: transparent;
  color: var(--preto);
  width: 40px; height: 40px;
  display: grid; place-items: center;
  border-radius: 50%;
}
.btn-adicionar:active { background: var(--fundo); }

/* --------------------------------------------------------------- progresso */

.barra {
  position: relative;
  height: 30px;
  border-radius: 20px;
  background: var(--fundo);
  overflow: hidden;
  flex: 1;
}
.barra__preenchimento { height: 100%; background: var(--amarelo-claro); transition: width .4s ease; }
.barra__texto {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  font-weight: 700; font-size: 13px;
}

.linha-hoje { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
.linha-hoje strong { font-size: 14px; }

/* ------------------------------------------------------------------ tabela */

.tabela { width: 100%; border-collapse: collapse; }
.tabela th {
  text-align: left;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--cinza-medio);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .5px;
  text-transform: uppercase;
}
.tabela td { padding: 10px 0; border-bottom: 1px solid var(--fundo); font-size: 13px; vertical-align: middle; }
.tabela tr:last-child td { border-bottom: 0; }
.tabela .col-centro { text-align: center; }

.evento-entrada { color: var(--sucesso); font-weight: 600; }
.evento-saida   { color: var(--erro);    font-weight: 600; }

/*
   Pastilhas de marcação — as mesmas do Dotte web (.reg_ponto_entrada e
   .reg_ponto_saida lá): entrada em amarelo claro, saída em cinza.

   Elas quebram linha dentro da célula em vez de a tabela abrir uma coluna fixa
   por batida. Num dia com 5, 6 ou 8 marcações — turno partido, saída para
   consulta, hora extra — todas aparecem. A tabela de 4 colunas escondia da
   quinta em diante, e o dia com mais movimento é justamente o que o
   colaborador precisa conferir.

   O cinza é o --cinza-claro do design system, não o rgba(38,43,67,.06) da web:
   a 6% de opacidade a pastilha some no cartão branco em tela de celular sob
   luz do dia.
*/
.marcas { display: flex; flex-wrap: wrap; gap: 4px; }

.marca {
  display: inline-block;
  min-width: 48px;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 12px;
  font-weight: 600;
  text-align: center;
  color: var(--preto);
  font-variant-numeric: tabular-nums;
}
.marca--entrada { background: var(--amarelo-claro); }
.marca--saida   { background: var(--cinza-claro); }

.rolagem-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* --------------------------------------------------------------- carrossel */

/*
  As setas flutuam sobre as bordas em vez de ocupar espaço na linha.

  Em flow elas comiam 56px de um card que tem 171px num iPhone, e sobrava tão
  pouco para o palco que a hora (32px) era cortada no último dígito. Fora do
  fluxo, o palco fica com a largura inteira do card.
*/
.carrossel { position: relative; display: flex; align-items: center; height: 100%; }
.carrossel__seta {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 1;
  border: 0; background: transparent; color: var(--cinza-medio);
  width: 22px; height: 22px; display: grid; place-items: center;
  transition: opacity .2s ease;
}
/* Encostadas na margem interna do card — o conteúdo é centrado e não chega lá. */
.carrossel__seta[data-carrossel-anterior] { left: -8px; }
.carrossel__seta[data-carrossel-proximo]  { right: -8px; }
/* Nos extremos a seta apaga em vez de sumir: o espaço fica reservado e nada
   pula de lugar quando ela volta a valer. */
.carrossel__seta[disabled] { opacity: .22; }

/*
  Slider de verdade: os itens ficam lado a lado num trilho e o que se move é o
  trilho. Antes cada item era `display:none` e o outro `block` — a troca era
  instantânea e não dava para dizer se tinha ido para a frente ou para trás.
*/
.carrossel__palco { flex: 1; min-width: 0; overflow: hidden; }
.carrossel__trilho {
  display: flex;
  will-change: transform;
  transition: transform .34s cubic-bezier(.22, .61, .36, 1);
}
.carrossel__item {
  flex: 0 0 100%;
  min-width: 0;
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .carrossel__trilho { transition: none; }
}
.carrossel__data { font-family: var(--fonte-num); font-size: 10px; color: var(--dourado); }
.carrossel__hora { font-family: var(--fonte-num); font-size: 32px; font-weight: 500; line-height: 1.1; }

.cartao-central {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  min-height: 150px; text-align: center;
}
.cartao-central__valor { font-family: var(--fonte-num); font-size: 32px; }

/* ------------------------------------------------------------- bottom nav */

/*
  Geometria do berço. O entalhe e o círculo são CONCÊNTRICOS: mesmo centro
  (--notch-x, --bolha-cy) e raios que diferem por --folga. Por isso --notch-r
  é derivado, nunca digitado à mão.

  A superfície é desenhada como path SVG (app.js monta o `d`), e não como
  retângulo com máscara circular. A diferença é justamente a que se vê: a
  máscara dá uma mordida abrupta; o path traça as curvas côncavas que ligam o
  entalhe ao resto da barra, como a BottomAppBar do Material.
*/
.nav {
  --notch-x: 50%;        /* centro da aba ativa; app.js atualiza */
  --bolha: 54px;         /* diâmetro do círculo flutuante */
  --folga: 8px;          /* respiro entre o círculo e a borda do entalhe */
  --bolha-cy: 6px;       /* centro do círculo, medido da borda de cima da barra */
  --notch-r: calc(var(--bolha) / 2 + var(--folga));

  /* Fora do fluxo, colada no fim do shell: a página corre por baixo dela e
     aparece dentro do entalhe. */
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  height: calc(var(--altura-nav) + var(--rodape));
  padding-bottom: var(--rodape);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

/*
  Fora do path não existe pixel algum — nem um retângulo transparente, nem uma
  máscara: o elemento simplesmente não pinta ali. Por isso o entalhe mostra a
  página rolando por baixo, e a sombra do path cai sobre esse conteúdo.
*/
.nav__forma {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  overflow: visible;
  filter: drop-shadow(0 -1px 3px rgba(0, 0, 0, .07));
  pointer-events: none;
}

.nav__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  color: var(--cinza-medio);
  font-size: 12px;
  position: relative;
}

.nav__icone { width: 26px; height: 26px; display: grid; place-items: center; transition: transform .2s ease; }
.nav__icone svg { width: 24px; height: 24px; }

.nav__item.ativo { color: var(--preto); font-weight: 600; }

/*
  A aba ativa sobe num círculo branco que flutua dentro do recorte, como o
  notch bar do app Flutter.

  O círculo sai do fluxo e é ancorado na borda de cima da barra — é o que
  garante que ele fique exatamente no centro do recorte. Um `::before` de mesmo
  tamanho toma o lugar do ícone no fluxo, então o rótulo não se move ao trocar
  de aba (antes o alinhamento dependia de margens negativas calculadas na mão,
  e qualquer mudança de fonte desalinhava tudo).
*/
.nav__item.ativo::before {
  content: '';
  width: 26px;
  height: 26px;
  flex: none;
}

.nav__item.ativo .nav__icone {
  position: absolute;
  left: 50%;
  top: calc(var(--bolha-cy) - var(--bolha) / 2);
  transform: translateX(-50%);
  width: var(--bolha);
  height: var(--bolha);
  margin: 0;
  border-radius: 50%;
  background: var(--branco);
  /* Sombra em duas camadas: uma difusa para destacar do fundo e uma curta,
     logo abaixo, para o botão parecer apoiado sobre o berço da barra. */
  box-shadow:
    0 6px 16px rgba(0, 0, 0, .16),
    0 2px 5px rgba(0, 0, 0, .12);
}

/* Navegadores sem máscara (raro): mantém a folga com um anel, sem buraco. */
@supports not ((-webkit-mask-image: radial-gradient(#000, #000)) or (mask-image: radial-gradient(#000, #000))) {
  .nav::before { -webkit-mask-image: none; mask-image: none; }
  .nav__item.ativo .nav__icone { box-shadow: 0 0 0 6px var(--fundo), 0 3px 10px rgba(0, 0, 0, .10); }
}

/* ------------------------------------------------------------------ modal */

.modal {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: none;
  background: rgba(0, 0, 0, .55);
  backdrop-filter: blur(2px);
}
.modal.aberto { display: block; }

.folha {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  background: var(--branco);
  border-radius: 20px 20px 0 0;
  padding: 8px var(--gap) calc(var(--gap) + var(--rodape));
  max-height: 92dvh;
  overflow-y: auto;
  animation: sobe .22s ease;
}
@keyframes sobe { from { transform: translateY(100%); } to { transform: translateY(0); } }

.folha__alca {
  width: 40px; height: 4px;
  border-radius: 2px;
  background: var(--cinza-claro);
  margin: 4px auto 14px;
}

.folha__opcao {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  min-height: 56px;
  padding: 0 6px;
  border: 0;
  border-bottom: 1px solid var(--fundo);
  background: transparent;
  font-size: 16px;
  text-align: left;
}
.folha__opcao:last-of-type { border-bottom: 0; }
.folha__opcao:active { background: var(--fundo); }
.folha__opcao svg { width: 22px; height: 22px; flex: none; color: var(--texto-suave); }
.folha__opcao--perigo { color: var(--erro); }
.folha__opcao--perigo svg { color: var(--erro); }

.folha__titulo {
  font-family: var(--fonte-display);
  font-size: 26px;
  font-weight: 700;
  text-align: center;
  line-height: 1.2;
  margin: 12px 0 8px;
}

/*
  Confirmação (Dotte.confirma) — o que substitui o `window.confirm` do
  navegador, que estampa o domínio do site e não aceita personalização.

  Centrada, e não bottom sheet como a .folha: a folha é para escolher entre
  opções, e o polegar sobe até ela. Aqui é uma decisão de sim/não que precisa
  ser LIDA antes de tocada — no centro do olhar, não na borda da tela.
*/
.confirmar {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(400px, calc(100vw - 40px));
  max-height: 88dvh;
  overflow-y: auto;
  background: var(--branco);
  border-radius: 18px;
  padding: 24px 20px 16px;
  animation: confirmar-entra .18s ease;
}

@keyframes confirmar-entra {
  from { opacity: 0; transform: translate(-50%, -46%); }
  to   { opacity: 1; transform: translate(-50%, -50%); }
}

.confirmar__titulo {
  margin: 0 0 8px;
  font-family: var(--fonte-display);
  font-size: 20px;
  font-weight: 700;
  line-height: 1.25;
}

.confirmar__texto {
  margin: 0 0 20px;
  font-size: 15px;
  line-height: 1.5;
  color: var(--texto-suave);
}

.confirmar__acoes { display: flex; flex-direction: column; gap: 6px; }

/* ------------------------------------------------------------ câmera/face */

.camera {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: none;
  background: #000;
}
.camera.aberto { display: block; }

.camera__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scaleX(-1); /* espelhado: o usuário vê o próprio reflexo */
}

/*
  Vinheta: escurece as bordas e realça o centro, onde o rosto deve ficar.
  Substitui o `box-shadow: 0 0 0 100vmax` que fazia esse papel antes — aquele
  pintava um retângulo de sombra do tamanho da viewport a cada quadro, e a
  borda dura denunciava a forma do alvo.
*/
.camera__escurecer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 64% 42% at 50% 44%, rgba(0, 0, 0, 0) 42%, rgba(0, 0, 0, .74) 100%),
    linear-gradient(to bottom, rgba(0, 0, 0, .5) 0, rgba(0, 0, 0, 0) 22%, rgba(0, 0, 0, 0) 62%, rgba(0, 0, 0, .72) 100%);
}

/*
  Alvo e anel: mesma geometria do modo stand (r=47 no viewBox de 100, daí o
  dasharray 295.3 = 2πr). Mantida idêntica de propósito — as duas telas
  resolvem o mesmo problema e não há motivo para divergirem.
*/
.camera__alvo {
  position: absolute;
  left: 50%;
  top: 44%;
  transform: translate(-50%, -50%) scale(1);
  width: min(72vw, 42vh);
  aspect-ratio: 1;
  pointer-events: none;
  transition: transform .25s ease;
}

/* `display:block` não é enfeite: como inline, o SVG assenta numa linha de
   texto e o descendente da fonte somava 3px à altura do alvo — o círculo saía
   281×284 em vez de quadrado, e o anel ficava levemente oval. */
.camera__anel { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }

.camera__anel-trilho,
.camera__anel-progresso {
  fill: none;
  stroke-width: 2.5;
  stroke-linecap: round;
}
.camera__anel-trilho {
  stroke: rgba(255, 255, 255, .32);
  transition: stroke .2s ease;
}
.camera__anel-progresso {
  stroke: var(--amarelo);
  stroke-dasharray: 295.3;
  stroke-dashoffset: 295.3;
  transition: stroke-dashoffset .12s linear, stroke .2s ease;
}

/* Estados — a cor comunica antes do texto. */
.camera--pronto .camera__anel-trilho { stroke: rgba(255, 255, 255, .45); }

.camera--achou .camera__anel-trilho { stroke: rgba(255, 212, 0, .45); }
.camera--achou .camera__alvo { transform: translate(-50%, -50%) scale(1.03); }

/* Enviando: o anel vira branco e gira. É o que diz "já capturei, estou
   falando com o servidor" — antes a câmera fechava e a espera acontecia numa
   tela parada, sem sinal nenhum de que algo estava em curso. */
.camera--enviando .camera__anel-progresso {
  stroke: #fff;
  stroke-dasharray: 74 221;
  stroke-dashoffset: 0;
  transition: none;
  transform-origin: 50% 50%;
  animation: camera-gira 1s linear infinite;
}
.camera--enviando .camera__anel-trilho { stroke: rgba(255, 255, 255, .25); }

@keyframes camera-gira {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .camera__alvo { transition: none; }
  .camera--enviando .camera__anel-progresso { animation-duration: 2.4s; }
}

/* Clarão da captura: faz a foto parecer tirada em vez de a tela congelar. */
.camera__flash {
  position: absolute;
  inset: 0;
  background: #fff;
  opacity: 0;
  pointer-events: none;
  z-index: 3;
}
.camera__flash.dispara { animation: camera-flash .32s ease-out; }

@keyframes camera-flash {
  0%   { opacity: .85; }
  100% { opacity: 0; }
}

.camera__topo {
  position: absolute;
  top: calc(var(--topo) + 12px);
  left: 12px; right: 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  z-index: 2;
}
.camera__fechar {
  width: 42px; height: 42px;
  border: 0; border-radius: 50%;
  background: rgba(0, 0, 0, .5);
  color: #fff;
  display: grid; place-items: center;
}

/*
  Instrução. Sem a barra de progresso embaixo (agora ela é o anel), o texto
  ganhou a faixa inferior inteira e pôde crescer — precisa ser lido de relance,
  com o rosto no enquadramento e não olhando para a legenda.
*/
.camera__status {
  position: absolute;
  left: 20px; right: 20px;
  bottom: calc(var(--rodape) + 44px);
  text-align: center;
  color: #fff;
  font-family: var(--fonte-display);
  font-size: 19px;
  font-weight: 700;
  line-height: 1.35;
  letter-spacing: -0.2px;
  text-shadow: 0 1px 10px rgba(0, 0, 0, .7);
  z-index: 2;
  transition: color .2s ease;
}
.camera--achou .camera__status { color: var(--amarelo); }

/* ------------------------------------------------------------------ avisos */

.aviso {
  border-radius: var(--raio);
  padding: 10px 12px;
  font-size: 13px;
  margin-bottom: var(--gap);
  display: flex;
  gap: 8px;
  align-items: flex-start;
}
.aviso--offline { background: #FFF6D6; border: 1px solid var(--amarelo-escuro); }
.aviso--erro    { background: #FFF1F1; border: 1px solid var(--erro); color: #A63535; }
.aviso--info    { background: #EEF4F7; border: 1px solid #CFE0E8; }

.faixa-offline {
  position: fixed;
  left: 0; right: 0;
  top: 0;
  z-index: 80;
  padding: calc(var(--topo) + 6px) 12px 6px;
  background: var(--preto);
  color: var(--amarelo);
  font-size: 12px;
  text-align: center;
  transform: translateY(-120%);
  transition: transform .25s ease;
}
.faixa-offline.visivel { transform: translateY(0); }

.toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--altura-nav) + var(--rodape) + 16px);
  transform: translate(-50%, 20px);
  z-index: 90;
  max-width: calc(100% - 32px);
  padding: 12px 16px;
  border-radius: var(--raio);
  background: var(--preto);
  color: #fff;
  font-size: 13px;
  opacity: 0;
  pointer-events: none;
  transition: opacity .2s ease, transform .2s ease;
}
.toast.visivel { opacity: 1; transform: translate(-50%, 0); }
.toast--erro { background: #8C2F2F; }

/* ---------------------------------------------------------------- listas */

.lista-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 0;
  border-bottom: 1px solid var(--fundo);
}
.lista-item:last-child { border-bottom: 0; }
.lista-item__titulo { font-weight: 600; font-size: 14px; }
.lista-item__apoio { font-size: 12px; color: var(--texto-suave); margin-top: 2px; }

.selo {
  font-size: 11px;
  font-weight: 700;
  padding: 4px 10px;
  border-radius: 12px;
  white-space: nowrap;
}
.selo--pendente  { background: #FFF3CD; color: #8A6A00; }
.selo--aprovado  { background: #E5F5E6; color: #2E7D32; }
.selo--rejeitado { background: #FFE9E9; color: #A63535; }

.dado { display: flex; justify-content: space-between; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--fundo); }
.dado:last-child { border-bottom: 0; }
.dado__rotulo { font-weight: 500; }
.dado__valor { color: var(--texto-suave); text-align: right; }

/* ------------------------------------------------------------------ chips */

.chips {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 4px;
  margin-bottom: var(--gap);
  scrollbar-width: none;
}
.chips::-webkit-scrollbar { display: none; }

.chip {
  flex: none;
  padding: 8px 16px;
  border-radius: 20px;
  background: var(--branco);
  border: 1px solid var(--cinza-claro);
  font-size: 13px;
  white-space: nowrap;
}
.chip.ativo { background: var(--preto); border-color: var(--preto); color: var(--branco); font-weight: 600; }

/* --------------------------------------------------------------- paginador */

.paginador {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-bottom: var(--gap);
}
.paginador__pilula {
  background: #7A7A7A;
  color: #fff;
  padding: 8px 22px;
  border-radius: 20px;
  font-weight: 700;
  font-size: 14px;
  text-transform: uppercase;
  min-width: 150px;
  text-align: center;
}
.paginador button { border: 0; background: transparent; color: var(--cinza-medio); padding: 8px; }

/* ------------------------------------------------------------------ perfil */

.perfil-topo {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 8px 0 20px;
}
/*
   O placeholder é o fundo do próprio avatar, não um elemento que entra quando a
   foto falha: o furo já tem o tamanho final no primeiro quadro (sem salto de
   layout enquanto a imagem baixa) e uma foto que não carrega — offline, URL
   morta, servidor de mídia fora — degrada para a silhueta em vez do glifo de
   imagem quebrada do WebView, que desenha junto o texto do alt e estoura o
   círculo. A foto entra por cima e se apaga sozinha no onerror.
*/
.avatar {
  --avatar-tam: 72px;
  position: relative;
  width: var(--avatar-tam);
  height: var(--avatar-tam);
  flex: none;
  border-radius: 50%;
  overflow: hidden;
  background: var(--cinza-medio);
  color: var(--branco);
  display: grid;
  place-items: center;
}
.avatar--64 { --avatar-tam: 64px; }
.avatar--96 { --avatar-tam: 96px; }

/* Sem isto o <svg> cai no tamanho padrão de elemento substituído (300x150) e
   vira uma elipse deitada dentro do círculo. */
.avatar__icone { width: 62%; height: 62%; }

.avatar__iniciais {
  font-family: var(--fonte-display);
  font-weight: 700;
  font-size: calc(var(--avatar-tam) * .36);
  line-height: 1;
}

.avatar__foto {
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.avatar__foto[hidden] { display: none; }

.avatar-botao {
  position: relative;
  border: 0;
  background: transparent;
  padding: 0;
  flex: none;
  line-height: 0;
  border-radius: 50%;
}

.avatar-lapis {
  position: absolute;
  right: -2px; bottom: -2px;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--amarelo);
  color: var(--preto);
  display: grid;
  place-items: center;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .18);
}
.avatar-lapis svg { width: 14px; height: 14px; }

.perfil-topo__nome { font-family: var(--fonte-display); font-weight: 700; font-size: 18px; }
.perfil-topo__email { font-size: 13px; color: var(--texto-suave); word-break: break-all; }

/* ------------------------------------------------------------------ drawer */

.drawer {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: none;
}
.drawer.aberto { display: block; }
.drawer__fundo { position: absolute; inset: 0; background: rgba(0, 0, 0, .45); }
.drawer__painel {
  position: absolute;
  top: 0; bottom: 0; left: 0;
  width: min(78vw, 320px);
  background: var(--branco);
  padding: calc(var(--topo) + 20px) 20px calc(var(--rodape) + 20px);
  display: flex;
  flex-direction: column;
  gap: 4px;
  animation: entra-esq .2s ease;
  overflow-y: auto;
}
@keyframes entra-esq { from { transform: translateX(-100%); } to { transform: translateX(0); } }

.drawer__link {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 8px;
  border-radius: var(--raio);
  font-size: 15px;
}
.drawer__link:active { background: var(--fundo); }
.drawer__link svg { width: 20px; height: 20px; flex: none; }
.drawer__sep { height: 1px; background: var(--fundo); margin: 8px 0; }

/* ------------------------------------------------------------------- login */

.login {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: calc(var(--topo) + 32px) 24px calc(var(--rodape) + 32px);
  background:
    radial-gradient(circle at 50% 0%, rgba(255, 212, 0, 0) 30%, rgba(255, 212, 0, .25) 100%),
    var(--fundo);
  max-width: 480px;
  margin: 0 auto;
}
.login__marca {
  font-family: var(--fonte-display);
  font-weight: 700;
  font-size: 40px;
  text-align: center;
  letter-spacing: -1px;
  margin-bottom: 4px;
}
.login__sub { text-align: center; color: var(--texto-suave); margin-bottom: 28px; }

/* ------------------------------------------------------------------ splash */

.splash {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  background: var(--amarelo);
  transition: opacity .4s ease;
}
.splash.saindo { opacity: 0; pointer-events: none; }

/* Ícone estático: usado quando a animação já rodou nesta sessão ou falhou. */
.splash__icone { width: 132px; height: 132px; }

.splash__lottie {
  width: min(84vw, 460px);
  aspect-ratio: 2 / 1;   /* a composição é 1000×500 */
}

/* Com a animação, o ícone estático sai de cena. */
.splash.com-lottie .splash__icone { display: none; }
.splash.sem-animacao .splash__lottie { display: none; }

/* ------------------------------------------------------------- diagnóstico */

.diag-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 0;
  border-bottom: 1px solid var(--fundo);
}
.diag-item:last-child { border-bottom: 0; }
.diag-item__pino {
  width: 10px; height: 10px;
  border-radius: 50%;
  margin-top: 5px;
  flex: none;
  background: var(--cinza-medio);
}
/* Modificadores com o prefixo do bloco: `.aviso` solto é a caixa de alerta
   global, e o pino herdava o padding dela. */
.diag-item__pino--ok    { background: var(--sucesso); }
.diag-item__pino--aviso { background: var(--amarelo-escuro); }
.diag-item__pino--falha { background: var(--erro); }
.diag-item__corpo { flex: 1; min-width: 0; }
.diag-item__titulo { display: block; font-weight: 600; font-size: 14px; }
.diag-item__detalhe { display: block; font-size: 12px; color: var(--texto-suave); word-break: break-word; margin-top: 2px; }

/* -------------------------------------------------------------- utilidades */

.oculto { display: none !important; }
.centro { text-align: center; }
.pilha > * + * { margin-top: 12px; }
.espacador { height: 12px; }

.girando {
  width: 22px; height: 22px;
  border: 2px solid rgba(0, 0, 0, .15);
  border-top-color: var(--preto);
  border-radius: 50%;
  animation: gira .7s linear infinite;
  margin: 20px auto;
}
@keyframes gira { to { transform: rotate(360deg); } }

/* ------------------------------------------------------------- aguardando */

/*
  Véu de espera para qualquer ida ao servidor.

  A animação é só `transform: rotate`, de propósito: assim ela roda no
  compositor e continua girando mesmo quando a thread principal está ocupada —
  que é exatamente quando o usuário mais precisa ver que algo está acontecendo.
  Um spinner desenhado por JS congelaria junto com a tela.
*/
.aguarde {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  background: rgba(245, 245, 245, .72);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
.aguarde.visivel { display: flex; animation: aguarde-entra .18s ease; }

@keyframes aguarde-entra { from { opacity: 0; } to { opacity: 1; } }

.aguarde__roda {
  width: 42px; height: 42px;
  border: 3px solid rgba(0, 0, 0, .12);
  border-top-color: var(--amarelo-escuro);
  border-radius: 50%;
  animation: gira .7s linear infinite;
}

.aguarde__texto {
  font-family: var(--fonte-display);
  font-weight: 600;
  font-size: 14px;
  color: var(--texto-suave);
  text-align: center;
  padding: 0 32px;
  margin: 0;
}

/* ------------------------------------------------- botão em espera */

/*
  O botão continua legível: some só o rótulo, e a roda entra no lugar. Trocar
  o texto por "AGUARDE" faria a largura pular; a roda ocupa espaço fixo.
*/
.btn--ocupado { pointer-events: none; }
.btn--ocupado::before {
  content: '';
  width: 20px; height: 20px;
  border: 2px solid rgba(0, 0, 0, .25);
  border-top-color: var(--preto);
  border-radius: 50%;
  animation: gira .7s linear infinite;
  flex: none;
}
.btn--ocupado.btn--escuro::before { border-color: rgba(255, 255, 255, .3); border-top-color: #fff; }

/* ------------------------------------------------- permissões (primeiro uso) */

/*
  Tela cheia, e não um modal por cima da home.

  O diálogo do navegador aparece colado ao topo (Android) ou no meio da tela
  (iOS) sem dizer por que está sendo pedido. Se o fundo for a home, o usuário lê
  "quer acessar sua câmera?" enquanto olha um relógio de ponto — e nega. Ocupar
  a tela inteira faz o texto do app ser a única coisa lida antes do diálogo.

  Fica abaixo do splash (z-index 100) de propósito: quem controla a ordem é o
  JS, mas se algo falhar, é o splash que ganha — ele sempre sai sozinho.
*/
.permissoes {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: none;
  background: var(--fundo);
  overflow-y: auto;
  padding: calc(var(--topo) + 24px) var(--gap) calc(var(--rodape) + 24px);
}
.permissoes.aberto { display: block; }

.permissoes__caixa {
  width: min(420px, 100%);
  margin: 0 auto;
  min-height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.permissoes__icone {
  display: grid;
  place-items: center;
  width: 96px; height: 96px;
  margin: 8px auto 20px;
  border-radius: 50%;
  background: var(--amarelo-claro);
  color: var(--dourado);
}
.permissoes__icone svg { width: 44px; height: 44px; }

.permissoes__titulo {
  font-family: var(--fonte-display);
  font-size: 26px;
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  margin: 0 0 10px;
}

.permissoes__texto {
  text-align: center;
  font-size: 15px;
  line-height: 1.5;
  color: var(--texto-suave);
  margin: 0 0 24px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}
