/* ==========================================================================
   A CELEBRAÇÃO — a camada do momento
   O app ganha dinheiro o tempo todo e comemorava no canto da tela. Aqui o
   ganho grande para no meio, com peso, e sai sozinho.

   Três coisas moram neste arquivo:
     1. o cartão da celebração   (.celebra*)   — presente, nível novo, dobro
     2. o presente               (.presente*)  — a caixa que flutua e abre
     3. o apoio                  (.moeda-voa, .feed-sacar-destravou)

   TRÊS REGRAS QUE VALEM PARA O ARQUIVO INTEIRO:

   - NADA AQUI ROUBA O TOQUE. A camada é `pointer-events: none` e só o cartão
     recebe dedo. Com a celebração aberta o feed continua deslizando, o vídeo
     continua respondendo e o botão de sacar continua clicável por baixo. Uma
     comemoração que trava a tela é um modal com outro nome.

   - O ESTADO PARADO É O ESTADO FINAL. Todo elemento nasce visível e no lugar;
     a animação só descreve de ONDE ele vem. Assim, desligar o movimento não
     apaga nada — a pessoa lê o mesmo valor, o mesmo selo e o mesmo botão.
     O bloco de `prefers-reduced-motion` no fim só corta laço e movimento.

   - COR SÓ POR TOKEN. A raridade entra como classe (`celebra-raro`) e vira
     `--grau`, que é a única variável de cor daqui. Verde continua sendo só
     dinheiro; ouro continua sendo só conquista.
   ========================================================================== */

/* ============================================================ 1. O CARTÃO */

.celebra {
  position: fixed;
  inset: 0;
  z-index: 62;                 /* acima do toast (60), abaixo do modal (70) */
  display: grid;
  place-items: center;
  /* O recuo de cima é a barra do feed (saldo e Sacar) e o de baixo é o rodapé
     com a barra de navegação: a celebração nasce no meio do que sobra, nunca
     por cima do dinheiro nem do botão de saque. */
  padding:
    calc(env(safe-area-inset-top, 0px) + 116px) 20px
    calc(env(safe-area-inset-bottom, 0px) + var(--nav-h, 64px) + 96px);
  pointer-events: none;
}

/* O véu escurece a tela em volta sem bloquear nada: ele existe para o cartão
   ter contraste, não para prender a pessoa. */
.celebra-veu {
  position: absolute;
  inset: 0;
  opacity: 0;
  background: radial-gradient(120% 90% at 50% 45%,
    color-mix(in srgb, var(--grau) 22%, transparent) 0%,
    rgba(0, 0, 0, .55) 62%,
    rgba(0, 0, 0, .74) 100%);
  transition: opacity var(--tempo-medio) var(--curva);
}

.celebra.visivel .celebra-veu { opacity: var(--veu, .5); }

/* O cartão TAMBÉM deixa o toque passar. Ele nasce bem no meio da tela, que é
   onde o polegar desliza: se ele pegasse o dedo, um deslize começado em cima
   dele não passaria a campanha — e "não trava o feed" viraria mentira. Só os
   botões de decisão recebem toque; tocar em qualquer outro lugar fecha, que é
   o que já se espera de uma comemoração. */
.celebra-cartao {
  position: relative;
  pointer-events: none;
  width: min(92vw, calc(300px * var(--fator, 1)));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e-2);
  padding: var(--e-6) var(--e-5) var(--e-5);
  border-radius: var(--raio-gg);
  background:
    radial-gradient(120% 80% at 50% 0%,
      color-mix(in srgb, var(--grau) 26%, transparent) 0%, transparent 62%),
    var(--superficie);
  border: 1px solid color-mix(in srgb, var(--grau) 40%, transparent);
  box-shadow:
    var(--sombra-alta),
    0 0 0 1px rgba(255, 255, 255, .04),
    0 10px 60px color-mix(in srgb, var(--grau) 30%, transparent);
  text-align: center;
  overflow: hidden;
  animation: celebra-entra 460ms var(--curva-mola);
  transition: opacity var(--tempo-curto) var(--curva-saida),
              transform var(--tempo-curto) var(--curva-saida);
}

/* A saída: some encolhendo. Fica no `:not(.visivel)` porque é o mesmo estado
   que o fechamento liga — e o cartão só entra no documento junto com a classe,
   então isto nunca pisca na entrada. */
.celebra:not(.visivel) .celebra-cartao {
  opacity: 0;
  transform: scale(.94) translateY(10px);
}

/* A luz que escapa por trás do cartão. Só acende do raro para cima: é uma das
   coisas que separam o prêmio grande do miúdo. */
.celebra-luz {
  position: absolute;
  left: 50%;
  top: -40%;
  width: 200%;
  aspect-ratio: 1;
  transform: translateX(-50%);
  opacity: 0;
  pointer-events: none;
  background: conic-gradient(from 0deg,
    transparent 0deg, color-mix(in srgb, var(--grau) 42%, transparent) 12deg,
    transparent 24deg, transparent 42deg,
    color-mix(in srgb, var(--grau) 30%, transparent) 54deg, transparent 66deg,
    transparent 90deg, color-mix(in srgb, var(--grau) 42%, transparent) 102deg,
    transparent 114deg, transparent 140deg,
    color-mix(in srgb, var(--grau) 30%, transparent) 152deg, transparent 164deg,
    transparent 190deg, color-mix(in srgb, var(--grau) 42%, transparent) 202deg,
    transparent 214deg, transparent 250deg,
    color-mix(in srgb, var(--grau) 30%, transparent) 262deg, transparent 274deg,
    transparent 300deg, color-mix(in srgb, var(--grau) 42%, transparent) 312deg,
    transparent 324deg, transparent 360deg);
  mask-image: radial-gradient(closest-side, #000 20%, transparent 78%);
  -webkit-mask-image: radial-gradient(closest-side, #000 20%, transparent 78%);
}

.celebra-emblema {
  display: grid;
  place-items: center;
  width: calc(56px * var(--fator, 1));
  height: calc(56px * var(--fator, 1));
  border-radius: 50%;
  color: var(--grau);
  background: color-mix(in srgb, var(--grau) 18%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--grau) 34%, transparent);
}

.celebra-emblema .icone { width: 60%; height: 60%; }

.celebra-selo {
  font-size: var(--txt-mini);
  font-weight: var(--peso-extra);
  letter-spacing: var(--letra-rotulo);
  text-transform: uppercase;
  color: var(--grau);
  background: color-mix(in srgb, var(--grau) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--grau) 38%, transparent);
  border-radius: var(--raio-pill);
  padding: 3px 10px;
}

.celebra-titulo {
  margin: 0;
  font-size: calc(var(--txt-forte) * var(--fator, 1));
  font-weight: var(--peso-extra);
  line-height: var(--altura-apertada);
  letter-spacing: var(--letra-titulo);
  color: var(--texto);
  text-wrap: balance;
}

/* Verde é dinheiro, e dinheiro é o que a pessoa veio ver: é o maior número do
   cartão em qualquer degrau. */
.celebra-valor {
  font-size: calc(var(--txt-valor) * var(--fator, 1));
  font-weight: var(--peso-extra);
  line-height: 1;
  color: var(--verde);
  font-variant-numeric: tabular-nums;
}

.celebra-apoio {
  margin: 0;
  font-size: var(--txt-nota);
  line-height: var(--altura-normal);
  color: var(--texto-fraco);
  text-wrap: pretty;
}

.celebra-acoes {
  display: flex;
  gap: var(--e-2);
  width: 100%;
  margin-top: var(--e-2);
}

.celebra-acoes .btn {
  flex: 1;
  min-width: 0;
  pointer-events: auto;
  padding-inline: var(--e-3);
  white-space: nowrap;
}

/* Cartão com decisão é cartão largo: "Agora não" quebrando em duas linhas faz
   a saída parecer menos válida que a entrada. */
.celebra-cartao:has(.celebra-acoes) { width: min(92vw, 324px); }

/* ---------------------------------------------------------- Os degraus
   Cinco degraus, e o que muda entre eles é o TAMANHO, a LUZ e o VÉU. O resto
   (confete, tremor, tempo em pé) é decidido em lib/fx.js, no mesmo lugar em
   que se decide a contagem. */

.celebra-comum    { --grau: var(--texto-3);     --fator: .78; --veu: .30; }
.celebra-incomum  { --grau: var(--acento-terra); --fator: .88; --veu: .40; }
.celebra-raro     { --grau: var(--acento-vinho);  --fator: 1;   --veu: .55; }
.celebra-epico    { --grau: var(--marca);       --fator: 1.1; --veu: .68; }
.celebra-lendario { --grau: var(--amarelo);     --fator: 1.2; --veu: .82; }

/* A luz gira só do raro para cima, e gira mais depressa quanto mais raro. */
.celebra-raro .celebra-luz     { opacity: .40; animation: celebra-gira 14s linear infinite; }
.celebra-epico .celebra-luz    { opacity: .55; animation: celebra-gira 10s linear infinite; }
.celebra-lendario .celebra-luz { opacity: .72; animation: celebra-gira 7s linear infinite; }

/* O lendário ainda ganha um anel que respira por fora do cartão. */
.celebra-lendario .celebra-cartao {
  animation: celebra-entra 460ms var(--curva-mola), celebra-pulsa-anel 2.4s ease-in-out 460ms infinite;
}

.celebra-treme { animation: celebra-entra 460ms var(--curva-mola), celebra-treme 520ms ease-in-out 300ms; }

.celebra-lendario .celebra-treme {
  animation:
    celebra-entra 460ms var(--curva-mola),
    celebra-treme 640ms ease-in-out 300ms,
    celebra-pulsa-anel 2.4s ease-in-out 940ms infinite;
}

@keyframes celebra-entra {
  0%   { opacity: 0; transform: scale(.82) translateY(18px); }
  62%  { opacity: 1; transform: scale(1.03) translateY(0); }
  100% { opacity: 1; transform: scale(1); }
}

@keyframes celebra-gira {
  to { transform: translateX(-50%) rotate(360deg); }
}

@keyframes celebra-treme {
  0%, 100% { translate: 0 0; }
  12% { translate: -6px 0; }
  26% { translate: 6px 0; }
  40% { translate: -4px 0; }
  54% { translate: 4px 0; }
  70% { translate: -2px 0; }
  86% { translate: 2px 0; }
}

@keyframes celebra-pulsa-anel {
  0%, 100% { box-shadow: var(--sombra-alta), 0 0 0 1px rgba(255,255,255,.04), 0 10px 60px color-mix(in srgb, var(--grau) 30%, transparent); }
  50%      { box-shadow: var(--sombra-alta), 0 0 0 3px color-mix(in srgb, var(--grau) 46%, transparent), 0 14px 80px color-mix(in srgb, var(--grau) 52%, transparent); }
}

/* ========================================================== 2. O PRESENTE */

/* A caixa mora na lateral esquerda, abaixo da barra do topo: longe do play do
   meio, longe da coluna de ações da direita e longe do texto do rodapé. Ela
   pede toque sem cobrir o que a pessoa está assistindo. */
.presente-zona {
  position: absolute;
  left: 12px;
  top: calc(env(safe-area-inset-top, 0px) + 124px);
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--e-2);
  pointer-events: none;
}

/* A ZONA DO PRESENTE GANHOU CORPO.
   Ela era um botão solto sobre o vídeo, e por isso lia como enfeite de canto.
   Agora tem fundo, borda e o nome embaixo: é uma seção da tela, do tamanho da
   importância que ela tem — é o que faz a pessoa continuar assistindo. */
.presente-btn {
  pointer-events: auto;
  border-radius: var(--raio-g);
  background: rgba(0, 0, 0, .34);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  padding: 8px 10px 6px;
  border: 0;
  background: none;
  cursor: pointer;
  display: grid;
  /* Anel e caixa dividem a MESMA célula, um por cima do outro; o rótulo desce
     para a linha de baixo. Empilhar com position:absolute daria o mesmo desenho
     e tiraria a caixa do fluxo — e aí o botão perderia a altura, junto com a
     área de toque. */
  grid-template-areas: 'disco' 'rotulo';
  /* `place-items` e não só `justify-items`: a caixa tem altura própria e o anel
     é maior que ela, então sem o alinhamento vertical ela cai no COMEÇO da
     célula — encostada na borda de cima do anel, em vez de no meio dele. */
  place-items: center;
  gap: 4px;
  min-width: var(--toque-min-compacto);
  min-height: var(--toque-min-compacto);
  font-family: inherit;
}

.presente-anel,
.presente-btn .presente-caixa { grid-area: disco; }

/* A caixa dentro do anel: menor que ele, para o fio correr por fora sem tocar no
   desenho. Com o desenho em SVG o laço já cabe dentro da própria área — ele era
   um elemento por fora antes, e por isso a caixa precisava ser menor do que
   deveria para o laço não atravessar o anel. */
.presente-btn .presente-caixa { --caixa: 34px; }
.presente-rotulo { grid-area: rotulo; }

/* Sem texto, sem linha. O rótulo fica vazio o tempo todo desde que o nome saiu
   da tela — só volta a ter conteúdo quando a caixa enche e ele diz "Abrir". Sem
   isto, o `gap` do grid reservaria a linha vazia e a caixa ficaria pendurada
   acima do centro do próprio botão. */
.presente-rotulo:empty { display: none; }

/* ---- o anel do tempo de uso -------------------------------------------
   Um fio fino em volta da caixa, que enche conforme a pessoa assiste e
   responde. Ele substituiu a frase "Presente em 3 tarefas": progresso que anda
   à vista diz o mesmo sem exigir leitura, e diz enquanto acontece. */

/* O ANEL É EXTERNO À CAIXA, e maior que ela.
   Ele encostava na caixa e os dois desenhos brigavam: de longe, a caixa parecia
   ter uma borda torta em vez de ter um medidor em volta. Afastando o anel e
   dando folga entre ele e a caixa, os dois passam a ser duas coisas — a caixa é
   o prêmio, o anel é quanto falta —, e cada uma se lê sozinha. */
/* A ESCALA CAIU POR PEDIDO DO DONO. O conjunto tinha 74px de anel e 48 de
   caixa, e com o nome embaixo ocupava um quadrado de uns 100px sobre o vídeo.
   Em 56/34 ele continua legível a um palmo de distância e volta a ser o que
   deveria ser: um medidor no canto, não um cartão. A área de TOQUE não encolheu
   junto — quem manda nela é `--toque-min-compacto` no botão. */
.presente-anel {
  width: 56px;
  height: 56px;
  /* Começa às 12 horas e anda no sentido do relógio. Sem isto o SVG começaria
     às 3 horas, que não é como ninguém lê progresso circular. */
  transform: rotate(-90deg);
  overflow: visible;
}

.presente-anel-trilho,
.presente-anel-fio {
  fill: none;
  stroke-linecap: round;
  /* Mais grosso do que era: com o anel afastado da caixa, um fio de 2,5px lido a
     dois palmos de distância some. Ele é o que diz quanto falta — precisa ser
     visto do outro lado da tela.
     O número é em unidades do viewBox (60), não em pixels: com o anel menor a
     escala caiu junto, e 4,5 aqui devolve na tela a espessura que 4 tinha em
     74px. */
  stroke-width: 4.5;
}

.presente-anel-trilho { stroke: rgba(255, 255, 255, .18); }

.presente-anel-fio {
  stroke: var(--verde-cheio, #1DB954);
  filter: drop-shadow(0 0 6px rgba(29, 185, 84, .6));
  /* A transição é o que faz o anel ANDAR em vez de saltar: o tempo é gravado a
     cada segundo, e sem ela cada gravação seria um pulo visível. */
  transition: stroke-dashoffset 900ms linear;
}

/* Cheio: o fio fica dourado e a caixa acende. A cor muda porque "pronto" não é
   "quase pronto" com mais um pedaço — é outro estado, e outro estado precisa de
   outra aparência, não de mais do mesmo. */
.presente-pronto .presente-anel-fio {
  stroke: var(--ouro, #F5C451);
  filter: drop-shadow(0 0 8px rgba(245, 196, 81, .75));
}

/* Quem balança é o DESENHO, não o botão: alvo de toque que não para quieto é
   alvo que escapa do dedo. A área continua parada; a caixa é que se mexe. */
.presente-btn.presente-pronto .presente-caixa {
  animation: presente-balanca 3.2s ease-in-out infinite;
}

/* A CAIXA VAZIA TAMBÉM SE MEXE, e mais devagar.
   Ela ficava parada e apagada, e por isso não era percebida como coisa — era
   desenho de canto. Um respiro lento dá identidade a ela sem prometer o que
   não existe: quem já viu a caixa cheia reconhece a diferença na hora, porque
   cheia ela balança em 3,2s e vazia ela só respira em 4,8s. */
.presente-btn:not(.presente-pronto) .presente-caixa {
  animation: presente-respira-vazia 4.8s ease-in-out infinite;
  opacity: .88;
}

@keyframes presente-respira-vazia {
  0%, 100% { transform: scale(1) translateY(0); }
  50%      { transform: scale(1.05) translateY(-2px); }
}

.presente-btn:active { transform: scale(.92); }

/* ---- a folha que explica a caixa -------------------------------------
   Aberta ao tocar na caixa VAZIA. Existe porque uma caixa de presente no canto
   da tela é uma pergunta, e pergunta sem resposta vira ruído — a pessoa toca
   uma vez, nada acontece, e nunca mais volta ali. */
.presente-explica {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--e-3);
  padding-bottom: var(--e-3);
}

.presente-explica-caixa {
  display: block;
  transform: scale(1.5);
  margin: var(--e-4) 0 var(--e-5);
}

/* A barra e a porcentagem numa linha só. Embaixo, o número virava uma terceira
   frase no meio de outras duas e a barra ficava sem legenda do lado. */
.presente-explica-linha {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  width: 100%;
}

.presente-explica-barra {
  flex: 1;
  height: 8px;
  border-radius: 999px;
  background: color-mix(in oklab, var(--texto) 12%, transparent);
  overflow: hidden;
}

.presente-explica-barra span {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--marca-2), var(--marca));
  transition: width 600ms var(--curva, ease);
}

.presente-explica-conta {
  margin: 0;
  font-size: var(--txt-nota);
  font-weight: var(--peso-forte);
  color: var(--marca-texto);
  font-variant-numeric: tabular-nums;
  /* Largura reservada para os três dígitos: sem ela, a barra encolhe um pixel
     a cada vez que a porcentagem passa de 9 para 10 e de 99 para 100. */
  min-width: 4ch;
  text-align: right;
}

/* ---- o pulo do fim do vídeo -------------------------------------------
   Disparado quando uma campanha termina e a caixa já está cheia. É o único
   momento em que ela sai do lugar, e é o momento certo: a pessoa acabou de ver
   dinheiro entrar e está olhando para a tela. */
.presente-pula {
  animation: presente-pula 900ms cubic-bezier(.2, .9, .3, 1) 2;
}

@keyframes presente-pula {
  0%   { transform: translateY(0) scale(1); }
  22%  { transform: translateY(-16px) scale(1.12); }
  42%  { transform: translateY(0) scale(.94); }
  58%  { transform: translateY(-7px) scale(1.04); }
  76%  { transform: translateY(0) scale(.99); }
  100% { transform: translateY(0) scale(1); }
}

.presente-rotulo {
  font-size: var(--txt-mini);
  font-weight: var(--peso-forte);
  color: #fff;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 1px 4px rgba(0, 0, 0, .85);
}

/* ---- a caixa desenhada. Nada de imagem: são dois blocos e duas fitas. ---- */

.presente-caixa {
  position: relative;
  display: block;
  width: var(--caixa, 52px);
  height: var(--caixa, 52px);
}

/* O VERMELHO DA CAIXA TEM TOKEN PRÓPRIO, e não é o `--vermelho-cheio` do app.
   Aquele é o âmbar de erro (#B4540A), escolhido para carregar texto branco numa
   mensagem de falha — usado aqui, a caixa saía marrom-alaranjada, que é a cor de
   um aviso, não a de um presente. Estes dois só existem para este desenho. */
:root {
  --presente-vermelho: #D8262C;
  --presente-vermelho-2: #9E1319;
}

/* A CAIXA É UM SVG desde 18/08/2026.
   Antes eram cinco `<span>` empilhados com `border-radius` de quatro números por
   canto, ajustados no olho até parecerem um laço. Nunca ficaram simétricos: cada
   acerto de um lado desencontrava o outro, e a caixa saía torta em toda tela.
   Aqui a alça direita é a esquerda espelhada, e não existe lado para
   desencontrar. */
.presente-svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.p-corpo {
  fill: var(--presente-vermelho);
}

.p-tampa {
  fill: color-mix(in srgb, var(--presente-vermelho) 88%, white);
}

.p-fita {
  fill: var(--marca);
}

.p-alca {
  fill: var(--marca-3);
  stroke: var(--marca-2);
  stroke-width: .6;
}

.p-no {
  fill: var(--marca);
  stroke: var(--marca-2);
  stroke-width: .6;
}

/* Pronta, o laço balança — e só ele. A tampa fica parada: caixa com a tampa
   tremendo parece caixa quebrada, não caixa animada. */
.presente-btn.presente-pronto .p-laco {
  transform-box: fill-box;
  transform-origin: 50% 100%;
  animation: presente-laco-balanca 2.4s ease-in-out infinite;
}

@keyframes presente-laco-balanca {
  0%, 100% { transform: rotate(0deg); }
  30%      { transform: rotate(-7deg); }
  65%      { transform: rotate(5deg); }
}

/* O brilho que pede toque: um halo que respira atrás da caixa. */
.presente-caixa::before {
  content: '';
  position: absolute;
  inset: -30%;
  border-radius: 50%;
  background: radial-gradient(closest-side,
    color-mix(in srgb, var(--amarelo) 55%, transparent) 0%, transparent 72%);
  animation: presente-respira 2.2s ease-in-out infinite;
  pointer-events: none;
}

/* ---- a contagem do próximo, no lugar da caixa quando não há nenhuma ---- */

.presente-conta {
  pointer-events: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border-radius: var(--raio-pill);
  background: rgba(0, 0, 0, .46);
  backdrop-filter: blur(10px);
  color: rgba(255, 255, 255, .82);
  font-size: var(--txt-mini);
  font-weight: var(--peso-medio);
  font-variant-numeric: tabular-nums;
}

.presente-conta .icone { width: 14px; height: 14px; opacity: .8; }

/* ---- a abertura: tampa saindo, luz escapando de dentro ---- */

.presente-abrindo {
  position: fixed;
  inset: 0;
  z-index: 61;
  display: grid;
  place-items: center;
  pointer-events: none;
}

.presente-abrindo .presente-caixa {
  --caixa: 132px;
  animation: presente-sacode 380ms ease-in-out;
}

/* A tampa voa na abertura. Com o desenho em SVG ela é `.p-tampa`, e o laço vai
   junto — os dois são a parte de cima da caixa e sair um sem o outro deixaria o
   laço flutuando sobre a caixa aberta. `transform-box: fill-box` faz o
   `transform` medir a própria forma, e não a tela inteira do SVG. */
.presente-abrindo .p-tampa,
.presente-abrindo .p-laco {
  transform-box: fill-box;
  transform-origin: 50% 100%;
  animation: presente-tampa-voa 620ms cubic-bezier(.2, .8, .3, 1) 340ms both;
}

.presente-abrindo .presente-corpo {
  animation: presente-corpo-abre 620ms cubic-bezier(.2, .8, .3, 1) 340ms both;
}

/* A luz de dentro: um clarão que cresce de dentro da caixa. */
.presente-clarao {
  position: absolute;
  width: 132px;
  height: 132px;
  border-radius: 50%;
  opacity: 0;
  background: radial-gradient(closest-side, #fff 0%,
    color-mix(in srgb, var(--amarelo) 80%, transparent) 42%, transparent 76%);
  animation: presente-clarao 700ms ease-out 380ms both;
}

.presente-raios {
  position: absolute;
  width: 320px;
  height: 320px;
  opacity: 0;
  background: conic-gradient(from 0deg,
    transparent 0deg, rgba(255, 255, 255, .5) 8deg, transparent 16deg,
    transparent 45deg, rgba(255, 255, 255, .35) 53deg, transparent 61deg,
    transparent 90deg, rgba(255, 255, 255, .5) 98deg, transparent 106deg,
    transparent 135deg, rgba(255, 255, 255, .35) 143deg, transparent 151deg,
    transparent 180deg, rgba(255, 255, 255, .5) 188deg, transparent 196deg,
    transparent 225deg, rgba(255, 255, 255, .35) 233deg, transparent 241deg,
    transparent 270deg, rgba(255, 255, 255, .5) 278deg, transparent 286deg,
    transparent 315deg, rgba(255, 255, 255, .35) 323deg, transparent 331deg);
  mask-image: radial-gradient(closest-side, transparent 12%, #000 30%, transparent 72%);
  -webkit-mask-image: radial-gradient(closest-side, transparent 12%, #000 30%, transparent 72%);
  animation: presente-raios 760ms ease-out 380ms both;
}

@keyframes presente-balanca {
  0%, 100% { transform: rotate(0) translateY(0); }
  25%      { transform: rotate(-6deg) translateY(-3px); }
  50%      { transform: rotate(0) translateY(-5px); }
  75%      { transform: rotate(6deg) translateY(-3px); }
}

@keyframes presente-respira {
  0%, 100% { opacity: .35; transform: scale(.9); }
  50%      { opacity: .75; transform: scale(1.06); }
}

@keyframes presente-sacode {
  0%, 100% { transform: rotate(0); }
  20% { transform: rotate(-9deg); }
  45% { transform: rotate(8deg); }
  70% { transform: rotate(-5deg); }
  88% { transform: rotate(3deg); }
}

@keyframes presente-tampa-voa {
  0%   { transform: translateY(0) rotate(0); opacity: 1; }
  35%  { transform: translateY(-96px) rotate(-18deg); opacity: 1; }
  100% { transform: translateY(-190px) rotate(-42deg); opacity: 0; }
}

@keyframes presente-corpo-abre {
  0%   { transform: scale(1); opacity: 1; }
  30%  { transform: scale(1.08); }
  100% { transform: scale(.7) translateY(26px); opacity: 0; }
}

@keyframes presente-clarao {
  0%   { opacity: 0; transform: scale(.2); }
  30%  { opacity: 1; transform: scale(1.4); }
  100% { opacity: 0; transform: scale(3.4); }
}

@keyframes presente-raios {
  0%   { opacity: 0; transform: scale(.3) rotate(0); }
  35%  { opacity: .9; }
  100% { opacity: 0; transform: scale(1.5) rotate(38deg); }
}

/* ============================================================= 3. O APOIO */

/* ---- moeda voando até o saldo do topo ---- */

.moedas {
  position: fixed;
  inset: 0;
  z-index: 63;
  pointer-events: none;
}

.moeda-voa {
  position: absolute;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  margin: -13px 0 0 -13px;
  border-radius: 50%;
  color: var(--sobre-amarelo);
  background: radial-gradient(circle at 34% 30%,
    color-mix(in srgb, var(--amarelo) 70%, white) 0%, var(--amarelo) 62%,
    color-mix(in srgb, var(--amarelo) 72%, black) 100%);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .45);
  animation: moeda-voa 760ms cubic-bezier(.42, 0, .3, 1) var(--atraso, 0ms) both;
}

.moeda-voa .icone { width: 16px; height: 16px; }

@keyframes moeda-voa {
  0%   { opacity: 0; transform: translate(0, 0) scale(.4) rotate(0); }
  14%  { opacity: 1; transform: translate(0, -18px) scale(1.1) rotate(0); }
  100% { opacity: 0; transform: translate(var(--dx, 0), var(--dy, 0)) scale(.42) rotate(var(--giro, 0deg)); }
}

/* ---- o saque que destravou: um brilho percorre o botão ---- */

.feed-sacar { position: relative; overflow: hidden; }

.feed-sacar-destravou::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg,
    transparent 28%, rgba(255, 255, 255, .58) 50%, transparent 72%);
  transform: translateX(-130%);
  animation: sacar-brilha 1.5s ease-in-out 3;
  pointer-events: none;
}

@keyframes sacar-brilha {
  0%   { transform: translateX(-130%); }
  55%  { transform: translateX(130%); }
  100% { transform: translateX(130%); }
}

/* ==================================================== 4. MOVIMENTO PARADO
   Quem pediu menos movimento recebe a MESMA informação e o MESMO dinheiro:
   o cartão aparece pronto, o presente continua na tela pedindo toque, o valor
   já entra escrito por inteiro (lib/fx.js corta a contagem) e a moeda não voa
   — o saldo do topo muda igual, porque quem muda o saldo é o livro-caixa.

   O que morre aqui é laço, tremor e voo. Nada some junto. */

@media (prefers-reduced-motion: reduce) {
  .celebra-cartao,
  .celebra-treme,
  .celebra-lendario .celebra-cartao,
  .celebra-lendario .celebra-treme,
  .celebra-luz,
  .presente-btn .presente-caixa,
  .presente-caixa::before,
  .presente-abrindo .presente-caixa,
  .presente-abrindo .presente-tampa,
  .presente-abrindo .presente-corpo,
  .presente-clarao,
  .presente-raios,
  .presente-pula,
  .presente-btn:not(.presente-pronto) .presente-caixa,
  .moeda-voa,
  .feed-sacar-destravou::after {
    animation: none !important;
  }

  /* O anel do presente NÃO entra na lista acima, e é de propósito: ele não é
     animação, é a leitura de quanto falta. O que sai é a interpolação — o fio
     passa a saltar de um valor para o outro em vez de deslizar, e continua
     dizendo exatamente a mesma coisa. */
  .presente-anel-fio { transition: none; }

  /* Sem o pulo, o aviso de "caixa cheia" tem que existir de outro jeito: um
     contorno claro em volta, parado. */
  .presente-pronto .presente-caixa {
    outline: 2px solid var(--ouro, #F5C451);
    outline-offset: 6px;
    border-radius: 4px;
  }

  /* Sem giro, a luz vira uma auréola parada — continua dizendo "isto é raro"
     sem se mexer. */
  .celebra-raro .celebra-luz     { opacity: .28; }
  .celebra-epico .celebra-luz    { opacity: .34; }
  .celebra-lendario .celebra-luz { opacity: .42; }

  /* O clarão e os raios da abertura não têm o que dizer parados: quem conta o
     que aconteceu é o cartão que entra logo depois. */
  .presente-clarao,
  .presente-raios { display: none; }

  .feed-sacar-destravou::after { display: none; }
}

/* ==========================================================================
   A ESCOLHA DO DOBRO
   Duas saídas com pesos deliberadamente diferentes. A de cima é um botão
   cheio com o valor; a de baixo é texto apagado. O desequilíbrio é o ponto —
   mas a de baixo continua sendo um alvo de toque de gente, visível desde o
   primeiro quadro, e fecha na primeira tentativa.
   ========================================================================== */

.folha-dobro .dobro-oferta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e-3, 14px);
  text-align: center;
  padding-block-end: var(--e-2, 8px);
}

.dobro-selo {
  font-size: 34px;
  line-height: 1;
  filter: drop-shadow(0 0 14px color-mix(in srgb, var(--marca) 55%, transparent));
}

@supports not (color: color-mix(in srgb, red 50%, blue)) {
  .dobro-selo { filter: none; }
}

.dobro-titulo {
  margin: 0;
  font-size: var(--txt-forte, 17px);
  font-weight: 700;
}

/* O antes e o depois: o valor que já caiu recuado, o dobrado em destaque. */
.dobro-conta {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-variant-numeric: tabular-nums;
}

.dobro-de {
  font-size: var(--txt-corpo, 15px);
  color: var(--texto-3);
  text-decoration: line-through;
}

.dobro-seta { color: var(--texto-3); font-size: 14px; }

.dobro-para {
  font-size: 34px;
  font-weight: 800;
  color: var(--verde);
  letter-spacing: -.02em;
}

.dobro-nota {
  margin: 0;
  font-size: var(--txt-nota, 13px);
  color: var(--texto-fraco);
  max-width: 30ch;
}

.dobro-sim { margin-block-start: var(--e-1, 4px); }

/* A saída. Apagada, sem caixa — e com 48px de alvo, que é mais que o mínimo.
   Discreta não é escondida: quem quer sair, sai de primeira. */
.dobro-nao {
  min-height: 48px;
  padding-inline: var(--e-4, 18px);
  color: var(--texto-3);
  font-size: var(--txt-nota, 13px);
  font-weight: 500;
}

.dobro-nao:hover,
.dobro-nao:focus-visible {
  color: var(--texto-fraco);
}

/* ------------------------------------------------- A peça que dobra o valor
   Oito segundos, sem botão de fechar. Curta de propósito: a janela do dobro
   no livro-caixa não pode vencer enquanto a pessoa assiste. */
.folha-anuncio .anuncio-dobro {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e-3, 14px);
}

/* O ANÚNCIO DE VERDADE.
   O PLAYER SEGUE O VÍDEO, e não o contrário.
   Ele era uma caixa 9:16 fixa com `object-fit: cover`, porque a pasta do dobro
   era para receber só peças verticais. Nem todas são: um comercial de televisão
   é deitado, e dentro de uma caixa em pé ele aparecia com o meio da tela ocupado
   e duas faixas mortas em cima e embaixo — a peça inteira encolhida para caber
   numa forma que não é a dela.
   Agora a proporção vem do arquivo: `--proporcao` é escrita pela tela a partir
   da largura e da altura que `vincular-anuncios.mjs` gravou no catálogo, e o
   9/16 fica só como reserva para peça sem medida. `contain` no lugar de `cover`
   fecha o assunto: nada é cortado em nenhum dos dois formatos. */
.anuncio-video {
  width: 100%;
  max-height: 52vh;
  aspect-ratio: var(--proporcao, 9 / 16);
  object-fit: contain;
  border-radius: var(--raio, 12px);
  background: #000;
  display: block;
  margin-inline: auto;
}

.anuncio-palco {
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: var(--raio, 12px);
  overflow: hidden;
  display: grid;
  place-items: center;
  background:
    radial-gradient(110% 80% at 50% 30%, var(--cor-campanha) 0%, transparent 74%),
    linear-gradient(180deg, #10101a 0%, #05050a 100%);
}

.anuncio-marca {
  font-size: 64px;
  font-weight: 800;
  color: #fff;
  opacity: .16;
  line-height: 1;
  animation: anuncio-respira 2.6s ease-in-out infinite;
}

@keyframes anuncio-respira {
  0%, 100% { transform: scale(1);    opacity: .16; }
  50%      { transform: scale(1.06); opacity: .24; }
}

.anuncio-quem {
  margin: 0;
  font-weight: 700;
  font-size: var(--txt-corpo, 15px);
}

.anuncio-barra {
  width: 100%;
  height: 4px;
  border-radius: 999px;
  background: var(--superficie-3);
  overflow: hidden;
}

.anuncio-barra-fill {
  height: 100%;
  width: 0;
  background: var(--marca);
  transition: width 1s linear;
}

.anuncio-conta {
  color: var(--marca-texto);
  font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  .anuncio-marca { animation: none; }
  .anuncio-barra-fill { transition: none; }
}

/* ---------------------------------------------------------------- o ganho
   subindo de onde o dedo tocou.

   Nasceu no botão de curtir do feed e virou vocabulário da casa: é o retorno de
   ganho pequeno e frequente, em oposição à celebração do meio da tela, que é
   para o que interrompe. Quem desenha é `ganhoSobe()` em lib/fx.js.

   Vive no <body>, com `position: fixed`, porque coluna com `overflow` recortado
   — a lateral do feed é uma — cortaria o número ao meio. Por isso as coordenadas
   vêm do getBoundingClientRect do elemento de origem, em JS. */
.ganho-sobe {
  position: fixed;
  z-index: 60;
  transform: translate(-50%, -50%);
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--verde, #1DB954);
  color: #06210F;
  font-weight: 800;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  pointer-events: none;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .45);
  animation: ganho-sobe 1.2s cubic-bezier(.2, .85, .3, 1) forwards;
}

/* A BOLHA NASCE ACIMA DO ALVO, e não em cima dele.
   Ela começava em -30% da própria altura a partir do topo do elemento, ou seja,
   dois terços dela ficavam POR CIMA do saldo — e no topo da tela, onde o alvo é
   a pílula do saldo, o valor que entra tapava o valor que a pessoa tem
   justamente no segundo em que ela olha para lá. Agora ela já surge inteira
   acima, e sobe a partir dali. */
@keyframes ganho-sobe {
  0%   { opacity: 0; transform: translate(-50%, -95%) scale(.7); }
  18%  { opacity: 1; transform: translate(-50%, -135%) scale(1.08); }
  32%  { transform: translate(-50%, -145%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -280%) scale(.96); }
}

@media (prefers-reduced-motion: reduce) {
  /* Sem voo: o valor aparece parado e some. A informação continua; o movimento
     é que sai. */
  .ganho-sobe { animation: ganho-parado 1.2s linear forwards; transform: translate(-50%, -190%); }
  @keyframes ganho-parado { 0%, 72% { opacity: 1; } 100% { opacity: 0; } }
}

/* ==========================================================================
   4. A RECOMPENSA RECEBIDA — a tela que confirma o dinheiro

   Diferente da celebração acima em uma coisa que muda tudo: esta INTERROMPE.
   A celebração deixa o feed deslizar por baixo; esta pede um toque para sair.
   É a diferença entre avisar e confirmar, e é confirmar que faz o ganho
   parecer um recebimento em vez de um número que passou correndo.

   A hierarquia é a da referência do dono, e a ordem é o desenho inteiro: o
   certo verde diz O QUE aconteceu antes de qualquer número; o valor é a maior
   coisa da tela; o saldo vem miúdo logo abaixo, porque é contexto e não
   notícia. O botão fecha a tela e é a única saída — sem × no canto, sem toque
   no fundo: um gesto só, igual à porta de entrada.
   ========================================================================== */

/* NO MEIO DA TELA, e não presa no rodapé.
   `.modal-fundo` alinha em `end center` porque quase todo modal deste app é uma
   folha que sobe de baixo. Esta não é folha: ela é o anúncio do dinheiro, e
   anúncio fica onde o olho já está.

   Quem alinha é o FUNDO, e a classe `modal-recompensa` fica na caixa — `modal()`
   a concatena em `.modal`, não no `.modal-fundo`. Daí o `:has()`: é o seletor
   que permite estilizar o pai a partir do filho sem mudar a assinatura de
   `modal()`, que é usada por meia dúzia de telas que estão certas do jeito que
   estão. */
.modal-fundo:has(.modal-recompensa) {
  place-items: center;
}

.modal.modal-recompensa {
  width: min(90vw, 340px);
  padding: var(--e-6) var(--e-5) var(--e-5);
  /* `.modal` empurra a si mesmo para o rodapé com `margin: auto 0 0`. */
  margin: 0;
}

/* Navegador sem `:has()` mantém o cartão no rodapé — que é onde todo modal
   deste app vive. Feio para este caso, nunca quebrado. */

.recompensa {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--e-3);
}

/* O certo dentro do disco verde. É a primeira coisa que o olho pega, e é de
   propósito: ele responde "deu certo?" antes de a pessoa começar a ler. */
.recompensa-selo {
  display: grid;
  place-items: center;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  color: var(--verde);
  background: color-mix(in oklab, var(--verde) 16%, transparent);
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--verde) 34%, transparent),
              0 0 34px color-mix(in oklab, var(--verde) 22%, transparent);
  animation: recompensa-selo 520ms var(--curva-mola) both;
}

.recompensa-titulo {
  margin: 0;
  font-size: var(--txt-forte);
  font-weight: var(--peso-extra);
  letter-spacing: var(--letra-rotulo);
  text-transform: uppercase;
  line-height: var(--altura-apertada);
  color: var(--texto);
  text-wrap: balance;
}

/* O maior número da tela, no verde que este app reserva para dinheiro.
   `tabular-nums` porque ele CONTA de zero até o valor: sem largura fixa por
   dígito, o número inteiro treme a cada passo da contagem. */
.recompensa-valor {
  font-size: clamp(38px, 12vw, 50px);
  font-weight: var(--peso-extra);
  line-height: 1;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
  color: var(--verde);
  text-shadow: 0 0 34px color-mix(in oklab, var(--verde) 30%, transparent);
}

.recompensa-saldo {
  margin: 0;
  font-size: var(--txt-mini);
  color: var(--texto-fraco);
  font-variant-numeric: tabular-nums;
}

/* A pergunta do fim do funil, embaixo de "Saque liberado!".
   Ela não é caixa-alta como o título: são duas linhas seguidas, e duas linhas
   em versal viram um bloco que ninguém lê até o fim. O título anuncia; esta
   pergunta fala com a pessoa, e por isso é escrita como se fala. */
.destravou-pergunta {
  margin: calc(-1 * var(--e-2)) 0 0;
  font-size: var(--txt-corpo);
  font-weight: var(--peso-medio);
  color: var(--texto-fraco);
}

.recompensa-saldo strong { color: var(--texto); font-weight: var(--peso-forte); }

/* O BOTÃO É VERDE, e é a única coisa deste app fora do extrato em que o verde
   não é um número.
   A regra da casa é estrita: vermelho é a marca, verde é dinheiro. Aqui o botão
   não leva a pessoa a lugar nenhum — ele fecha o anúncio de um valor que já
   entrou. Ele É o dinheiro, e pintá-lo de vermelho o faria disputar com a marca
   justamente onde a marca não tem nada a dizer. */
.recompensa-botao {
  margin-top: var(--e-2);
  gap: var(--e-2);
  text-transform: uppercase;
  letter-spacing: var(--letra-rotulo);
  font-size: var(--txt-nota);
  background: var(--verde);
  color: #05210F;
  border: 0;
  font-weight: var(--peso-extra);
  box-shadow: 0 8px 26px color-mix(in oklab, var(--verde) 34%, transparent);
}

.recompensa-botao:active { transform: scale(.98); }

@keyframes recompensa-selo {
  0%   { opacity: 0; transform: scale(.4); }
  60%  { opacity: 1; transform: scale(1.12); }
  100% { opacity: 1; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  /* O selo nasce pronto. A informação é a mesma; o que sai é o salto. */
  .recompensa-selo { animation: none; }
}

/* ---------------------------------------------- o trilho até o saque
   A barra que aparece embaixo de todo ganho. Ela é o que transforma um valor
   que caiu num PASSO em direção ao Pix — sem ela, cada ganho é um fim em si.

   Fica DEPOIS do saldo e ANTES do botão, de propósito: o olho desce do valor
   para a distância e da distância para a ação. Invertida, a pessoa decide sair
   antes de saber quanto falta. */
.recompensa-trilho {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: var(--e-2);
}

.recompensa-barra {
  height: 6px;
  border-radius: 999px;
  background: color-mix(in oklab, var(--texto) 12%, transparent);
  overflow: hidden;
}

.recompensa-barra-fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--verde), color-mix(in oklab, var(--verde) 70%, var(--marca)));
  /* Cresce da largura anterior para a nova. A pessoa vê a barra ANDAR, que é o
     que diz "você chegou mais perto" — barra que já nasce no lugar certo só
     informa uma posição. */
  transition: width 700ms var(--curva, ease);
}

.recompensa-trilho-txt {
  margin: 0;
  font-size: var(--txt-mini);
  color: var(--texto-fraco);
  text-wrap: pretty;
}

/* Perto do fim, a frase sobe de peso e ganha a cor do dinheiro. É o único
   degrau visual do trilho, e ele existe porque perto o número mobiliza. */
.recompensa-trilho-perto .recompensa-trilho-txt {
  color: var(--verde);
  font-weight: var(--peso-forte);
}

.recompensa-trilho-perto .recompensa-barra-fill {
  box-shadow: 0 0 12px color-mix(in oklab, var(--verde) 60%, transparent);
}

/* ------------------------------------------- o saque destravado
   A comemoração de ter CHEGADO, e não de ter ganhado — uma vez na vida da
   conta. O ouro no lugar do verde marca a diferença: aqui não é mais um valor
   que entrou, é a meta alcançada. */
.recompensa-selo-ouro {
  color: var(--marca);
  background: color-mix(in oklab, var(--marca) 18%, transparent);
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--marca) 40%, transparent),
              0 0 40px color-mix(in oklab, var(--marca) 30%, transparent);
}

.modal-destravou .modal {
  border: 1px solid color-mix(in oklab, var(--marca) 34%, transparent);
}

.recompensa-apoio {
  margin: 0;
  font-size: var(--txt-nota);
  color: var(--texto-fraco);
  text-wrap: pretty;
}

/* A saída discreta. Existe para ninguém ficar preso numa comemoração, e é
   discreta para não competir com o botão que leva ao saque. */
.recompensa-depois {
  margin-top: 2px;
  background: none;
  border: 0;
  color: var(--texto-fraco);
  font-size: var(--txt-mini);
  min-height: var(--toque-min-compacto);
}

.recompensa-depois:active { opacity: .7; }

@media (prefers-reduced-motion: reduce) {
  /* A barra vai direto para a posição final. O número continua o mesmo. */
  .recompensa-barra-fill { transition: none; }
}

/* ---- o pop-up de saque liberado ---------------------------------------
   O único aviso que o app dá uma vez na vida da conta, e o único cujo botão
   LEVA a algum lugar em vez de fechar. Ele podia ser discreto quando morava só
   no feed; agora que alcança qualquer tela, ele precisa parar o que a pessoa
   estava fazendo — e parar sem gritar. */

.destravou-chamada {
  position: relative;
  width: 100%;
}

/* A seta desenhada bate no canto de cima do botão. Fora do fluxo para não
   empurrar nada, e sem receber toque: quem chama não pode atrapalhar quem
   atende. */
.destravou-seta {
  position: absolute;
  /* A ALTURA MUDOU QUANDO A LINHA DO MÍNIMO SAIU.
     Ela mirava o canto do botão passando por cima de um parágrafo — e sem esse
     parágrafo o botão subiu, então a seta foi parar em cima do valor. Descida
     para a altura do próprio botão, ela volta a apontar para ele sem tapar o
     número, que é a coisa que a pessoa está lendo. */
  left: -34px;
  top: -30px;
  width: 96px;
  height: auto;
  /* Meia volta: o desenho aponta para cima e para a esquerda; virado, ele
     aponta para baixo e para a direita, que é onde o botão está. */
  transform: rotate(180deg);
  pointer-events: none;
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, .45));
  z-index: 2;
}

/* O botão do aviso herda o pulso comum de `.pulsa-chamando`, em styles.css. */
}

/* ---- a moeda do pop-up de recompensa -----------------------------------
   Veio da tela de avaliação, onde caía atrás deste mesmo pop-up. Hoje ela é o
   selo de toda recompensa do app — o certinho verde que estava aqui dizia
   "deu certo", que a pessoa já sabe porque está lendo o valor. */

.recompensa-moeda {
  /* Sem o círculo verde e sem o brilho: a moeda já é a forma e a cor. */
  background: none;
  box-shadow: none;
  font-size: 42px;
  line-height: 1;
  animation: none;

  /* A PILHA DE FONTES DE EMOJI é o que impede o quadradinho vazio. O desenho da
     moeda é um caractere, e quem o desenha é a fonte de emoji do sistema — que
     tem nome diferente em cada um. Sem declarar os três, um aparelho cujo nome
     não bate cai na fonte de texto, que não tem esse caractere, e mostra a
     caixa de tofu no lugar da moeda. */
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji",
    "Twemoji Mozilla", "Android Emoji", sans-serif;
}

.moeda-cai {
  display: inline-block;
  animation: moeda-cai 620ms cubic-bezier(.34, 1.56, .64, 1) both;
  transform-origin: 50% 100%;
}

@keyframes moeda-cai {
  0%   { transform: translateY(-38px) rotate(-18deg) scale(.7); opacity: 0; }
  55%  { transform: translateY(4px) rotate(6deg) scale(1.12);   opacity: 1; }
  75%  { transform: translateY(-6px) rotate(-2deg) scale(1);    }
  100% { transform: translateY(0) rotate(0) scale(1);           opacity: 1; }
}

/* Quem pediu menos movimento recebe a moeda parada, sem perder o feedback. */
@media (prefers-reduced-motion: reduce) {
  .moeda-cai { animation: none; }

