/* O VISUAL DE RARIDADE — as cinco camadas que fazem uma peça pertencer a um degrau.
 *
 * ⚠⚠ ESTE ARQUIVO É A FONTE ÚNICA, E "ÚNICA" JÁ FALHOU TRÊS VEZES POR PARCIALIDADE. Ele nasceu dentro
 * do `itens.html`; na primeira migração veio só o lustro dos quatro degraus de cima, e o jogo ficou
 * com um brilho solto — sem aura, sem padrão atrás, sem tinta e sem o contorno que segue a forma. As
 * outras quatro camadas continuavam em seletores do catálogo (`#grade…`, `.brilho…`), que o jogo não
 * tem. Migrar "a regra principal" e deixar o resto é o mesmo defeito que a tabela de cor já causou
 * aqui (divergiu em quatro raridades antes de alguém reparar).
 *
 * AS CINCO CAMADAS, na ordem em que pintam:
 *   aura parada → padrão andando atrás → arte → tinta na silhueta + contorno → lustro
 *
 * O CONTRATO, para quem for usar — e quem monta isso é [rarity-visual.ts](../src/rarity-visual.ts),
 * porque as duas telas terem a mesma aparência não se garante escrevendo a mesma coisa duas vezes:
 *   1. um CONTÊINER com a classe do degrau (`r-uncommon` … `r-eternal`), mais `com-aura` e
 *      `com-padrao` quando o degrau acende, e as variáveis `--cor-rar` (+ `--pad-*`, `--ceu-*`);
 *      ⚠ `--cor-rar` faltando não dá cor errada: invalida o `color-mix` e o navegador descarta a
 *      declaração inteira, em silêncio.
 *   2. dentro dele, `<span class="thumb" style="--ico:url(<arte>);--fase:<0..1>">` — o `--ico` é a
 *      MÁSCARA, e o `.thumb` TEM QUE TER O TAMANHO DO DESENHO: as camadas são `inset:0` dele e as
 *      máscaras são `contain` sobre ele, então uma caixa maior que a arte espalha a luz para fora.
 *   3. dentro do `.thumb`: o `.plano` (padrão/céu), a `<img>`, e `<span class="lustro"><i></i></span>`
 *      — no lustro o pai recorta e o filho anda. ⚠ Transformar o próprio elemento mascarado moveria a
 *      máscara junto e a luz sairia do item; pai recorta, filho anda, e os dois nunca se cruzam.
 *
 * Os seis degraus e as calibragens de cada um estão comentados abaixo, como vieram do catálogo.
 */

/* ══ A CAIXA DA ARTE E AS CAMADAS DO DEGRAU ═══════════════════════════════════════════════════════
   ⚠⚠ ESTAS REGRAS MORAVAM NO `itens.html` E POR ISSO O JOGO SÓ TINHA UMA DAS CINCO CAMADAS. O visual
   de raridade não é "o lustro": é aura parada + padrão andando atrás + tinta na silhueta + contorno
   que segue a forma + o lustro, nessa ordem. O inventário recebeu só a última e ficou com um brilho
   solto, sem borda acesa e sem fundo. Os seletores aqui são de CLASSE (`.com-aura .thumb`), não do
   `#grade` do catálogo, justamente para que as duas telas leiam a mesma regra.
   ⭐ Quem monta a marcação é `rarity-visual.ts` — contêiner com `r-<degrau> com-aura com-padrao` e
   `--cor-rar`, e dentro dele um `.thumb` com `--ico`/`--fase`. */
/* ⚠ `container-type: inline-size` faz a caixa virar RÉGUA: os `cqi` lá embaixo medem a largura dela.
   É o que deixa o padrão de fundo ter a mesma proporção num slot de 32px, num popup de 76px e num
   card de 138px. ⚠ `inline-size` e não `size`: conter os dois eixos tiraria a altura do
   `aspect-ratio` e a caixa colapsaria no catálogo. */
.thumb { position:relative; display:grid; place-items:center; aspect-ratio:1; contain:paint;
  container-type: inline-size; }
/* ⚠ a arte se ajusta À CAIXA, nunca o contrário: quem define o tamanho é o `.thumb`, e a imagem cabe
   dentro dele. Se fosse o inverso, as camadas mascaradas (`contain`, centradas na caixa) deixariam de
   coincidir com o desenho — foi assim que a luz saiu maior que a peça no inventário. */
.thumb img { max-width:100%; max-height:100%; image-rendering:auto; }

/* ⭐⭐ A AURA FIXA — o brilho parado por trás da peça, em toda raridade que acende. Ela é INDEPENDENTE
   do resto: já sumiu uma vez por estar amarrada na classe da partícula, e voltou como classe própria. */
.com-aura .thumb { background-image:
  radial-gradient(circle at 50% 46%, color-mix(in srgb, var(--cor-rar) 30%, transparent) 0%,
                  color-mix(in srgb, var(--cor-rar) 12%, transparent) 38%, transparent 70%);
  background-size: 100% 100%; background-position: center; background-repeat: no-repeat; }

/* A TINTA — a peça inteira colorida na cor do degrau, parada, MASCARADA pela própria silhueta.
   ⚠ Ela sobe pouco de propósito: acima de ~0.3 deixa de colorir a arte e passa a APAGÁ-LA. */
.thumb::before { content:""; position:absolute; inset:0; pointer-events:none; opacity:0;
  -webkit-mask-image: var(--ico); mask-image: var(--ico);
  -webkit-mask-size: contain;     mask-size: contain;
  -webkit-mask-position: center;  mask-position: center;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; }
.com-aura .thumb::before { background: var(--cor-rar); opacity: var(--tinta, .18); }

/* ⭐⭐ ORDEM DE PINTURA: aura → padrão → arte → lustro. ⚠ Sem isto o padrão pinta POR CIMA da arte —
   `.plano` é posicionado e o `<img>` não, e posicionado ganha de estático. E o LUSTRO precisa do 2:
   no inventário o `<img>` já tinha `z-index:1` e a luz ficava ATRÁS do desenho. */
.thumb img { position:relative; z-index:1; }
.lustro { z-index:3; }

/* ⚠ A borda de raridade é a COR SÓLIDA da própria célula (inline, RARITY_COLOR) — existiu aqui uma
   moldura de imagem por degrau (`com-moldura`, folha 31) cobrindo essa borda, e o dev a reverteu ao
   vê-la na tela: "as bordas de raridade ficaram meio ruins, voltar pra cor única mesmo" (2026-08-13).
   O git guarda a implementação completa (inclusive a conta do inset de 2 termos) se ela voltar. */

/* ⭐⭐ O CONTORNO QUE ACOMPANHA A FORMA — `drop-shadow` respeita o ALFA do PNG, então a luz sai no
   contorno do desenho e não num retângulo. É ele que faz a peça parecer ACESA em vez de ter uma luz
   por perto, e é o que o jogo não tinha. */
.com-aura .thumb img, .com-aura .peca { filter: drop-shadow(0 0 calc(0.0145 * 100cqi) var(--cor-rar))
  drop-shadow(0 0 var(--halo, 9px) color-mix(in srgb, var(--cor-rar) 65%, transparent)); }
/* ⚠ AVULSO NÃO LEVA O CONTORNO. O contorno de silhueta é a assinatura de EQUIPAMENTO especial — num
   minério de ferro (tier 2 → degrau uncommon) ele fazia a célula ler como item único na bolsa (o dev
   viu e mandou corrigir, item 28). A escada do avulso continua com aura e padrão; só a silhueta
   acesa é exclusiva de item. */
.slot.mat.com-aura .thumb img, .slot.mat.com-aura .peca { filter: none; }

/* O PADRÃO QUE ANDA ATRÁS — ladrilho repetido que desliza exatamente um ladrilho por volta (emenda
   invisível), no compositor. O eterno usa três planos em paralaxe; os outros, um só.
   ⚠ tamanho `100% + ladrilho` e topo em `-ladrilho`: o percurso expõe as bordas direita e de cima, e
   o excesso cobre exatamente o que o movimento expõe. */
.plano { position:absolute; left:0; pointer-events:none;
  top: calc(-1 * var(--lad)); width: calc(100% + var(--lad)); height: calc(100% + var(--lad));
  background-image: var(--img); background-size: var(--lad) var(--lad); background-repeat: repeat;
  /* mesmo relógio do lustro: sem ele o padrão de fundo também recomeçava a cada redesenho */
  animation: planoDeriva var(--dur) linear calc(-1 * var(--relogio, 0s)) infinite; }
/* ⚠⚠ O LADRILHO É FRAÇÃO DA CAIXA, NÃO PIXEL. Calibrado em px sobre o card do catálogo (~138px), o
   mesmo desenho de 64px vira o DOBRO do quadro num slot de 32px — o fundo aparecia gigante no
   inventário e no popup, com um quarto de favo cobrindo o item. `100cqi` é a largura real da caixa,
   então a proporção aprovada se mantém em qualquer superfície. ⚠ Os três planos do céu carregam a
   fração equivalente aos px originais (120, 46 e 78 sobre 138) — a paralaxe é a DIFERENÇA entre eles
   e reescalar um sem os outros a desfaria. */
.plano.padrao { --img: var(--pad-img); --lad: calc(var(--pad-frac) * 100cqi); --dur: var(--pad-dur) }
.plano.p1 { --img: var(--ceu-perto); --lad: calc(0.8696 * 100cqi); --dur: 13s }
.plano.p2 { --img: var(--ceu-medio); --lad: calc(0.3333 * 100cqi); --dur: 8.66s }
.plano.p3 { --img: var(--ceu-longe); --lad: calc(0.5652 * 100cqi); --dur: 26s }
@keyframes planoDeriva {
  from { transform: translate3d(0, 0, 0) }
  to   { transform: translate3d(calc(-1 * var(--lad)), var(--lad), 0) }
}

/* o halo e a TINTA escalam com o degrau — são o acompanhamento, não a leitura.
   ⚠⚠ O HALO TAMBÉM É FRAÇÃO DA CAIXA, pelo mesmo motivo do ladrilho: 12px de desfoque num card de
   138px é um contorno; os mesmos 12px num slot de 32px são uma mancha que cobre a peça. As frações
   abaixo são os pixels originais divididos pela caixa em que foram calibrados (138px), então o card
   do catálogo continua idêntico ao que foi aprovado e as superfícies menores acompanham.
   ⚠ `cqi` dentro de uma variável é resolvido ONDE ELA É USADA — aqui, no `filter` do `.thumb img`,
   que está dentro do contêiner. Declarada no ancestral, ela ainda mede a caixa certa. */
/* ⚠ a tinta VOLTOU à calibragem original: o "fundo quase invisível" do dev era o PADRÃO que anda
   atrás (reforçado no padraoDoFundo), não esta camada — e acima de ~0.3 a tinta apaga a arte. */
.r-uncommon  { --halo: calc(0.029 * 100cqi); --tinta: .10 }
.r-rare      { --halo: calc(0.043 * 100cqi); --tinta: .14 }
.r-heroic    { --halo: calc(0.065 * 100cqi); --tinta: .18 }
.r-mythic    { --halo: calc(0.087 * 100cqi); --tinta: .21 }
.r-ascendant { --halo: calc(0.109 * 100cqi); --tinta: .25 }
.r-eternal   { --halo: calc(0.138 * 100cqi); --tinta: .28 }

.lustro { position:absolute; inset:0; pointer-events:none; overflow:hidden; display:none;
  -webkit-mask-image: var(--ico); mask-image: var(--ico);
  -webkit-mask-size: contain;     mask-size: contain;
  -webkit-mask-position: center;  mask-position: center;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; }
/* ⭐⭐ O PALCO VALE PRA TODO DEGRAU COM MOVIMENTO, não só o eterno. Os três do meio animavam
   `background-position` (e dois deles com `filter` por cima), que repinta o elemento a cada quadro;
   aqui o desenho fica ESTÁTICO na camada e só o `transform` anda. Mesmo efeito, custo de
   compositor em vez de custo de pintura.
   ⚠⚠ OS SEIS DEGRAUS ENTRAM. Incomum e raro ficaram de fora na primeira migração com o argumento de
   que já eram baratos (animam só `opacity`) — mas eles moravam num seletor do CATÁLOGO
   (`.thumb::after`), e o jogo não tem `.thumb`. Resultado: o `play.ts` emitia o `<span class="lustro">`
   para as seis raridades que brilham e o CSS só desenhava quatro, então justo as duas que mais caem
   apareciam SEM efeito nenhum no inventário. "Barato demais para migrar" custou o efeito inteiro nos
   dois degraus mais vistos do jogo. */
.r-uncommon .lustro, .r-rare .lustro,
.r-heroic .lustro, .r-mythic .lustro, .r-ascendant .lustro, .r-eternal .lustro { display:block; }
/* sem silhueta, sem recorte: máscara com 404 é ignorada e o efeito vira um quadrado (ver acima) */
.thumb.vazio .lustro, .slot.vazio .lustro { display:none; }
/* ⚠ SEM `will-change`: a animação rodando já promove a camada sozinha, e `will-change` seguraria
   uma camada de GPU POR CARD mesmo nos pausados/fora da tela — com centenas de itens isso é memória
   de compositor por nada. Quem decide se a camada existe é a animação estar RODANDO. */
.lustro i { position:absolute; mix-blend-mode:screen; }
/* ⚠⚠ O ATRASO FAZ PARTE DO CONTRATO, não do catálogo. Ele desincroniza peças iguais na mesma tela, e
   é uma FRAÇÃO do ciclo (`--fase`, 0 a 1), nunca um número de segundos — assim a dessincronia
   sobrevive a mudar a duração de um degrau.
   ⚠ E sem ele o atalho `animation: lustrar var(--ciclo) linear var(--atraso) infinite` fica INVÁLIDO
   e o navegador descarta a declaração inteira: `animation-name` vira `none` e o efeito simplesmente
   não acontece. Foi o que aconteceu quando esta linha ficou para trás no `itens.html` — a folha
   carregava, a máscara aplicava, e nada se movia. */
/* ⚠⚠ O SEGUNDO TERMO É O QUE FAZ O BRILHO SOBREVIVER AO REDESENHO. `--fase` desincroniza peça de
   peça, mas é um valor FIXO: recriada, a célula recomeçava no mesmo ponto do ciclo, e como toda ação
   do jogo redesenha o painel inteiro por `innerHTML`, o brilho voltava ao começo o tempo todo (dev
   28/08/2026 — medido: `animation-delay: -0.918s` constante num ciclo de 6,8s).
   `--relogio` são os segundos já decorridos desde a abertura da página, carimbados no elemento no
   momento em que ele nasce (ver `varsDoContainer`). Somado ao atraso, o elemento novo entra na
   animação onde o antigo estava — a continuidade deixa de depender de o nó sobreviver. */
.lustro i { --atraso: calc((var(--fase, 0) * -1 * var(--ciclo, 0s)) - var(--relogio, 0s)); }

/* INCOMUM e RARO — O RESPIRO: a peça inteira acende e apaga devagar, sem faixa e sem percurso.
   ⚠ Os dois degraus mais baixos que acendem precisam ser LIDOS como o começo da escada, não como uma
   versão fraca da varredura do herói: por isso o evento é outro (pulsar, não atravessar) e não só
   uma opacidade menor. O incomum é o mais suave dos dois de propósito — ele tem que dar pra ver que
   está aceso sem competir com o raro logo acima.
   ⚠ Aqui o `<i>` preenche o pai inteiro: quem recorta na silhueta é a máscara do `.lustro`, então o
   que pulsa é a PEÇA, não um retângulo por cima dela. Nada de `transform` — só `opacity`, que já é
   composta, e é o motivo de estes dois nunca terem precisado do palco de translação. */
.r-uncommon .lustro i, .r-rare .lustro i { inset:0; width:auto; height:auto; background: var(--cor-rar); }
.r-uncommon .lustro i { --ciclo:3.2s; animation: respiroFraco var(--ciclo) ease-in-out var(--atraso) infinite; }
.r-rare .lustro i     { --ciclo:2.6s; animation: respiro     var(--ciclo) ease-in-out var(--atraso) infinite; }
@keyframes respiroFraco { 0%,100% { opacity:.03 } 50% { opacity:.15 } }
@keyframes respiro      { 0%,100% { opacity:.06 } 50% { opacity:.30 } }

/* HERÓICO — a mesma faixa a 105°. Ela é DESENHADA na diagonal e ANDA na horizontal, igual ao que o
   `background-position` fazia; o percurso vai de fora a fora pra não aparecer entrada.
   ⚠⚠ MAIS DEVAGAR, NA CADÊNCIA DO MÍTICO (dev: "o do heroico deveria ser mais devagar" → "mesma
   velocidade que do mítico"). ⭐ E não bastava trocar 2,4s por 6,8s: como a faixa dele é larga em
   relação ao percurso, o ciclo maior sozinho faria ela RASTEJAR por 3,15s — metade do ciclo com luz
   em cima da peça, que é o oposto do mítico. O mítico é lento porque passa RARO, não porque a faixa
   se arrasta. Então o percurso cresceu junto: a faixa desacelera de 1,47 pra 1,08 thumb/s E o vazio
   vai de 1,3s pra 5,3s. Medido: 1,50s visível / 5,30s vazio, contra 0,87 / 5,93 do mítico. */
.r-heroic .lustro i {
  --ciclo:6.8s;
  /* ⚠⚠ 2ª CALIBRAGEM — "tá parecendo uma barra de luz passando por cima do item". É LITERALMENTE a
     mesma queixa que o mítico já levou ("muito dura no item"), e as três causas eram as mesmas,
     todas ainda de pé aqui porque o herói nunca passou por aquela rodada:
       • LARGA — 62% da peça (79px de 128). Faixa que cobre metade do item de uma vez não lê como
         reflexo passando, lê como camada por cima. Agora 34% (44px).
       • ENCOSTAVA NA COR PURA no pico. O mítico já aprendeu: `var(--cor-rar)` cheio no meio dá um
         miolo DURO, e miolo duro é o que faz a faixa virar objeto sólido. Agora o pico é 72%, com
         paradas intermediárias — o corte deixa de existir.
       • ERA A ÚNICA SEM DESFOQUE. Mítico tem blur(7), ascendente blur(5), eterno blur(2); o herói,
         zero. Sem desfoque a borda do gradiente é uma LINHA, e linha reta lê como recorte.
     ⭐ A VELOCIDADE DE DESLOCAMENTO QUASE NÃO MUDOU (1,08 → 1,12 thumb/s) e o CICLO continua o do
     mítico — o dev acabou de escolher essa cadência e mexer nela desfaria a decisão. O que encurta
     é a PASSADA (1,50 → 1,20s), consequência de a faixa ser mais estreita: é o "um pouco mais
     rápido" sem tocar no ritmo.
     ⚠ Faixa mais fina exige percurso MAIOR em % — o `translate` é relativo ao PRÓPRIO elemento, e
     34% de 128 anda menos que 62% pelo mesmo percentual. Sem recalcular, afinar teria DESACELERADO
     o brilho (2,26s de passada) — o oposto do pedido. */
  top:-40%; bottom:-40%; left:0; width:34%; opacity:.55; filter: blur(5px);
  background: linear-gradient(105deg,
    transparent 0%,
    color-mix(in srgb, var(--cor-rar) 22%, transparent) 26%,
    color-mix(in srgb, var(--cor-rar) 72%, transparent) 50%,
    color-mix(in srgb, var(--cor-rar) 22%, transparent) 74%,
    transparent 100%);
  animation: passar var(--ciclo) linear var(--atraso) infinite;
}
/* MÍTICO — a brasa subindo na diagonal. ⚠ O blur fica ESTÁTICO na camada (rasterizado uma vez) e
   só o transform anda: antes ele era refeito a cada quadro, e desfoque é caro POR REPINTURA. */
.r-mythic .lustro i {
  --ciclo:6.8s;
  left:-40%; right:-40%; top:0; height:110%; width:auto; opacity:.5; filter: blur(7px);
  background: linear-gradient(162deg, transparent 0%,
    color-mix(in srgb, var(--cor-rar) 24%, transparent) 22%,
    color-mix(in srgb, var(--cor-rar) 78%, transparent) 50%,
    color-mix(in srgb, var(--cor-rar) 24%, transparent) 78%, transparent 100%);
  animation: subir var(--ciclo) linear var(--atraso) infinite;
}
/* ⭐⭐ ASCENDENTE — O PULSO DUPLO QUE DESCE. É o degrau inteiro reconstruído: ele perdeu DUAS coisas
   de uma vez (o aro que escalava, por acessibilidade, e a tira de partícula, por custo), e ficaria
   sem movimento nenhum enquanto os DOIS degraus abaixo dele têm passada. Isso quebra a escada ao
   contrário, que é tão ruim quanto quebrá-la pra cima.
   ⚠ O que ele NÃO podia ser: nem escala/rotação (gatilho vestibular), nem pixel vivo por relógio
   (foi o que acabou de sair). Sobra o palco composto — o mesmo do herói e do mítico, que já são
   baratos e já foram aprovados.
   ⭐ E não podia ser a MESMA passada de novo: "a mesma coisa, de outra cor" não lê como progressão,
   é a regra que este arquivo já aprendeu. O que muda aqui é o EVENTO, não a intensidade — a faixa
   tem DOIS picos separados por um vazio, então a peça acende duas vezes em sequência rápida e
   depois fica muito tempo quieta. Nenhum outro degrau pulsa duas vezes.
   ⚠ Custa o mesmo que um pico só: os dois vão no MESMO gradiente, no MESMO elemento, com o MESMO
   `translate`. É rítmo novo sem camada nova.
   ⚠ De propósito o mais DISCRETO dos três (opacidade .42 contra .5/.55): este degrau já carrega o
   maior halo, a maior tinta e o padrão mais rico. A passada aqui é o acabamento, não a leitura. */
.r-ascendant .lustro i {
  --ciclo:6.8s;
  /* ⚠⚠ A INCLINAÇÃO É 30° (dev: "mantém de cima pra baixo, mas dá uma angulada, uns 30 graus").
     `210deg` porque a faixa é PERPENDICULAR à linha do gradiente: 180deg desce reto e dá faixa
     horizontal, então 30° de faixa = 180+30. ⚠ Ela cai pra direita, ao CONTRÁRIO do mítico (que
     sobe pra direita a 18°) — o dev não disse pra que lado, e inclinar as duas pro mesmo lado faria
     os degraus vizinhos lerem como a mesma listra. Um caractere pra virar, se ele preferir.
     ⚠⚠ E AS PARADAS ENCOLHERAM, o que NÃO é enfeite: a linha do gradiente é
     `L = larg·|sen θ| + alt·|cos θ|`, e a 30° ela cresce 28% contra os 12° de antes. Parada em %
     é fração de L — mantê-las daria uma faixa 28% mais GROSSA sem ninguém pedir, justo no degrau
     que o dev vem pedindo pra aliviar. Encolhidas na mesma proporção, a espessura em pixel fica
     idêntica à que ele já viu (rampa 16,6px, vale 21px). */
  left:-40%; right:-40%; top:0; height:110%; width:auto; opacity:.42; filter: blur(7px);
  background: linear-gradient(210deg,
    transparent 0%,
    color-mix(in srgb, var(--cor-rar) 18%, transparent) 24%,
    color-mix(in srgb, var(--cor-rar) 62%, transparent) 31%,   /* 1º pico */
    color-mix(in srgb, var(--cor-rar) 10%, transparent) 40%,   /* o vazio entre eles */
    color-mix(in srgb, var(--cor-rar) 62%, transparent) 49%,   /* 2º pico */
    color-mix(in srgb, var(--cor-rar) 18%, transparent) 56%,
    transparent 80%);
  animation: descer var(--ciclo) linear var(--atraso) infinite;
}
/* ⚠⚠ O ARO ANTERIOR FOI REMOVIDO, e o motivo é ACESSIBILIDADE, igual ao eterno.
   Ele era `scale(.05)` → `scale(2.6)` num ciclo de 1,5s: um aro nascendo do miolo e explodindo pra
   fora, 40 vezes por minuto, em TODO card ascendente da grade ao mesmo tempo.
   ⭐⭐ É LITERALMENTE O MESMO MECANISMO que fez o eterno ser remodelado — escala repetida de forma
   grande, o gatilho vestibular mais forte deste conjunto, que deu tonteira no Juliano. Quando o
   eterno foi consertado este degrau não foi olhado junto, e ficou de pé com 4,5× a frequência: os
   vizinhos passam a cada 6,8s (herói e mítico) e 5,3s (eterno); ele repetia a cada 1,5s.
   ⚠ O dev chegou nele pelo INCÔMODO ("ainda tá muita coisa"), não pela regra — sintoma de gatilho
   vestibular é justamente esse, cansa antes de dar nome. E ele ia mostrar esta tela pro Juliano.
   ⚠ CONSEQUÊNCIA: o ascendente fica sem lustro. Quem carrega o degrau agora é o PADRÃO (a rosácea,
   a mais rica das cinco) mais o halo. Se faltar movimento aqui, o caminho NÃO é voltar o aro — é uma
   passada composta, do mesmo feitio do herói/mítico, que não escala nada. */
/* ETERNO — o lustro esporádico: cruza em 0,45s e some por ~4,8s. */
.r-eternal .lustro i {
  --ciclo: var(--lustro-ciclo, 5.3s);
  top:-30%; bottom:-30%; left:0; width:52%; filter: blur(2px);
  background: linear-gradient(100deg, transparent 0%,
    color-mix(in srgb, var(--cor-rar) 30%, transparent) 34%,
    color-mix(in srgb, #ffffff 72%, var(--cor-rar)) 50%,
    color-mix(in srgb, var(--cor-rar) 30%, transparent) 66%, transparent 100%);
  animation: lustrar var(--ciclo) linear var(--atraso) infinite;
}

/* ⚠⚠ TODO `@keyframes` DESTE PALCO SÓ PODE TOCAR `transform` E `opacity`. Qualquer outra
   propriedade tira a animação do compositor e devolve a repintura por quadro — que é exatamente o
   que esta migração veio remover. A guarda prende isso. */
/* ⚠⚠ OS PERCURSOS NÃO SÃO CHUTE — foram CALCULADOS pra reproduzir o ritmo antigo. `background-position`
   em % desloca a imagem por `p × (container − imagem)`, o que é bem diferente de transladar o
   elemento; migrar "no olho" mudaria a velocidade e o vazio entre passadas sem ninguém pedir.
   Conferido: herói 1,11s visível / 1,29s de vazio; mítico 0,86s / 5,94s — os mesmos de antes. */
/* ⚠ recalculado junto com a largura (34%): o percurso é em % do PRÓPRIO elemento, então afinar sem
   mexer aqui teria desacelerado a passada de 1,50s pra 2,26s. Hoje: 1,20s visível / 5,60s de vazio. */
@keyframes passar { from { transform: translate3d(-1020%, 0, 0) } to { transform: translate3d(1214%, 0, 0) } }
/* de BAIXO pra CIMA — mesmo sentido e mesma proporção de vazio de antes */
@keyframes subir  { from { transform: translate3d(0, 1027%, 0) } to { transform: translate3d(0, -1041%, 0) } }
/* ⚠ `abrir` (o aro do ascendente) foi REMOVIDO — era o único quadro deste palco que escalava, e
   escala em laço é o gatilho vestibular que o eterno já tinha custado. A guarda abaixo agora proíbe
   `scale` em QUALQUER quadro daqui, não só no `lustrar`: a regra nunca foi sobre um degrau. */
/* o ascendente DESCE. ⚠ Os dois números não são simétricos de propósito: o elemento começa `top:0`
   com 110% de altura, então a folga que falta em cima é ~14 pontos maior que a de baixo.
   ⚠⚠ O PERCURSO FOI RECALCULADO QUANDO A FAIXA GANHOU OS 30° (era 1500%), e não é ajuste fino —
   é a mesma armadilha que já mordeu o herói duas vezes. Faixa inclinada tem PEGADA VERTICAL muito
   maior: além da própria espessura, ela varre `largura × tan(θ)` de altura ao atravessar. Medido em
   alturas do thumb: 0,98 a 12° → 1,72 a 30°. Só girar teria esticado a passada de 0,81s pra 1,12s
   (+38%) e comido o silêncio — ou seja, teria desfeito sozinho o "acende duas vezes e fica muito
   tempo quieta", que é a identidade do degrau.
   ⭐ Com 2068% a janela volta ao que o dev já tinha visto: 0,81s de passada, 5,99s de vazio. */
@keyframes descer { from { transform: translate3d(0, -1041%, 0) } to { transform: translate3d(0, 1027%, 0) } }
/* ⚠ o percurso inteiro acontece nos primeiros 8,5% do ciclo (0,45s de 5,3s); o resto do tempo a
   faixa fica parada FORA do quadro. Parada fora, ela não pinta nada — o custo é só o do compositor
   segurando a camada. */
@keyframes lustrar {
  0%    { transform: translate3d(-120%, 0, 0) }
  8.5%  { transform: translate3d(230%, 0, 0) }
  100%  { transform: translate3d(230%, 0, 0) }
}
/* ⚠⚠ FORA DA JANELA, A ANIMAÇÃO PAUSA — a outra metade do "centenas de itens". O
   `content-visibility` pula pintura, mas animação de compositor continua TICANDO num card que
   ninguém vê: camada viva + compositor ocupado, multiplicado pelo inventário inteiro. A classe
   `na-tela` vem de um IntersectionObserver (ver `vigiarCards`); sem ela, tudo pausado.
   ⚠ Pausada, não removida: remover a animação zeraria a FASE do item ao rolar de volta.
   ⚠⚠ E este bloco vem DEPOIS de toda regra de degrau DE PROPÓSITO: em `.lustro i` ele EMPATA em
   especificidade com `#grade.r-… .lustro i` (1,2,2), e no empate a ordem decide — antes deles, o
   atalho `animation:` de cada degrau (que embute `running`) venceria e a pausa não pegaria. É a
   terceira vez que o atalho de animação morde esta frente; ver a guarda. */

/* ⚠⚠ A HUD PARADA (28/09/2026 — o PC "gritando"; `atualizarHudParado` no play.ts): minimizado, ou fora de foco com um "FPS fora de
   foco" escolhido, o padrão e o lustro das peças param — com a bolsa e o depósito abertos eram mais de cem camadas andando em todo
   vsync do monitor, sem ninguém olhar. Esta regra vem DEPOIS das dos degraus, pela mesma razão da de cima (o atalho `animation:`
   embute `running`), e o `!important` a põe acima de qualquer uma delas. Na volta, cada animação é posta de novo na linha do
   relógio comum (`realinharNaLinhaDoTempo`, rarity-visual.ts): o brilho volta onde estaria, sem pular. */
html.hud-parado .plano, html.hud-parado .lustro i { animation-play-state: paused !important; }

/* ⭐ NO JOGO, A PEÇA FORA DA VISTA TAMBÉM PARA (29/09/2026 — dev: "o que tá fora de vista deveria ser economizado em recursos"): a
   peça rolada para fora da bolsa ou do depósito animava o padrão e o lustro como as da vista. A classe `fora-da-vista` vem da vigia do
   play.ts (`vigiarBrilhoForaDaVista`, um IntersectionObserver como o `vigiarCards` do catálogo), e sem ela a peça anda: o que acabou
   de nascer brilha já. Na volta, a animação entra de novo na linha do relógio comum (`realinharNaLinhaDoTempo`). Depois dos degraus,
   pela mesma razão das de cima. */
.thumb.fora-da-vista .plano, .thumb.fora-da-vista .lustro i { animation-play-state: paused !important; }

