/* screens.css — telas de tela cheia dentro de #ui-root (js/ui/screens/*): auth, seleção/criação de personagem, lobby.
   Layout pensado em unidades de 640×360; toda medida de arte é calc(N * var(--px)). */

.screen {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
}

.screen-header {
  position: absolute;
  top: calc(4 * var(--px));
  left: calc(4 * var(--px));
  right: calc(4 * var(--px));
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: calc(4 * var(--px));
}

.screen-title {
  font-size: var(--text-2);
  color: var(--c-gold);
  text-shadow: var(--px) var(--px) 0 #000;
}

.screen-footer {
  position: absolute;
  left: calc(4 * var(--px));
  right: calc(4 * var(--px));
  bottom: calc(4 * var(--px));
  display: flex;
  justify-content: center;
  gap: calc(4 * var(--px));
}

/* Seleção de personagem: cartões lado a lado. */
.char-list {
  display: flex;
  gap: calc(8 * var(--px));
  align-items: stretch;
}

.char-card {
  width: calc(120 * var(--px));
  min-height: calc(160 * var(--px));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(4 * var(--px));
  cursor: pointer;
}
.char-card.is-selected { outline: var(--px) solid var(--c-gold); }
.char-card.is-empty { justify-content: center; color: var(--c-text-dim); }
.char-card-name { color: var(--c-gold); }
.char-card-meta { color: var(--c-text-dim); }

/* Criação de personagem: formulário em duas colunas (opções | prévia). --- showcase --- (docs/specs/showcase.md) a
   prévia virou a VITRINE DA CLASSE — uma janela para o jogo em escala 1×, com a distância real entre o herói e os
   bonecos (o arqueiro atira de ~260 px) — e passou de 128 para 352 px. O formulário mede o que o conteúdo MÍNIMO dele
   pede (`min-content`: as filas Rosto/Cabelo com o retrato dão 276 px, medido no navegador em 2026-09-30 — os "264" do
   desenho eram nominais, e a grade antiga de 400 já transbordava 12 px) e a vitrine fica com o resto: 632 = 276 + 4 +
   352, 4 px de margem de cada lado (a do título). Formulário mais largo um dia = vitrine mais estreita, nunca a tela cortada. */
.char-create {
  display: grid;
  grid-template-columns: min-content minmax(0, 1fr);
  gap: calc(4 * var(--px));
  width: calc(632 * var(--px));
}

.option-row {
  display: flex;
  align-items: center;
  gap: calc(4 * var(--px));
  margin-bottom: calc(4 * var(--px));
}
.option-row-label { min-width: calc(48 * var(--px)); color: var(--c-text-dim); }
.option-row .btn { min-width: calc(40 * var(--px)); }

/* --- classes-v3 --- 14 classes em três ABAS de peso de armadura (Pesada · Média · Leve) e grade de 3 colunas. A aba ocupa a
   linha que era do rótulo "Classe" (~+2 px) e a grade nunca passa de 2 linhas (5 classes = 3+2). Com 2 vãos de
   4 px em 264 px de arte sobram 85 px por botão, o bastante para "Eletromante" (11 letras). 4 colunas × 14 classes dariam
   4 linhas (+28 px), que a coluna de 360 px não tem (abaixo ainda vêm gênero, rosto e cabelo). FALLBACK da §2 do pacote 15 aplicado
   (abas sem margin-bottom, grade com gap de 2 px) e, pela P14 de classes-v3, a linha "Armadura: …" fora da ficha e sem os
   8 px de margem depois dela (regra .screen-create .option-row--stack + .text-dim, mais abaixo). --- /classes-v3 --- */
.option-row--stack {
  display: flex;
  flex-direction: column;
  align-items: stretch;
}
.class-tabs {
  display: flex;
  gap: calc(4 * var(--px));
}
.class-tabs .btn { flex: 1; min-width: 0; }
.class-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: calc(2 * var(--px));
  flex: 1;
}
.class-grid .btn { min-width: 0; }

/* --- uinav --- As duas fileiras do rodapé antigo morreram na reforma da navegação
   (docs/specs/ui-gameplay.md): a fila de oito botões de jogo (centrada, bottom 26) e a
   fileira da conta (bottom 8). Medida na última vez em que existiu: 636 px de arte em 640 — 4 px de folga, ou
   seja, o próximo destino a entrar quebraria a fila em duas linhas. No lugar delas há UMA faixa, `.lobby-bar`
   logo abaixo, e destino novo entra como LINHA da tabela de `js/ui/lobbyNav.js`, nunca como botão novo aqui.
   --- /uinav --- */

/* z 2 = contexto de empilhamento PRÓPRIO, logo acima da ilha e do mapa de nódulos (z 1) e abaixo dos seletores (9) e das
   janelas (10+). Não é enfeite: a Faixa A leva furo — o rosto do cartão do duelo (`widgets/duelPrompt.js`) e o dos retratos
   de uma janela arrastada para o alto (`widgets/portrait.js › FAIXA_A`) —, e a máscara do furo cria contexto de empilhamento. Sem
   z próprio, o painel do selo da ilha que mora nela (`.island-next`, z 9) ficaria preso no z 0 da faixa e afundaria para
   baixo da ilha enquanto o furo existisse; com z 2 ele já está nesse contexto, e o furo não muda nada da pilha. */
.lobby-topbar {
  position: absolute;
  z-index: 2;
  top: calc(4 * var(--px));
  left: calc(4 * var(--px));
  right: calc(4 * var(--px));
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: calc(4 * var(--px));
}
/* O selo do título sem altura própria (a moldura dele entra nas margens negativas): ele mede o TEXTO mais a borda 4+4
   (`padding-inline: 0`), e é essa a conta do pior caso da ficha do herói (`client/tests/ui/lobbyHud.test.js`). */
.lobby-topbar .title-chip { padding: 0; line-height: calc(10 * var(--px)); cursor: inherit; }
.lobby-topbar .title-chip.has-frame { margin: calc(-4 * var(--px)) 0; }

/* --- 2026-10-01 (pedido do dono: "está muito simples … pensar como se fosse uma interface mobile, com o celular deitado,
   alguns botões grandes") --- A FAIXA A (docs/specs/ui-gameplay.md §0.1): CINCO PEÇAS soltas sobre o céu do lobby, cada uma
   na sua placa — o retrato (44×44), a ficha do herói (249), a da aventura (213), o selo da ilha (56) e a carteira (54) —,
   absolutas em px de arte (`js/ui/lobbyHud.js › HUD_PIECES` dá a tabela; `widgets/lobbyBand.js` as posiciona com `placeAt`).
   A caixa `.lobby-hud` não tem moldura nem fundo: o céu aparece nos vãos de 4. A altura é FIXA (44) porque
   `windows/windowManager.js › lobbyBands` mede a `.lobby-topbar` AO VIVO e `windowLayout.js › fitLobbyTop` desce as janelas
   para `y + h + 2` = 50 — a mais alta (a ficha de Inspecionar, 280) fecha em 330 ≤ 332. Peça mais alta que a faixa é janela
   nascendo por cima dela: se não couber, encolha o CONTEÚDO. Nenhum texto leva reticência (o pior caso de cada peça cabe
   inteiro — o teste mede o texto real). */
.lobby-hud { position: relative; flex: 1; min-width: 0; }
/* Os três grupos são `role="group"` (o `aria-label` por extenso, G7) e não têm caixa: as peças são absolutas na faixa. */
.hud-row { position: static; height: 0; }
.lobby-hero, .lobby-plate, .lobby-isle, .lobby-wallet { position: absolute; box-sizing: border-box; }
.lobby-hero.hidden, .lobby-plate.hidden, .lobby-isle.hidden { display: none; }
/* A PLACA (`hud_plate`, 9-slice slice 4: aro de aço com rebite de ouro e miolo fundo — `art/src/ui/hud_plates.py`). O texto
   mora na caixa útil, por dentro do aro: o que fosse posto em cima dele sairia por baixo da moldura (o B64 da faixa antiga). */
.lobby-plate {
  border: calc(4 * var(--px)) solid transparent;
  border-image: var(--ui-hud_plate, url(../assets/ui/hud/hud_plate.png)) 4 fill / calc(4 * var(--px)) stretch;
  image-rendering: pixelated;
}
/* As duas LINHAS de uma placa: 14 de altura (a da barra de EXP), na caixa útil; o `top` vem de `lobbyHud.js › hudLineTop`.
   Sem `flex-wrap`: nada quebra numa terceira linha. */
.lobby-line {
  position: absolute;
  left: 0;
  right: 0;
  height: calc(14 * var(--px));
  display: flex;
  align-items: center;
  gap: calc(4 * var(--px));
  white-space: nowrap;
  line-height: calc(10 * var(--px));
}
.lobby-power, .lobby-eta { flex: none; }
.lobby-eta { text-align: right; }
/* os ociosos encostam à direita da linha do marco (a exceção não empurra o marco) */
.lobby-workers { margin-left: auto; }
/* O RETRATO (44×44): a moldura de ouro `hud_portrait` (9-slice slice 4, SEM `fill`: o miolo é o slot de 36 do rosto,
   `widgets/portrait.js`). A moldura é a RAIZ do furo do rosto; o brasão e o selo do nível são IRMÃOS dela e pintam por cima
   do canto do rosto (o furo não os recorta). O botão inteiro abre os Atributos. */
.lobby-hero { margin: 0; padding: 0; border: 0; background: none; font: inherit; color: inherit; cursor: pointer; }
.lobby-portrait {
  position: absolute;
  inset: 0;
  box-sizing: border-box;
  border: calc(4 * var(--px)) solid transparent;
  border-image: var(--ui-hud_portrait, url(../assets/ui/hud/hud_portrait.png)) 4 / calc(4 * var(--px)) stretch;
  image-rendering: pixelated;
}
.lobby-portrait > .slot.portrait { cursor: inherit; }
.lobby-hero:hover .lobby-level, .lobby-hero:focus-visible .lobby-level { color: var(--c-text); border-color: var(--c-text); }
.lobby-hero:focus-visible { outline: var(--px) solid var(--c-gold); outline-offset: var(--px); }
/* o brasão da classe (`ico_class_<key>` 12×12, a série do Ranking), cravado no canto de baixo à esquerda da moldura */
.lobby-crest {
  position: absolute;
  left: 0;
  bottom: 0;
  width: calc(12 * var(--px));
  height: calc(12 * var(--px));
  background: center / 100% 100% no-repeat;
  image-rendering: pixelated;
  pointer-events: none;
}
.lobby-crest.hidden { display: none; }
/* o NÍVEL num selo de ouro (a moldura de 1 px do `.hud-seal`, sobre o fundo dos campos), no canto de baixo à direita */
.lobby-level {
  position: absolute;
  right: calc(-2 * var(--px));
  bottom: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: calc(2 * var(--px));
  box-sizing: border-box;
  min-width: calc(14 * var(--px));
  height: calc(11 * var(--px));
  padding: 0 calc(2 * var(--px));
  border: var(--px) solid var(--c-gold);
  background: var(--c-field-bg);
  color: var(--c-gold);
  line-height: calc(9 * var(--px));
  white-space: nowrap;
  pointer-events: none;
}
/* O SELO DA ILHA (56×44, `#lobby-island`): a placa de aço; o ícone `ico_island` 16×16 + `Nv N` em cima e a barra do que já
   está à mão para o próximo nível embaixo (`bar_fill_cast`, o verde: a barra de EXP do herói é a laranja). */
.lobby-isle {
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(3 * var(--px));
  border: calc(4 * var(--px)) solid transparent;
  border-image: var(--ui-hud_plate, url(../assets/ui/hud/hud_plate.png)) 4 fill / calc(4 * var(--px)) stretch;
  background: none;
  font: inherit;
  color: inherit;
  image-rendering: pixelated;
  cursor: pointer;
}
.lobby-isle:hover .lobby-isle-lv, .lobby-isle.is-open .lobby-isle-lv { color: var(--c-gold); }
.lobby-isle:focus-visible { outline: var(--px) solid var(--c-gold); outline-offset: var(--px); }
.lobby-isle-top { display: inline-flex; align-items: center; gap: calc(3 * var(--px)); line-height: calc(10 * var(--px)); white-space: nowrap; }
.lobby-isle-ico {
  flex: none;
  width: calc(16 * var(--px));
  height: calc(16 * var(--px));
  background: var(--ui-ico_island, none) center / 100% 100% no-repeat;
  image-rendering: pixelated;
}
.lobby-isle-bar { flex: none; width: calc(42 * var(--px)); }
.lobby-isle.is-max .lobby-isle-lv { color: var(--c-gold); }
/* DÁ PARA EXPANDIR (o `▲` do painel está livre): a placa de OURO (`hud_plate_gold`; G2 — ouro = pede ação), o martelo
   `ico_lot_up` no canto (o selo "pode subir" do lote da Casa na ilha e do selo do prédio nas janelas — o mesmo quadro aceso,
   `island-badge-lit` de island.css) e um aro de 1 px que pulsa em `steps`. A cor nunca vem sozinha (§8.7): o martelo e a
   barra cheia dizem o mesmo. Movimento reduzido: tudo parado, no quadro base. */
.lobby-isle.is-ready {
  border-image-source: var(--ui-hud_plate_gold, url(../assets/ui/hud/hud_plate_gold.png));
  animation: lobby-isle-pulse 1.6s steps(1, end) infinite;
}
.lobby-isle.is-ready .lobby-isle-lv { color: var(--c-gold); }
/* o aro aceso ~35 % do ciclo (o martelo acende ~15 % do dele): chama sem virar pisca-pisca */
@keyframes lobby-isle-pulse {
  0%   { box-shadow: 0 0 0 var(--px) var(--c-gold); }
  35%  { box-shadow: none; }
  100% { box-shadow: none; }
}
.lobby-isle-up {
  position: absolute;
  right: calc(-6 * var(--px));
  top: calc(-6 * var(--px));
  width: calc(12 * var(--px));
  height: calc(12 * var(--px));
  background: var(--ui-ico_lot_up, none) no-repeat 0 0 / 100% 100%;
  image-rendering: pixelated;
  pointer-events: none;
}
.lobby-isle-up::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--ui-ico_lot_up_lit, none) no-repeat 0 0 / 100% 100%;
  image-rendering: pixelated;
  opacity: 0;
  animation: island-badge-lit 2.4s step-end infinite;
}
.lobby-isle-up.hidden { display: none; }
@media (prefers-reduced-motion: reduce) {
  .lobby-isle.is-ready, .lobby-isle-up::after { animation: none; }
}
/* A CARTEIRA (54×44) — --- focus --- (docs/specs/focus.md §8) de DUAS MOEDAS: a PÍLULA do ouro (20) em cima e a do Foco (20)
   embaixo, as duas na placa de aço (`hud_plate`): 20 + 4 + 20 = 44, encostando nas linhas de cima e de baixo da faixa como as
   outras peças (revisão de 2026-10-01: o que flutua no meio da faixa lia solto; o selo dos conectados sem placa lia como um furo no
   céu). Sem o Foco (desligado, ou antes do 1.º `focus.state`) o ouro fica no alto, onde estava — nada pula quando o Foco chega. O
   selo dos conectados foi para o canto da linha da barra (abaixo). As alturas vêm de `lobbyHud.js › HUD_PILL`/`HUD_FOCUS_PILL`/
   `HUD_ONLINE_H` (o `lobbyBand.js` as escreve). */
.lobby-wallet { display: flex; flex-direction: column; align-items: stretch; justify-content: flex-start; gap: calc(4 * var(--px)); }
.lobby-pill, .lobby-online {
  box-sizing: border-box;
  justify-content: flex-end;
  padding: 0 calc(1 * var(--px));
  border: calc(4 * var(--px)) solid transparent;
  border-image: var(--ui-hud_plate, url(../assets/ui/hud/hud_plate.png)) 4 fill / calc(4 * var(--px)) stretch;
  image-rendering: pixelated;
  white-space: nowrap;
}
.lobby-pill { line-height: calc(10 * var(--px)); }
.lobby-pill.hidden, .lobby-online.hidden { display: none; }
/* o miolo do selo dos conectados tem 8 (16 − o aro de 4 + 4): o ponto `ico_online` 8×8 e o número de 8 */
.lobby-online { line-height: calc(8 * var(--px)); }
/* --- focus --- A PISTA na pílula (o ouro abreviado, o Foco perto do tanque): o pontilhado geral de base.css (`[data-hint].has-more`)
   trocava o aro de 4 de baixo por 1 — sob a placa em `border-image` ele não aparece (a promessa invisível, UX-GRAMATICA G4) e o miolo
   crescia 3 px, descendo o número e jogando a tira do Foco em cima da moldura (medido em 2026-10-07 pela cena `foco-medir` do kit: a
   borda `1px dotted`, a tira em y 60 na moldura de 59…63). O aro volta a 4 e o pontilhado abraça o NÚMERO, no molde do `.lobby-here`.
   No Foco (pista só perto do tanque) a tira mora logo abaixo da linha do número: a linha encolhe a 9 e o pontilhado é o 10.º px, em
   cima da tira — com a margem −1 do ouro ele cairia debaixo dela. */
#ui-root .hud-meter.lobby-pill[data-hint].has-more { border-bottom: calc(4 * var(--px)) solid transparent; }
#ui-root .hud-meter.lobby-pill[data-hint].has-more > .text-gold:last-child { border-bottom: var(--px) dotted rgba(244, 240, 230, 0.4); margin-bottom: calc(-1 * var(--px)); }
#ui-root .hud-meter.lobby-pill[data-hint].has-more > .focus-num { border-bottom: var(--px) dotted rgba(244, 240, 230, 0.4); line-height: calc(9 * var(--px)); }
/* --- focus --- O FOCO perto do tanque (o crédito da próxima virada não cabe): a placa de OURO (G2: ouro = pede ação), o número em
   ouro e a TIRA de 2 px no pé da caixa útil, o saldo ÷ o tanque (o molde da `.band-strip` de guide.css, na cor do Foco) — forma E
   cor, nada só por cor (§8.7). O padding de 2 embaixo sobe o número 1 px: a tira não encosta na letra. */
.lobby-focus { position: relative; }
.lobby-focus.is-near { border-image-source: var(--ui-hud_plate_gold, url(../assets/ui/hud/hud_plate_gold.png)); padding-bottom: calc(2 * var(--px)); }
.lobby-focus.is-near > .focus-num { color: var(--c-gold); }
.lobby-focus-tank { display: none; position: absolute; left: 0; right: 0; bottom: 0; height: calc(2 * var(--px)); background: var(--c-shade); pointer-events: none; }
.lobby-focus.is-near > .lobby-focus-tank { display: block; }
.lobby-focus-fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: var(--c-focus); }
/* --- focus --- OS CONECTADOS no CANTO da linha da barra do lobby (`lobbyHud.js › HUD_ONLINE_CORNER`: x … 636, y 338…354, o
   lugar do antigo "Jogadores online"), filho da tela, por baixo do aviso de conexão (`.hud-net`, z 55, que sem conexão o cobre: o
   número estaria velho). A barra acaba em x 432. A pilha de toasts POUSA com o pé em y 332 (`toast.js › TOAST_REST_BOTTOM`), mas
   desce até o piso (y 356, `TOAST_MIN_BOTTOM`) quando uma janela baixa não a deixa caber em cima: aí ela cobre o selo pelos 3,2 s do
   aviso — aceito (polimento, 2026-10-07): o selo é um número de leitura, não um controle, e o aviso é o que acabou de acontecer. Some
   com a carta do mar, como a barra. */
.lobby-online.is-corner { position: absolute; pointer-events: auto; }
.screen-lobby.is-node-map > .lobby-online.is-corner { display: none; }
/* O PAINEL DO PRÓXIMO NÍVEL DA ILHA (`widgets/islandNext.js`, `#lobby-island-panel`): por baixo do selo, encostado à direita
   dele (x 334…578 do palco, a partir de y 50), por cima da ilha, na camada dos SELETORES DE MODO (z 9: acima dela, abaixo das
   janelas — "Onde conseguir" aberta por um material nasce por cima). Abrir o painel fecha as janelas, o menu e o seletor
   aberto (o painel nunca nasce por baixo de nada), e abrir um seletor fecha o painel. A moldura de 4 é a da barra da ilha
   (`.tooltip-frame`); o custo é a TABELA do cartão da janela Construir (`.ib-cost`: ícone · valor à direita · ✔/✖) e o
   material, a casinha de 16 com o selo de tier. */
.island-next {
  position: absolute;
  top: calc(46 * var(--px));
  right: calc(54 * var(--px) + 4 * var(--px));
  z-index: 9;
  box-sizing: border-box;
  width: calc(244 * var(--px));
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: calc(4 * var(--px));
  padding: calc(2 * var(--px)) calc(4 * var(--px)) calc(4 * var(--px));
  white-space: nowrap;
  pointer-events: auto;
}
.island-next.hidden { display: none; }
.island-next-head { display: flex; align-items: center; gap: calc(4 * var(--px)); line-height: calc(10 * var(--px)); }
.island-next-ico {
  flex: none;
  width: calc(16 * var(--px));
  height: calc(16 * var(--px));
  background: var(--ui-ico_island, none) center / 100% 100% no-repeat;
  image-rendering: pixelated;
}
.island-next-to { color: var(--c-gold); }
.island-next-close { margin-left: auto; padding: 0 calc(3 * var(--px)); }
/* o que o nível DÁ: "Libera" + `+4 vagas` numa linha, e os prédios novos em duas colunas (ícone 16 + o nome escrito) */
.island-next-gives { display: flex; flex-wrap: wrap; align-items: center; column-gap: calc(4 * var(--px)); row-gap: calc(2 * var(--px)); }
.island-next-gives.hidden, .island-next-slots.hidden, .island-next-blds.hidden { display: none; }
.island-next-blds {
  flex-basis: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: calc(4 * var(--px));
  row-gap: calc(2 * var(--px));
}
.island-next-bld { display: flex; align-items: center; gap: calc(3 * var(--px)); min-height: calc(16 * var(--px)); line-height: calc(10 * var(--px)); }
.island-next-bld-ico { flex: none; width: calc(16 * var(--px)); height: calc(16 * var(--px)); background: center / 100% 100% no-repeat; image-rendering: pixelated; }
/* a tabela do custo tem a largura do conteúdo (esticada nos 236 do painel, o valor ia parar longe do ícone) */
.island-next-cost { padding-top: 0; align-self: flex-start; }
.island-next-nocol { display: block; width: calc(16 * var(--px)); height: var(--px); }
.island-next-cost.hidden, .island-next .btn.hidden { display: none; }
.island-next > .btn { align-self: flex-end; }
/* Botão de texto da faixa: sem moldura (não é `.btn`), 10 px de altura com a borda de 1 px da PISTA reservada embaixo —
   com ou sem pista (`.has-more`, base.css) a caixa mede o mesmo, e a linha 2 não cresce. */
.hud-btn {
  position: relative;
  display: flex;
  align-items: center;
  gap: calc(3 * var(--px));
  box-sizing: border-box;
  height: calc(10 * var(--px));
  margin: 0;
  padding: 0;
  border: 0;
  border-bottom: var(--px) solid transparent;
  background: none;
  font: inherit;
  color: inherit;
  line-height: calc(9 * var(--px));
  white-space: nowrap;
  flex: none;
  cursor: pointer;
}
/* O ALVO DE TOQUE (pedido do dono, 2026-10-01: "alguns botões grandes", o celular deitado — no palco em 1× um px de arte é
   um px de tela): o botão de texto MOSTRA 10, mas o dedo acerta 18 — um `::after` invisível estica o alvo 4 px para cima e
   para baixo (a linha de 14 + metade do vão de 4 de cada lado: a linha 1 vai de y 4 a 22 da placa e a linha 2 de 22 a 40,
   sem invadir a vizinha nem passar do aro). O texto continua com 8 px (a escala é a do palco, §8.7 — roadmap §4
   `topo-celular-escala`). */
.hud-btn::after { content: ''; position: absolute; left: 0; right: 0; top: calc(-4 * var(--px)); bottom: calc(-5 * var(--px)); }
.hud-btn:hover { color: var(--c-gold); }
/* a pista de `base.css` põe `cursor: help`; aqui o gesto é também um CLIQUE (o seletor, o modo, a barra da Casa) */
#ui-root .hud-btn[data-hint].has-more { cursor: pointer; }
.lobby-identity, .lobby-here { gap: calc(4 * var(--px)); }
/* Na ficha da aventura, o `here` (ou a estreia) e o marco tomam a LINHA inteira: o dedo acerta a linha, não a palavra (antes,
   `◎ Nv 12` era um alvo de 35×10 numa placa de 213 que não fazia nada fora do texto). A PISTA continua marcando só as PALAVRAS
   (G4 — a listra solta da linha era o recusado de ux-casos §27): o pontilhado sai da caixa e vai para o primeiro filho. */
.lobby-here, .lobby-next, .lobby-first-run { flex: 1 0 auto; justify-content: flex-start; }
/* a estreia é o convite da faixa: ouro, como era o texto (o `color: inherit` do `.hud-btn` apagava o `.text-gold`) */
.hud-btn.lobby-first-run { color: var(--c-gold); }
#ui-root .hud-btn.lobby-here[data-hint].has-more, #ui-root .hud-btn.lobby-next[data-hint].has-more { border-bottom-color: transparent; }
#ui-root .hud-btn.lobby-here[data-hint].has-more > :first-child,
#ui-root .hud-btn.lobby-next[data-hint].has-more > :first-child { border-bottom: var(--px) dotted rgba(244, 240, 230, 0.4); margin-bottom: calc(-1 * var(--px)); }
/* O nome é o `.hud-name` dourado do HUD da corrida (cor e sombra); a regra base de `hud.css` é da corrida e não se mexe
   (`hudHeroLevel.test.js`). Aqui ele só não encolhe nem corta: o pior caso (16 letras) cabe. */
.lobby-hud .hud-name { display: inline; overflow: visible; flex: none; }
.hud-meter { display: inline-flex; align-items: center; gap: calc(3 * var(--px)); flex: none; }
/* O SELO de estado (G2): moldura de 1 px + palavra curta + cor. 1 + 8 + 1 = 10 px de altura — a linha 2 não cresce. */
.hud-seal {
  display: inline-flex;
  align-items: center;
  gap: calc(2 * var(--px));
  box-sizing: border-box;
  height: calc(10 * var(--px));
  padding: 0 calc(2 * var(--px));
  border: var(--px) solid currentColor;
  line-height: calc(8 * var(--px));
  flex: none;
}
.hud-seal.is-gold { color: var(--c-gold); }
.hud-seal.is-lock { color: var(--c-danger); }
/* os ÚNICOS ícones menores que 16 da reforma (D32): vivem em linha com texto de 8 px */
.hud-ico { flex: none; background: center / 100% 100% no-repeat; image-rendering: pixelated; }
.hud-ico.is-power { width: calc(9 * var(--px)); height: calc(9 * var(--px)); background-image: var(--ui-ico_power); }
.hud-ico.is-gold { width: calc(9 * var(--px)); height: calc(9 * var(--px)); background-image: var(--ui-ico_gold); }
.hud-ico.is-focus { width: calc(9 * var(--px)); height: calc(9 * var(--px)); background-image: var(--ui-ico_focus); }
.hud-ico.is-target { width: calc(9 * var(--px)); height: calc(9 * var(--px)); background-image: var(--ui-ico_target); }
.hud-ico.is-online { width: calc(8 * var(--px)); height: calc(8 * var(--px)); background-image: var(--ui-ico_online); }
.hud-ico.is-worker { width: calc(9 * var(--px)); height: calc(9 * var(--px)); background-image: var(--ui-ico_worker); }
/* o ícone do MODO no marco (`ico_dungeon`/`ico_tower`/`ico_advanced`: a peça 16×16 da linha da gaveta, sem escala — `Avançada`
   sozinha era um adjetivo sem substantivo). Passa do botão sem crescer a linha (a margem negativa: 16 − 3 − 4 = os 9 do miolo
   do botão, que tem 10 com a borda da pista — com −3/−3 ele sobrava 1 e centrava em meio px, borrado em 1×) e fica na caixa
   útil da placa (linha 2: y 23…39 da placa, o aro começa em 40). A key vem inline (`lobbyBand.js`). */
.hud-ico.is-mode { width: calc(16 * var(--px)); height: calc(16 * var(--px)); margin: calc(-3 * var(--px)) 0 calc(-4 * var(--px)); }
/* --- clan --- o emblema entra em 12×12: `widgets/clanEmblem.js` desenha em 16 por padrão, e um 16 aqui passaria 2 px da
   linha de 14 */
.hud-emblem { width: calc(12 * var(--px)) !important; height: calc(12 * var(--px)) !important; flex: none; }
/* A barra de EXP: 148×14 EXTERNOS = calha de 142×8 + moldura de 3 em cada lado. O `.bar` de nine-slice.css nasce com
   8 px de altura TOTAL (`box-sizing: border-box`, moldura de 3 inclusa), que é o tamanho do HUD da corrida; aqui ele
   precisa dos 14 da linha da ficha, como os `.hud-bars .bar` do hud.css já fazem com 10. A largura vem do `lobbyBand.js`
   (`lobbyHud.js › HUD_ID_LINE2.bar`). A barra do selo da ilha fica com os 8 da peça. */
.lobby-line > .bar { flex: none; height: calc(14 * var(--px)); }

/* Botões de janela do lobby (Mochila, Habilidades): ícone 16×16 à esquerda e selo no canto superior direito — pontos de
   habilidade a gastar (dourado) · mochila a partir de 90 % (dourado) e cheia (vermelho piscando em steps(2)). O selo é
   um <span> inerte; sai 5 px para fora do botão para não cobrir o rótulo. */
.lobby-window-btn { position: relative; }
.lobby-btn-icon {
  width: calc(16 * var(--px));
  height: calc(16 * var(--px));
  margin: calc(-2 * var(--px)) 0 calc(-2 * var(--px)) calc(-2 * var(--px));
  background: center / 100% 100% no-repeat;
  image-rendering: pixelated;
}
/* --- lobby-v2 --- (05 §3) os cinco ícones CONTEXTUAIS do `01` §2.6 (Contratar, Inspecionar, mascote, medidor de
   build, replay). Eles não são da barra do lobby, e por isso têm família própria: a key vem inline
   (`background-image: var(--ui-<key>)`, posto por `client/public/js/ui/widgets/button.js › button`) e a caixa é a
   mesma 16×16 da D32. A margem negativa de 2 px em cima e embaixo é a MESMA de `.lobby-btn-icon`, e pela mesma razão:
   o `.btn` cru tem 16 px de altura TOTAL com moldura de 4 (`client/public/css/nine-slice.css`), ou seja 8 de miolo —
   sem o desconto, um ícone de 16 faria todo botão da lista crescer para 24 e estourar janela de altura fixa. */
.btn-icon {
  width: calc(16 * var(--px));
  height: calc(16 * var(--px));
  margin: calc(-2 * var(--px)) 0;
  background: center / 100% 100% no-repeat;
  image-rendering: pixelated;
  flex: none;
}
/* --- /lobby-v2 --- */
.lobby-btn-icon.is-bag { background-image: var(--ui-ico_bag); }
/* --- durability --- a marca de durabilidade no botão Mochila da barra do lobby (irmã da do HUD, hud.css › .bag-dur). */
.lobby-dur { width: calc(9 * var(--px)); height: calc(9 * var(--px)); margin: 0 0 0 calc(2 * var(--px)); background: none no-repeat 0 0 / 100% 100%; image-rendering: pixelated; display: none; }
#btn-inventory.is-dur-worn .lobby-dur { display: inline-block; background-image: var(--ui-ico_dur_worn, none); }
#btn-inventory.is-dur-broken .lobby-dur { display: inline-block; background-image: var(--ui-ico_dur_broken, none); }
/* --- /durability --- */
/* --- clan --- */
.lobby-btn-icon.is-clan { background-image: var(--ui-ico_clan); }
/* --- /clan --- */
.btn > .lobby-badge {
  position: absolute;
  top: calc(-9 * var(--px));
  right: calc(-7 * var(--px));
  min-width: calc(11 * var(--px));
  height: calc(11 * var(--px));
  padding: 0 calc(2 * var(--px));
  line-height: calc(9 * var(--px));
  text-align: center;
  color: var(--c-text-dark);
  background: var(--c-gold);
  border: var(--px) solid #2a2118;
  box-shadow: 0 var(--px) 0 rgba(0, 0, 0, 0.5);
  pointer-events: none;
}
.btn > .lobby-badge.hidden { display: none; }
.btn > .lobby-badge.is-full {
  color: var(--c-text);
  background: var(--c-danger);
  animation: lobby-badge-blink 0.8s steps(2, jump-none) infinite;
}
@keyframes lobby-badge-blink {
  from { background: var(--c-danger); }
  to { background: #7a241d; }
}
/* o selo não desce com o bisel do :active (só o rótulo desce) */
.btn:active > .lobby-badge { transform: none; }

/* --- uinav --- Reforma da navegação (docs/specs/ui-gameplay.md).
   A barra: UMA faixa à esquerda (y 336–352), com os modos de jogo, a mochila e `≡ Menu` — e nada mais, nunca
   (destino novo = uma linha da tabela de `js/ui/lobbyNav.js`). "Jogadores online" continua onde estava, à direita.
   O painel: duas colunas de 128 px com 4 de vão; a moldura de 4 px é o `border-image` do `.tooltip-frame` (é ela o
   `MENU_PAD` da conta: 4 + 128 + 4 + 128 + 4 = 268 de fora a fora, com `box-sizing: border-box`) — por isso o painel
   NÃO leva padding próprio, que somaria 8 px e furaria a medida. z-index 60 = o do `.ctx-menu`. */
/* --- lobby-v2 --- (02 §1, D34) A faixa tem largura e altura FIXAS: x 4…432 (428 de largura) porque x 436…636 é do
   `.hud-net` (hud.css: `right: 4; max-width: 200`) e da `.toast-stack` (windows.css) — essa âncora É o conserto do
   B51. Altura 20 com `align-items: center` para o topo ser y 336 com o trilho cheio OU vazio: `windowManager.js ›
   lobbyBands` mede este retângulo ao vivo, e sem altura fixa o y de nascimento das 24 janelas passaria a depender de
   o jogador ter carta não lida. */
/* B80 (avaliação de UX de 2026-09-23): a barra fica ACIMA de qualquer janela. Janela e barra são irmãs nesta camada e
   a janela é anexada depois, então sem z ela ganhava por ordem de DOM: com a Mochila arrastada ao limite,
   `elementFromPoint` no centro dos controles devolvia `win-inventory` e Exploração, ▾ e Masmorra paravam de responder.
   z de janela = `10 + índice` (windowManager › sync), no máximo `9 + WINDOW_IDS.length` = 42 com as 33 janelas de hoje
   (a 32.ª é a `war`, Guerras — --- guerra ---; a 33.ª a `warband`, o Grupo Guerreiro — --- warband ---); 43 fica acima delas e abaixo do tooltip (50), do menu (60) e do modal
   (100/200). Janela nova = este número sobe junto (`client/tests/ui/scrollReach.test.js` exige z > 9 + WINDOW_IDS.length e
   < 50) — e o VÉU DO RESUMO DA CORRIDA (`.summary-backdrop`, abaixo) tem de continuar UMA unidade acima da barra.
   Sozinho isto esconderia a faixa do título da janela arrastada para baixo — por isso o arrasto também recebe o piso
   da barra (windowLayout.js › dragStage). */
.lobby-bar {
  position: absolute;
  left: calc(4 * var(--px));
  bottom: calc(4 * var(--px));
  width: calc(428 * var(--px));
  height: calc(20 * var(--px));
  z-index: 43;
  display: flex;
  align-items: center;
  gap: calc(4 * var(--px));
}
/* split button `▶ Explorar` (D18): dois <button> IRMÃOS num role="group" — um controle, dois gestos */
.lobby-split { display: flex; }
.lobby-btn-chevron { width: calc(10 * var(--px)); flex: none; }
/* o trilho (04) é elástico e ancorado à direita do vão; vazio ele SOME, para não deixar um buraco de 164 px na conta
   em dia — que é o estado normal de quem acabou de criar o personagem */
#lobby-rail {
  display: flex;
  gap: calc(4 * var(--px));
  margin-left: auto;
  width: auto;
  min-height: calc(20 * var(--px));
  pointer-events: auto;
}
#lobby-rail:empty { display: none; }
/* O RÓTULO do trilho ("Pronto:"): texto de 8 px alinhado ao centro dos slots de 20. Ele é `aria-hidden` porque cada
   slot já diz o que é no `aria-label` — o rótulo é para quem OLHA. Nasce só com slot vivo (`widgets/pendingRail.js`),
   senão o `:empty` de cima não tiraria o buraco do trilho vazio. Reserva de 40 px em `ui/lobbyNav.js`. */
.rail-label {
  align-self: center;
  white-space: nowrap;
  line-height: calc(10 * var(--px));
  pointer-events: none;
}
.lobby-btn-icon.is-play { background-image: var(--ui-ico_play); }
.lobby-btn-icon.is-menu { background-image: var(--ui-ico_menu); }
.lobby-btn-icon.is-storage { background-image: var(--ui-ico_bank16); }
/* --- ajustes (2026-09-30, decisão do dono) --- os botões SÓ de ícone da barra (`[baú]` Armazém e `[≡]`): 24 px = moldura de 4 +
   o ícone de 16 + 4, a conta de `lobbyNav.js › LOBBY_BAR` (`minW: 24`, `iconOnly`). O nome mora no `aria-label` e na dica. O
   rótulo vazio sai do fluxo (sem o vão de 2 do `.btn`) e o ícone perde o recuo de 2 à esquerda que tem ao lado de texto. */
.lobby-bar .btn.is-icon { width: calc(24 * var(--px)); padding: 0; justify-content: center; flex: none; }
.lobby-bar .btn.is-icon > .lobby-btn-icon { margin: calc(-2 * var(--px)) 0; }
.lobby-bar .btn.is-icon > span:empty:not(.lobby-btn-icon) { display: none; }
/* --- lobby-v2 --- (04 §5, D22) A regra que escondia o selo agregado do `≡ Menu` com a gaveta aberta saiu junto com o
   selo: não há mais nada no botão para esconder.
   --- lobby-v2 --- (04 §3.4) O TRILHO: um slot de 24×20 por pendência viva, com o ícone 16×16 centrado e o número
   DENTRO da moldura — o selo da Mochila sobe 5 px para dentro da faixa das janelas, e internalizar este faz o
   contrato "janela termina em y ≤ 332" valer sem asterisco. Nada existe só na cor (§8.7): todo slot tem
   `aria-label`, e o `count` imprime número. */
.rail-slot {
  position: relative;
  flex: none;
  width: calc(24 * var(--px));
  height: calc(20 * var(--px));
  padding: 0;
  border: calc(4 * var(--px)) solid transparent;
  border-image: url(../assets/ui/buttons/btn_secondary.normal.png) 4 fill / calc(4 * var(--px)) stretch;
  background: center / calc(16 * var(--px)) calc(16 * var(--px)) no-repeat;
  image-rendering: pixelated;
  cursor: pointer;
}
/* O ícone vai num `::before`, e não no `background` do botão: `border-image … 4 fill` pinta a fatia do MEIO da
   moldura por cima do background do elemento, e o ícone do slot saía invisível — os dois slots apareciam como
   retângulos cinzas mudos, e o de `kind: 'dot'`, que não tem número, não comunicava nada (medido no navegador em
   2026-09-23, `07` §3 linha 9 → PERGUNTAS 24). O pseudo-elemento é filho: pinta DEPOIS da moldura.
   `background-image: inherit` pega o `var(--ui-<key>)` que `widgets/pendingRail.js` escreve embutido no botão.
   Caixa: 16×16 (D32) centrada nos 24×20 do slot → (24−16)/2 = 4 e (20−16)/2 = 2 a contar da borda EXTERNA, que são
   0 e −2 a contar da caixa de padding (a borda tem 4 px em cada lado). */
.rail-slot::before {
  content: '';
  position: absolute;
  left: 0;
  top: calc(-2 * var(--px));
  width: calc(16 * var(--px));
  height: calc(16 * var(--px));
  background-image: inherit;
  background-size: calc(16 * var(--px)) calc(16 * var(--px));
  background-repeat: no-repeat;
  image-rendering: pixelated;
  pointer-events: none;
}
/* o `+N` não tem ícone: ele é o número (§3, `ui.lobby.pending.more`) */
.rail-slot.is-more::before { content: none; }
.rail-slot:hover { border-image-source: url(../assets/ui/buttons/btn_secondary.hover.png); }
.rail-slot:active { border-image-source: url(../assets/ui/buttons/btn_secondary.pressed.png); }
.rail-slot:focus-visible { outline: var(--px) solid var(--c-gold); outline-offset: calc(-1 * var(--px)); }
/* o número é um <span> INERTE, no molde do `.lobby-badge` mas por dentro: canto inferior direito do slot */
.rail-slot > .is-count {
  position: absolute;
  right: calc(-2 * var(--px));
  bottom: calc(-3 * var(--px));
  min-width: calc(8 * var(--px));
  height: calc(9 * var(--px));
  padding: 0 var(--px);
  line-height: calc(9 * var(--px));
  text-align: center;
  color: var(--c-text-dark);
  background: var(--c-gold);
  pointer-events: none;
}
/* o `+N` é o MESMO selo de ouro com tinta escura dos outros slots, só que CENTRADO (o slot não tem ícone): o "+2" dourado
   sem fundo sobre o aço do botão tinha contraste de ~1,6:1 e sumia na barra (revisão de 2026-10-01, ui-gameplay.md §7) */
.rail-slot.is-more { display: flex; align-items: center; justify-content: center; }
.rail-slot.is-more > .is-count { position: static; }
/* D35: a fila do PvP é ESTADO EM CURSO, não pendência — pulso em `steps`, nunca número (mesmo molde do `.npc-marker`) */
.rail-slot.is-pulse { animation: rail-pulse 0.8s steps(2, jump-none) infinite; }
@keyframes rail-pulse {
  from { filter: brightness(1); }
  to { filter: brightness(1.6); }
}
/* D37: o estado de estreia pulsa o único CTA primário da tela. O botão é o `.btn` do `widgets/button.js` — este
   projeto não tem classe `.lobby-btn`; o pulso fica preso à barra para nenhum outro botão do jogo herdá-lo. */
.lobby-bar .btn.is-pulse { animation: rail-pulse 0.8s steps(2, jump-none) infinite; }
/* O painel é colocado por `lobbyNav.js › menuAnchorPos` com o bordo inferior em y 332 — 1 px dentro do selo "60/60" da
   Mochila e do ponto do próprio Menu, que sobem 5 px acima da barra (y 331). Justamente na hora de escolher destino o
   aviso de mochila cheia ficava tapado. A margem negativa sobe o painel inteiro 8 px (bordo em y 324) sem tocar no
   módulo puro, que é de outra trilha (avaliação de UX, rodada 2). */
.lobby-menu {
  position: absolute;
  z-index: 60;
  margin-top: calc(-8 * var(--px));
  display: flex;
  gap: calc(4 * var(--px));
}
.lobby-menu-col { display: flex; flex-direction: column; width: calc(128 * var(--px)); }
.lobby-menu-head { height: calc(12 * var(--px)); line-height: calc(12 * var(--px)); color: var(--c-text-dim); }
/* --- lobby-v2 --- (04 §2.0) O nome da conta, primeira linha da seção `Conta`: saiu da Faixa A (que passou a ser um
   painel só, de 632 px) e veio para junto de `Trocar personagem`, que é o que se faz com ele. Altura 10 — a coluna
   `Social + Conta` tem 186 no modelo e vai a 196, ainda abaixo dos 204 da coluna mais alta, então os 400×212 do
   painel não mudam. Nome de conta é dado escrito por jogador: `textContent`, nunca i18n. */
.lobby-menu-account { height: calc(10 * var(--px)); line-height: calc(10 * var(--px)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lobby-menu-item {
  display: flex;
  align-items: center;
  gap: calc(3 * var(--px));
  height: calc(18 * var(--px));
  background: none;
  border: 0;
  padding: 0;
  text-align: left;
  color: var(--c-text);
  cursor: pointer;
}
/* --- ilha-v2 --- linha do menu trancada pela ilha: apagada + cadeado (G2: forma e cor, nunca só cor) */
.lobby-menu-item.is-locked .lobby-menu-label { color: var(--c-text-dim); }
.lobby-menu-item .is-lock { width: calc(12 * var(--px)); height: calc(12 * var(--px)); margin-left: calc(2 * var(--px)); background-repeat: no-repeat; background-size: 100% 100%; image-rendering: pixelated; }
.lobby-menu-item .is-ico {
  width: calc(16 * var(--px));
  height: calc(16 * var(--px));
  background: center / auto no-repeat;
  image-rendering: pixelated;
  flex: none;
}
/* o rótulo encolhe com reticências: a coluna tem 128 px e o texto do jogador pode crescer no i18n */
.lobby-menu-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* selo: <span> inerte dentro do botão (§8.7), aqui em linha (o do canto é o `.lobby-badge` dos botões da barra) */
.lobby-menu-badge { flex: none; margin-left: auto; color: var(--c-gold); }
/* --- lobby-v2 --- a coluna da tecla tem largura mínima (PERGUNTAS 6): com o rótulo em `flex: 1 1 auto` as margens
   automáticas colapsam, e a linha COM selo e SEM tecla jogava o número no extremo direito — `Presença diária  1`
   lia-se como se `1` fosse a tecla, logo acima de `Configurações [O]`. A reserva só é impressa quando há selo ou
   tecla (ver `widgets/lobbyMenu.js`): aplicá-la a TODA linha tiraria 16 px do rótulo e `Exploração avançada`
   (91,2 px numa caixa de 109 na fonte real) passaria a sair com reticências. */
.lobby-menu-item .is-key { margin-left: auto; min-width: calc(16 * var(--px)); text-align: right; color: var(--c-text-dim); }
/* estados iguais aos do `.ctx-menu-item` (css/windows.css) — o menu é o mesmo gesto, com outra moldura */
.lobby-menu-item:hover, .lobby-menu-item:focus { background: rgba(255, 255, 255, 0.12); outline: none; }
.lobby-menu-item:active { background: rgba(255, 255, 255, 0.2); }
/* --- lobby-v2 --- (02 §2.4) O véu COME o clique de "sair da gaveta": com `pointer-events: none` ele atravessaria e,
   com 8 POIs nas bordas e o painel no meio-direita, quem tenta FECHAR o menu abriria o Armazém. z 59 = abaixo do
   painel (60) e das janelas (60+), que continuam fechando a gaveta pelo `pointerdown` de captura. */
.lobby-menu-backdrop {
  position: absolute;
  inset: 0;
  z-index: 59;
  background: rgba(14, 12, 20, 0.4);
  pointer-events: auto;
}
/* --- /uinav --- */

/* NPCs do lobby (ui-gameplay.md §7; widgets/npcHotspots.js). Botão transparente 48×64 centrado no NPC; placa (arte
   `tooltip`, quase preta com aro claro: lê sobre qualquer fundo) com ícone do papel 12×12 + nome, centrada no NPC por
   uma caixa de largura fixa em flex — posição inteira em px de arte, sem transform; marcador 9×9 quicando 2 px em
   steps(2) a cada 0,8 s enquanto nenhuma janela está aberta. Hover/foco: placa acesa (texto dourado, aro dourado) e
   cantoneiras douradas no hotspot. */
.npc-hotspots {
  position: absolute;
  inset: 0;
}
.npc-hotspot {
  position: absolute;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
}
.npc-hotspot:focus { outline: none; }
/* cantoneiras: 4 cantos de 6 px desenhados com gradientes (1 px de arte de espessura) */
/* --- uinav --- `.is-lit` entra na lista: quando o menu APONTA o personagem (02 §3.4) o foco é posto por código
   depois de um clique de mouse, e aí `:focus-visible` não casa — sem esta classe a placa acendia sozinha e a
   PESSOA, que é o alvo do clique, não recebia marca nenhuma. */
.npc-hotspot:hover, .npc-hotspot:focus-visible, .npc-hotspot.is-lit {
  --c: var(--c-gold);
  --t: var(--px);
  --l: calc(6 * var(--px));
  background:
    linear-gradient(var(--c), var(--c)) top left / var(--l) var(--t) no-repeat,
    linear-gradient(var(--c), var(--c)) top left / var(--t) var(--l) no-repeat,
    linear-gradient(var(--c), var(--c)) top right / var(--l) var(--t) no-repeat,
    linear-gradient(var(--c), var(--c)) top right / var(--t) var(--l) no-repeat,
    linear-gradient(var(--c), var(--c)) bottom left / var(--l) var(--t) no-repeat,
    linear-gradient(var(--c), var(--c)) bottom left / var(--t) var(--l) no-repeat,
    linear-gradient(var(--c), var(--c)) bottom right / var(--l) var(--t) no-repeat,
    linear-gradient(var(--c), var(--c)) bottom right / var(--t) var(--l) no-repeat;
}
.npc-hotspot:active { --c: #fff2c0; }

.npc-plate-box {
  position: absolute;
  display: flex;
  justify-content: center;
  pointer-events: none;
}
.npc-plate {
  display: flex;
  align-items: center;
  gap: calc(3 * var(--px));
  height: calc(16 * var(--px));
  /* 3 px à direita: com 1 px o "ã" de "Mira, a Artesã" encostava no relevo do 9-slice (avaliação de UX, rodada 2) */
  padding: 0 calc(3 * var(--px)) 0 calc(1 * var(--px));
  color: var(--c-text);
  white-space: nowrap;
}
/* o miolo entra 2 px na faixa da moldura (4 px) para a placa caber em 16 px de altura */
.npc-plate > * { margin: calc(-2 * var(--px)) 0; }
.npc-plate.is-lit {
  color: var(--c-gold);
  box-shadow: 0 0 0 var(--px) var(--c-gold);
}
/* --- lobby-v2 --- (D32) TODO ícone de navegação do lobby é 16×16 — linha da gaveta, botão da barra, slot do trilho e
   placa de POI. A placa misturava 9, 10 e 12 px na mesma coluna e o ícone pequeno boiava no vão. As quatro keys de 12
   foram redesenhadas em 16 pelo `01-arte` (`ico_bank16`, `ico_clan16`, `ico_anvil16`, `ico_foreman16`). */
.npc-plate-icon {
  width: calc(16 * var(--px));
  height: calc(16 * var(--px));
  background: center / 100% 100% no-repeat;
  image-rendering: pixelated;
}
.npc-plate-icon.is-storage { background-image: var(--ui-ico_bank16); }
/* a bigorna é aço escuro sobre placa quase preta: ganha um fundo de pedra clara (o ícone do banco já traz moldura) */
.npc-plate-icon.is-crafter { background-image: var(--ui-ico_anvil16); background-color: #8a8478; box-shadow: inset 0 0 0 var(--px) #c8c0b0; }
/* --- clan --- */
.npc-plate-icon.is-clan { background-image: var(--ui-ico_clan16); }
/* --- /clan --- */
/* --- work --- o Capataz nunca teve regra: a placa dele saía só com o nome (a key de 12 px existia e ninguém a citava) */
.npc-plate-icon.is-work { background-image: var(--ui-ico_foreman16); }
/* --- /work ---
   --- lobby-v2 --- (03 §5) Hierarquia por CONTEÚDO na placa: os 4 POIs de uso diário (os NPCs) trazem `is-name` e
   mostram o nome SEMPRE; os 4 ocasionais (prédios e herói) são só o ícone, com o nome no hover/foco e no `aria-label`.
   Esconder os oito nomes obrigaria o jogador novo a passar o ponteiro em cada silhueta, uma por uma; mostrar os oito
   devolve a poluição que a reforma veio tirar. */
.npc-plate-name { display: none; }
.npc-plate .is-name,
.npc-plate.is-lit .npc-plate-name { display: inline; }
/* O POI de prédio/herói é o MESMO botão transparente dos NPCs (mesma camada, mesmas cantoneiras de hover/foco); o que
   muda é a placa, que normalmente traz só o ícone — e então não precisa do respiro de 3 px que existe à direita do
   NOME. Acesa (hover/foco), o nome aparece e o respiro volta. */
.city-poi { cursor: pointer; }
.city-poi-plate { padding: 0 calc(1 * var(--px)); }
.city-poi-plate.is-lit { padding: 0 calc(3 * var(--px)) 0 calc(1 * var(--px)); }
/* --- uinav --- Selo na placa do personagem (docs/specs/ui-gameplay.md): os
   selos de clã e de trabalho saíram dos botões extintos e vieram para quem atende, que está sempre na tela. Mesmo
   molde do `.lobby-badge`, ancorado no canto superior direito da placa — e por isso, e só por isso, a placa vira
   `position: relative`. O `margin: 0` desfaz o `.npc-plate > *` de cima (que entra 2 px na moldura para o miolo
   caber em 16 px): num filho absoluto aquele recuo viraria deslocamento do selo. */
.npc-plate { position: relative; }
.npc-plate > .npc-plate-badge {
  position: absolute;
  top: calc(-4 * var(--px));
  /* O `right` do pacote (−5) foi medido no navegador e reprovado: o bloco absoluto se conta da caixa de PADDING (por
     dentro da moldura de 4 px), então o selo entrava 5 px sobre o nome — "Mestre Aldric" virava "Mestre Aldri▣" e
     "Capataz Bento", "Capataz Bent▣". Com −11 o selo começa no bordo interno direito, cobre só a quina da moldura e
     sobra 1 px de ar depois da última letra. Fica 7 px para fora da placa, e as placas vizinhas (clã e trabalho, as
     duas da fileira de cima) têm 16,6 px de vão: não encostam. */
  right: calc(-11 * var(--px));
  min-width: calc(11 * var(--px));
  height: calc(11 * var(--px));
  margin: 0;
  padding: 0 calc(2 * var(--px));
  line-height: calc(9 * var(--px));
  text-align: center;
  color: var(--c-text-dark);
  background: var(--c-gold);
  border: var(--px) solid #2a2118;
  pointer-events: none;
}
.npc-plate > .npc-plate-badge.hidden { display: none; }
/* --- /uinav --- */

.npc-marker {
  position: absolute;
  width: calc(9 * var(--px));
  height: calc(9 * var(--px));
  background: var(--ui-ico_npc_marker) center / 100% 100% no-repeat;
  image-rendering: pixelated;
  pointer-events: none;
  animation: npc-marker-bounce 0.8s steps(2, jump-none) infinite;
}
.npc-marker.hidden { display: none; }
@keyframes npc-marker-bounce {
  from { margin-top: 0; }
  to { margin-top: calc(-2 * var(--px)); }
}

/* Resumo da run (ui-gameplay.md §6 item 7; screens/lobby.js). O backdrop fica ABAIXO do tooltip de item (z 50) e acima
   das janelas (z 10+) E da barra do lobby (`.lobby-bar`, z 43 — --- warband --- a 33.ª janela, `warband`): o tooltip do loot
   aparece por cima do modal, e a barra fica sob o véu — nenhum botão dela é clicável com o resumo aberto. 44 = a barra + 1:
   enquanto os dois valiam 40 quem punha o véu por cima era só a ordem do DOM (o modal é anexado depois da tela); agora é o
   número, e ele sobe junto com o da barra. Duas colunas: loot (226 = 6 slots de 36 + 5 vãos
   de 2) | números da run. Melhor item: slot 2× — a caixa redefine --px para o dobro (ampliação por fator inteiro na
   mesma grade de pixels, sem transform — §8.7). */
.modal-backdrop.summary-backdrop { z-index: 44; }
/* posição e largura inteiras em px de arte (o centro por grid cairia em meio pixel de arte): (640 − 360) / 2 = 140 */
/* Altura: o resumo cresce com encontros, pactos e "Como foi a sua build" e passava do palco (y 50 → 487 em 360, "OK"
   fora da tela, só saía com Esc). Agora o painel é coluna com teto de palco (top 8 + 8 de respiro embaixo): o CORPO rola
   e o rodapé (Abrir mochila / OK) fica sempre visível. Centrado na vertical pelo backdrop (coluna flex com 8 de
   respiro em cima e embaixo): o resumo curto fica no meio, o alto ocupa o palco sem passar dele. */
/* O respiro NÃO é simétrico: o painel vive entre as DUAS faixas fixas do lobby — a do personagem, que acaba em y 44
   (nome, nível, ouro), e a barra de modos com os seus selos, que começa em y 331 (`.lobby-bar` em y 336 e o selo
   "60/60" da Mochila 5 px acima). Com 8 px de respiro o painel nascia em y 8 e ia a y 352: apagava o nome/nível/ouro
   em cima e a barra de modos inteira embaixo, e com o medidor "Como foi a sua build" aberto cobria as duas faixas
   de ponta a ponta. Agora nasce em y 46 e termina em y 324 — o corpo é que rola (avaliação de UX, rodada 2). */
.modal-backdrop.summary-backdrop { display: flex; flex-direction: column; justify-content: center; align-items: flex-start; padding: calc(46 * var(--px)) 0 calc(36 * var(--px)); }
.summary-backdrop > .modal {
  position: relative;
  left: calc(140 * var(--px));
  width: calc(360 * var(--px));
  max-width: none;
  display: flex;
  flex-direction: column;
  min-height: 0;
  max-height: 100%;
}
.summary-backdrop .window-title, .summary-backdrop .modal-actions { flex: none; }
/* Barra à vista: o medidor "Como foi a sua build" rola aqui (meter.css) — 919 px de conteúdo em 596 de caixa. A regra
   PRECISA da especificidade dos três seletores: `.summary-backdrop .window-body` empatava com `.modal > .window-body`
   (windows.css, `scrollbar-width: thin`) e perdia por ordem de carga, porque windows.css entra DEPOIS de screens.css
   no index.php — o conserto da rodada 1 (B52) não valia nada. O `@supports` desenha a barra em pixel-art, como
   craft.css e clan.css já fazem. */
.modal-backdrop.summary-backdrop > .modal > .window-body { flex: 1 1 auto; min-height: 0; overflow-x: hidden; overflow-y: auto; scrollbar-width: auto; scrollbar-color: #8a84a8 #12101c; padding-right: calc(2 * var(--px)); }
/* …e uma MÁSCARA embaixo, porque neste Chromium a barra estilizada é overlay (clientWidth não muda, nada aparece em
   repouso): com o medidor da build aberto o corpo mede 683 px numa caixa de 464, e sem o degradê a última linha saía
   fatiada ao meio, parecendo defeito de render em vez de "há mais para ver".
   O degradê em si é do MECANISMO ÚNICO: este seletor está nomeado em `css/scroll-fade.css` (carregada por último).
   Não volte a escrevê-lo aqui — foi a cópia à mão em 9 folhas que deixou as três listas mais longas do jogo de fora. */
@supports selector(::-webkit-scrollbar) {
  .modal-backdrop.summary-backdrop > .modal > .window-body { scrollbar-width: auto; scrollbar-color: auto; }
  .modal-backdrop.summary-backdrop > .modal > .window-body::-webkit-scrollbar { width: calc(6 * var(--px)); }
  .modal-backdrop.summary-backdrop > .modal > .window-body::-webkit-scrollbar-track { background: #221f33; border-left: var(--px) solid #0d0a14; }
  .modal-backdrop.summary-backdrop > .modal > .window-body::-webkit-scrollbar-thumb { background: #8a84a8; border: var(--px) solid #0d0a14; }
  .modal-backdrop.summary-backdrop > .modal > .window-body::-webkit-scrollbar-thumb:hover { background: #bcc8d6; }
}
.summary-reason { margin-bottom: calc(4 * var(--px)); color: var(--c-text-dim); }
.summary-reason.is-death { color: var(--c-danger); }
.summary-cols {
  display: flex;
  align-items: flex-start;
  gap: calc(10 * var(--px));
}
.summary-loot {
  display: flex;
  flex-direction: column;
  gap: calc(3 * var(--px));
  width: calc(226 * var(--px));
  flex-shrink: 0;
}
.summary-side {
  display: flex;
  flex-direction: column;
  gap: calc(6 * var(--px));
  flex: 1;
  min-width: calc(96 * var(--px));
}
.summary-stats { grid-template-columns: auto 1fr; margin: 0; }
.summary-qualities { display: flex; flex-direction: column; gap: calc(1 * var(--px)); }

/* faixa na cor da qualidade do melhor item (--best-color vem do JS como var(--q<id>)): três degraus duros, sem dégradé */
.summary-best {
  display: flex;
  align-items: center;
  gap: calc(6 * var(--px));
  margin-bottom: calc(2 * var(--px));
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--best-color, transparent) 34%, transparent) 0 45%,
    color-mix(in srgb, var(--best-color, transparent) 20%, transparent) 45% 72%,
    color-mix(in srgb, var(--best-color, transparent) 9%, transparent) 72% 100%
  );
}
/* Shining: a faixa vira o arco-íris de manifest.shining.bands varrendo em degraus (mesmo ritmo do texto Shining) */
.summary-best { position: relative; }
.summary-best > * { position: relative; }
.summary-best.is-shining { background: none; }
.summary-best.is-shining::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--shining-gradient, none);
  background-size: 200% 100%;
  opacity: 0.22;
  animation: summary-shining 1.333s steps(16) infinite;
  pointer-events: none;
}
/* meia volta de fase em relação ao texto Shining (0 → −100 %): a letra nunca cai sobre a própria cor */
@keyframes summary-shining {
  from { background-position: 50% 0; }
  to { background-position: -50% 0; }
}
.summary-best-box {
  --px2: calc(2 * var(--px));
  width: calc(72 * var(--px));
  height: calc(72 * var(--px));
  flex-shrink: 0;
  animation: summary-best-pop 0.32s steps(4, jump-none) 1;
}
.summary-best-slot { --px: var(--px2); }
/* quantidade de pilha não é ampliada (texto só em --text-1/--text-2): vai na linha do nome */
.summary-best-slot .slot-qty { display: none; }
@keyframes summary-best-pop {
  from { transform: scale(1.25); filter: brightness(2); }
  to { transform: scale(1); filter: none; }
}
.summary-best-text {
  display: flex;
  flex-direction: column;
  gap: calc(2 * var(--px));
  min-width: 0;
}
.summary-best-name {
  font-size: var(--text-2);
  line-height: 1;
  text-shadow: var(--px) var(--px) 0 #000;
  overflow-wrap: anywhere;
}
.summary-best-name.is-shining-text { text-shadow: none; }

.summary-loot-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: calc(4 * var(--px));
}
.summary-more {
  padding: 0 calc(3 * var(--px));
  color: var(--c-text-dark);
  background: var(--c-gold);
  border: var(--px) solid #2a2118;
}
.summary-grid {
  display: flex;
  flex-direction: column;
  gap: calc(2 * var(--px));
}
.summary-grid-row {
  display: flex;
  gap: calc(2 * var(--px));
}
.summary-grid .slot, .summary-best .slot { cursor: help; }
.summary-grid .slot:focus-visible, .summary-best .slot:focus-visible { outline: var(--px) solid var(--c-gold); outline-offset: calc(-1 * var(--px)); }
/* No resumo o véu de "não posso equipar" (35 % na mochila) tingia a grade inteira de vermelho e abafava as molduras de
   qualidade — aqui a informação é o achado; o véu fica só como um tom (o tooltip continua dizendo o requisito em vermelho). */
.summary-grid .slot.is-unusable::after, .summary-best .slot.is-unusable::after { background: rgba(217, 72, 59, 0.14); }
.summary-lost { color: var(--c-danger); }
/* Filtro de loot: o que foi convertido não se perdeu — ouro, nunca perigo (loot-filter). */
.summary-filtered { color: var(--c-gold); }
/* --- vender --- (docs/specs/bulk-sell.md §9.5) a venda à mão na corrida, irmã da linha do filtro e na MESMA língua (UX §19.4):
   `Vendidos: 3 [moeda] +87` / `Filtrados: 4 [moeda] +120` — o rótulo curto, a moeda da série e o número (G1). */
.summary-sold { color: var(--c-gold); }
.summary-filtered, .summary-sold { display: flex; align-items: center; gap: calc(3 * var(--px)); }
.summary-gold-chip { display: inline-flex; align-items: center; gap: calc(2 * var(--px)); white-space: nowrap; }
.summary-empty { padding: calc(6 * var(--px)) 0; }

@media (prefers-reduced-motion: reduce) {
  .npc-marker, .summary-best-box, .summary-best.is-shining::before, .btn > .lobby-badge.is-full,
  /* --- lobby-v2 --- (04) o pulso do slot de estado (D35) e o do CTA de estreia (D37) também param */
  .rail-slot.is-pulse, .lobby-bar .btn.is-pulse { animation: none; }
}

/* Sandbox (sandbox.php): painel lateral de controles fora do palco. */
body.sandbox #stage { right: calc(160 * var(--px)); }
.sandbox-panel {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: calc(160 * var(--px));
  overflow: auto;
  padding: calc(4 * var(--px));
  background: #16161e;
  color: var(--c-text);
  border-left: var(--px) solid #2a2a36;
}
.sandbox-panel label { display: block; margin-bottom: calc(2 * var(--px)); color: var(--c-text-dim); }
.sandbox-panel input, .sandbox-panel select {
  width: 100%;
  margin-bottom: calc(3 * var(--px));
  padding: calc(1 * var(--px)) calc(2 * var(--px));
  background: #0c0c12;
  border: var(--px) solid #2a2a36;
  color: var(--c-text);
}

/* --- run-life --- Exploração avançada (windows/advancedSelect.js; estilos do painel vêm inline, daí os `!important`).
   A lista de pactos tinha teto de 112 px com rolagem sem barra: mostrava 2,5 dos 6 cartões, pactos marcados ficavam
   fora da vista e "Cortina de Ferro" cortava no meio da linha. Agora cada pacto é UMA linha (nome · efeito e paga) sem a
   moldura 9-slice de 8 px por cartão, e o painel, mais largo (440), mostra a lista inteira: ~250 px de altura, centrado
   no palco — cabe entre a faixa do personagem (y 44) e a barra de modos (y 332). */
#advanced-select { max-width: calc(440 * var(--px)) !important; }
/* Camada dos SELETORES DE MODO (Exploração, Avançada; Masmorra e Torre em dungeon.css/tower.css): z 9 — acima das placas
   de NPC da praça (que, montadas depois do seletor, cortavam a borda dele: "Mira, a Artesã" sobre o seletor da
   Exploração — B50) e ABAIXO das janelas (z 10+), do resumo (42) e de `.modal-backdrop` (100): uma janela aberta a
   partir do seletor (Habilidades pelo "Ajustar a barra" da Torre) nasce por cima dele. */
#screen-lobby > .panel:has(#btn-enter-explore),
#advanced-select { z-index: 9; }
/* …e ABAIXO não basta: o seletor continuava VIVO por baixo da janela aberta depois dele (B50 da rodada 2). Sobravam à
   mostra um "Entrar", um "×" e números órfãos ("61", "6", "36", "× 1,15") nas bordas da janela — e o "Entrar" que
   aparecia ainda começava a corrida com um clique. Enquanto houver janela do windowManager aberta os QUATRO seletores
   de modo somem (some a arte e somem os cliques); fechada a janela, o seletor volta como estava. */
#ui-root:has(section.window[role='dialog']) #screen-lobby > .panel:has(#btn-enter-explore),
#ui-root:has(section.window[role='dialog']) #advanced-select,
#ui-root:has(section.window[role='dialog']) #dungeon-select,
#ui-root:has(section.window[role='dialog']) #tower-window { display: none; }
/* --- lobby-v2 --- (03 §6.3) MESMO conserto de B50, agora para a cidade: `npcHotspots.js` escondia só os MARCADORES
   com janela aberta; os botões seguiam clicáveis por baixo dela. O POI 7 (Torre, x 452…548 · y 96…200) fica sob quase
   toda janela do lobby, e um clique — ou um `Tab` + `Enter` às cegas — chamaria `openMode('tower')` → `clearForMode()`
   → FECHA todas as janelas do jogador. O foco é cortado no JS (`tabIndex`), que o CSS não alcança. */
#ui-root:has(section.window[role='dialog']) .npc-hotspots { pointer-events: none; }
/* Teto de 120 com rolagem: a descrição inteira dos seis pactos (abaixo) faz a lista medir 176 px, e o painel passava
   a valer 320 px de altura — nascia em y 24, por cima da faixa do personagem, e terminava em y 344, por cima da barra
   de modos. Com 120 cabem quatro cartões e os DOIS últimos (Garganta Seca e Ossos de Vidro) ficam abaixo da dobra.
   PROVA da rodada 2 (medido: scrollHeight 176 × clientHeight 120, `offsetWidth − clientWidth = 0`): só o degradê do pé
   NÃO avisa — o jogador escolhe entre quatro pactos sem saber que existem seis (Garganta Seca e Ossos de Vidro).
   MEDIDO também que no Chromium HEADLESS a barra é overlay mesmo com `::-webkit-scrollbar` (`.craft-list-scroll`, que
   já tem a regra, também mede gutter 0) — no navegador do jogador, com barra clássica, `overflow-y: scroll` + a regra
   abaixo desenham a barra em repouso. A máscara encurtou para 4 px para não apagar o pé dela. FICA EM ABERTO (relatado
   na prova de UX de 2026-09-22, rodada 2): a garantia de afford­ance em qualquer navegador é uma linha "4 de 6 — role a
   lista" abaixo do quadro, que pede i18n + advancedSelect.js. O painel continua entre y 44 e y 332. */
/* O degradê (4 px aqui, não os 12 padrão) é do MECANISMO ÚNICO: `#advanced-pacts` está nomeado em
   `css/scroll-fade.css`, e é LÁ que estão o `--scroll-fade: 4px` e o `--scroll-fade-pad: 0` — um `#id` escrito nesta
   folha perderia para a declaração do mecanismo, que é carregado depois. */
#advanced-pacts {
  max-height: calc(120 * var(--px)) !important;
  overflow-y: scroll !important;
  overflow-x: hidden !important;
}
@supports selector(::-webkit-scrollbar) {
  #advanced-pacts { scrollbar-width: auto; scrollbar-color: auto; }
  #advanced-pacts::-webkit-scrollbar { width: calc(7 * var(--px)); }
  #advanced-pacts::-webkit-scrollbar-track { background: #221f33; border-left: var(--px) solid #0d0a14; }
  #advanced-pacts::-webkit-scrollbar-thumb {
    background: #8391a8;
    border: var(--px) solid #221f33;
    border-left: calc(2 * var(--px)) solid #221f33;
    box-shadow: inset var(--px) var(--px) 0 #bcc8d6;
  }
  #advanced-pacts::-webkit-scrollbar-thumb:hover { background: #bcc8d6; }
}
#advanced-pacts > .advanced-pact {
  border-image: none !important;
  padding: calc(2 * var(--px)) calc(3 * var(--px)) !important;
  background: rgba(0, 0, 0, 0.28);
  align-items: flex-start;
  cursor: pointer;
}
#advanced-pacts > .advanced-pact.is-selected { background: rgba(224, 176, 64, 0.14); }
/* A descrição do pacto é a MALDIÇÃO que o jogador está aceitando: com `nowrap` + reticências os SEIS pactos saíam
   cortados nas três resoluções ("Cortina de Ferro …") e ele marcava a caixa sem ler o que paga nem o que ganha.
   Agora o nome fica em cima, em linha própria, e o efeito + a paga quebram embaixo em quantas linhas precisarem —
   o painel tem 440 de largura e altura de sobra até y 324 (avaliação de UX, rodada 2). */
#advanced-pacts > .advanced-pact > div { display: flex; flex-direction: column; gap: 0; min-width: 0; white-space: normal; }
#advanced-pacts > .advanced-pact > div > :first-child { flex: none; }
#advanced-pacts > .advanced-pact > div > :last-child { min-width: 0; color: var(--c-text-dim); line-height: calc(10 * var(--px)); }
/* B62/B63: o cartão passou a ter três linhas — nome + "3/6", a MALDIÇÃO e a PAGA (esta derivada do dado, em ouro, para
   os dois lados da aposta ficarem lado a lado). A paga é o último filho e cairia no dim da regra acima. O aviso
   "Role a lista: 6 pactos ao todo." (#advanced-pacts-more, em advancedSelect.js) fecha o item que ficou EM ABERTO no
   comentário do teto acima: fica FORA do quadro que rola, logo abaixo dele, e não depende de barra de rolagem visível. */
/* Ícone do debuff (art/src/ui/icons_pacts.py, `ico_pact_<key>`): ele ADIANTA o nome na mesma linha, no arranjo do
   tooltip (ícone seguido do nome). 16×16 como o `.dg-row-ico` da Masmorra, `flex: none` para não encolher quando o
   nome quebra, e `is-empty` (pacto sem arte no manifesto) apenas não desenha nada — quadrado vazio contornado parece
   imagem quebrada. A linha do nome vira flex só por causa dele; o resto do cartão continua em coluna. */
#advanced-pacts > .advanced-pact > div > .advanced-pact-title { display: flex; align-items: center; gap: calc(2 * var(--px)); }
.advanced-pact-ico {
  width: calc(16 * var(--px));
  height: calc(16 * var(--px));
  flex: none;
  background: center / auto no-repeat;
  image-rendering: pixelated;
}
.advanced-pact-ico.is-empty { background: none; }
#advanced-pacts > .advanced-pact > div > .advanced-pact-reward { color: var(--c-gold, #e8c170); }
#advanced-pacts > .advanced-pact > div > .advanced-pact-index { color: var(--c-text-dim); }
#advanced-pacts-more { margin-top: calc(1 * var(--px)); line-height: calc(10 * var(--px)); }
#advanced-pacts > .advanced-pact > input { margin: calc(1 * var(--px)) 0 0; flex: none; align-self: flex-start; }

/* Cartão de conexão (ui.js › createNetCard; estilo inline): "Tentar de novo" encostava no texto — 8 de respiro. */
.net-card > .panel > :last-child { margin-top: calc(8 * var(--px)) !important; }

/* Criação de personagem (screens/characterCreate.js). A descrição da classe encostava em "Gênero", o erro do nome em
   "Classe", e Voltar/Criar ficavam soltos no rodapé da TELA, longe do formulário. Agora: 8 de respiro depois da
   descrição, 6 depois da linha de erro, e o rodapé entra no fluxo da grade da tela, logo abaixo do painel, com os
   botões alinhados à direita da coluna do formulário (632 − 352 da vitrine − 4 de vão). A âncora do boneco e o interior
   do painel são medidos depois do layout (placePreview), então a prévia e a vitrine seguem o painel. O boneco maior
   (2× de espera) e a luta (1×) são da cena, não do CSS. */
.screen-create { align-content: center; row-gap: calc(6 * var(--px)); }
.screen-create > .screen-footer {
  position: static;
  width: calc(632 * var(--px));
  padding-right: calc(356 * var(--px)); /* --- showcase --- os botões na coluna do formulário: 352 da vitrine + 4 de vão */
  justify-content: flex-end;
}
.screen-create .field-error { margin-bottom: calc(6 * var(--px)); }
/* --- classes-v3 --- (P14) os 8 px de respiro depois da ficha saíram: com 14 classes em abas, eles eram o que faltava para
   a coluna caber nos 360 px (a linha "Armadura: …" também saiu — a aba acesa já diz o peso). */
.screen-create .option-row--stack + .text-dim { margin-bottom: 0; }

/* Selo de contagem (Menu da barra do lobby, "Habilidades" da corrida — .hud-badge em hud.css): com 3 dígitos ("119") o
   selo passava do canto do botão. Dígitos 0,5 px mais juntos e 1 px de respiro lateral: 3 dígitos cabem em 16, e o
   selo nunca passa de 17 (o "9+" para números maiores é do JS). Só nos selos de PONTOS: o da Mochila traz "57/60" de
   propósito, e com o teto de 17 ele virava "57/". */
.btn > .lobby-badge.is-points, .hud-badge { letter-spacing: calc(-0.5 * var(--px)); padding: 0 calc(1 * var(--px)); max-width: calc(17 * var(--px)); overflow: hidden; white-space: nowrap; }

/* ---- criação de personagem: a ficha da classe na gramática de enxugamento (docs/ux-casos.md §22) -------------
   Era a PRIMEIRA tela do jogo e a que mais escrevia: quatro parágrafos com os MESMOS stats que Atributos, Talentos e
   o Catálogo já desenham com a série única de `js/ui/statIcons.js`. O nome do stat virou o ícone da série (G1) e a
   palavra continua no `aria-label`/gesto de cada ficha (G7). Nenhum PNG novo. */
/* GRADE, não fila (a lição da 2.ª rodada do Catálogo, §11): em fila as nove fichas quebravam 8 + 1 e a nona nascia
   órfã numa linha só dela — três faixas de ícone+número empilhadas, e o jogador com uma pergunta a mais. Em
   `auto-fill` elas caem em 5 + 4, com os ícones alinhados em coluna. */
.create-chips {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(calc(40 * var(--px)), 1fr));
  align-items: center;
  gap: 0 calc(4 * var(--px));
}
/* a fila dos atributos leva o selo "principal" junto do número: numa célula fixa ele transbordaria para a vizinha */
.create-chips.is-wide { display: flex; flex-wrap: wrap; gap: 0 calc(6 * var(--px)); }
.create-chip { display: inline-flex; align-items: center; gap: calc(1 * var(--px)); white-space: nowrap; color: var(--c-text); }
.create-ico {
  display: inline-block;
  flex: none;
  width: calc(16 * var(--px));
  height: calc(16 * var(--px));
  background-repeat: no-repeat;
  background-size: 100% 100%;
  image-rendering: pixelated;
}
/* G2 — selo: moldura + palavra curta + cor (o mesmo desenho de `.attr-seal` e de `.codex-seal`). A cor nunca vem
   sozinha (ui-gameplay.md §8.7): a palavra está DENTRO do selo. */
.create-seal {
  flex: none;
  margin-left: calc(2 * var(--px));
  padding: 0 calc(2 * var(--px));
  border: var(--px) solid currentColor;
  line-height: 1.1;
  color: var(--c-gold);
}
/* A PISTA NUM SELO (o mesmo defeito que a §14 achou no botão, com outra roupa): `.has-more` desenha um
   `border-bottom` pontilhado, e aqui ele cai EXATAMENTE em cima da moldura sólida do selo — promessa invisível, que
   é o pior resultado do enxugamento. A moldura do selo é que vira pontilhada embaixo. */
#ui-root .create-seal[data-hint].has-more { border-bottom-style: dotted; }
/* §17: a pista é um rodapé, então ela tem a largura do elemento que a leva — num bloco de largura cheia o pontilhado
   vira um risco de ponta a ponta (a listra do próprio teste de erro da G4). */
.create-twins { width: fit-content; }

/* ---- troca-classe ---- o MODO TROCA da criação (screens/characterCreate.js › classChangeParts; docs/specs/shop.md §12.8). A coluna
   do formulário segue com a largura mínima da criação (a grade e as fichas mandam nela): as linhas novas QUEBRAM, nunca a alargam.
   Altura (o celular deitado, palco 640×360 sem rolar): o rosto 36 (no lugar do Nome) e o bloco "O que vai acontecer" + a linha do
   motivo ocupam o lugar de Gênero, Rosto, Cabelo, Cor e do aviso de permanência. */
/* a coluna da criação mede o que as filas Rosto/Cabelo com o retrato pedem (276, `.char-create`); sem elas o `min-content` encolhia a
   coluna para ~140 e a grade de classes transbordava ("Eletromante" sobre "Cronomante", visto no 844×390 em 2026-10-04): a troca fixa
   a mesma largura, e o rodapé (padding-right 356) continua alinhado com ela */
.screen-create.is-class-change .char-create { grid-template-columns: calc(276 * var(--px)) minmax(0, 1fr); }
/* a troca é mais alta que a criação (o rosto 36 e as três pistas de 24): centrada, ela subia sobre o título. Presa no alto, logo abaixo
   dele (o título vai de 4 a 24). Pior caso: nível 50 com habilidades, atributos, talentos e build (as fichas quebram em 2 filas, 35), a
   ficha da classe com 2 linhas e a dica, o que não serve e o motivo com a porta da Mochila. O painel não tem o respiro de baixo (o
   padding é inline: characterCreate.js › form), 2 de vão até o rodapé e 2 antes do bloco (.class-change-effects); e as abas de peso
   moram na linha do rosto, SOB o nome (.class-change-who-side): a fila delas (16 + 4) sai da coluna e paga o vão do selo `atual`
   (abaixo). Medido no 844×390 e no 1280×720 (2026-10-04, Gigante e Mago com as 4 fichas): rodapé de 334 a 351 no palco de 360;
   com as abas na coluna ele ia a 357 sem o vão do selo, e com o vão sairia do palco */
.screen-create.is-class-change { align-content: start; padding-top: calc(25 * var(--px)); row-gap: calc(2 * var(--px)); }
.class-change-who { display: flex; align-items: stretch; gap: calc(4 * var(--px)); margin-bottom: calc(9 * var(--px)); }
.class-change-who-side { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: space-between; }
.class-change-name { display: flex; align-items: center; gap: calc(4 * var(--px)); }
/* o selo `atual` da classe de hoje: INTEIRO acima do rótulo, no canto ↗ do botão, sentado na borda de cima (o fundo dele cobre só a
   moldura). Dentro do rótulo ele empurraria "Eletromante" para fora dos 85 px da célula; por cima do canto ele cobria o fim do nome
   ("Guerrei[atual]", revisão de 2026-10-04). O vão que ele ocupa: 9 acima da 1.ª fila (a margem da linha do rosto) e 9 entre as
   filas (o row-gap da grade, só na troca: a criação não tem selo) — 1 de ar sobre ele e 1 até o texto do rótulo */
.screen-create.is-class-change .class-grid { row-gap: calc(9 * var(--px)); }
.class-grid .btn { position: relative; }
.class-grid .btn > .class-change-current {
  position: absolute;
  top: calc(-12 * var(--px));
  right: calc(-2 * var(--px));
  margin: 0;
  padding: 0 calc(1 * var(--px));
  line-height: 1;
  background: var(--c-field-bg);
  pointer-events: none;
}
.class-change-effects { display: flex; flex-direction: column; margin-top: calc(2 * var(--px)); }
/* as três linhas com pista são ALVO do dedo (a dica abre no toque): 24 de altura, o mínimo do lint de E2E, sem vão entre elas (o vão
   seria um toque em lugar nenhum) */
.class-change-resets, .class-change-keeps, .class-change-misfit { display: flex; align-items: center; min-height: calc(24 * var(--px)); width: fit-content; max-width: 100%; }
/* a fila `[Do zero] [hab] Habilidades · …`: a pista é DA FILA (uma, nunca uma por ficha) e tem a largura dela (G4) */
.class-change-resets { flex-wrap: wrap; gap: calc(2 * var(--px)) calc(6 * var(--px)); }
.class-change-resets > .class-change-zero { margin-left: 0; color: var(--c-danger); }
.class-change-chip { display: inline-flex; align-items: center; gap: calc(2 * var(--px)); white-space: nowrap; }
.class-change-ico { display: inline-block; flex: none; width: calc(16 * var(--px)); height: calc(16 * var(--px)); background-repeat: no-repeat; background-size: 100% 100%; image-rendering: pixelated; }
.class-change-misfit { gap: calc(2 * var(--px)); }
.class-change-msg { display: flex; align-items: center; flex-wrap: wrap; gap: calc(4 * var(--px)); min-height: calc(12 * var(--px)); margin-top: calc(2 * var(--px)); }
.class-change-msg:empty { display: none; }
/* o rodapé: `[ico] 2.500 → [ico] 1.500` (ou "Faltam N IToken.") à esquerda de Voltar e Trocar, alinhado pelo meio como no VIP */
.class-change-cost { display: flex; align-items: center; gap: calc(3 * var(--px)); margin-right: auto; }
.class-change-price, .class-change-hold-price { display: inline-flex; align-items: center; gap: calc(2 * var(--px)); }
#class-change-confirm-body { display: flex; flex-direction: column; gap: calc(6 * var(--px)); }
#class-change-confirm-body > .btn-hold { align-self: center; }

/* ---- showcase ---- a VITRINE DA CLASSE no painel Prévia (screens/characterCreate.js; docs/specs/showcase.md). O centro
   do painel é o canvas (a luta recortada no interior); por cima dele, no céu do mapa, a fila das ativas e a legenda.
   Fila: ícones de 32 px, 2 linhas de até 8 (8 × 32 + 7 × 4 = 284 < 336 do interior: as 14 ativas e a ficha da ultimate),
   a altura das duas linhas é RESERVADA (a fila que chega com o filme não empurra nada). A da vez acende com a moldura de
   estado ativo. */
.create-preview { display: flex; flex-direction: column; align-items: center; }
.create-showcase-strip {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-content: flex-start;
  gap: calc(4 * var(--px));
  width: calc(284 * var(--px));
  min-height: calc(68 * var(--px));
  margin-top: calc(2 * var(--px));
}
.create-showcase-ico {
  position: relative;
  display: inline-block;
  flex: none;
  width: calc(32 * var(--px));
  height: calc(32 * var(--px));
  padding: 0;
  border: var(--px) solid rgba(0, 0, 0, 0.55);
  background: rgba(14, 12, 24, 0.55);
  color: inherit;
  font: inherit;
  cursor: default;
  image-rendering: pixelated;
}
button.create-showcase-ico { cursor: pointer; }
.create-showcase-art { position: absolute; inset: 0; background: center / 100% 100% no-repeat; image-rendering: pixelated; }
/* --- ultimate --- a ficha da ULTIMATE (a última da fila): a moldura e um fio por dentro na cor da classe (`ult-c-<classe>`, a do
   medidor: css/ult.css), que a separa das ativas sem palavra nenhuma; acesa, a moldura de estado ativo vale por cima */
.create-showcase-ico.is-ult {
  border-color: rgba(var(--ult-rgb, 176, 168, 152), 0.95);
  box-shadow: inset 0 0 0 var(--px) rgba(var(--ult-rgb, 176, 168, 152), 0.45);
}
/* a da vez: a moldura de estado ativo (a mesma cor do botão aceso), nunca só cor — o ícone ganha também o contorno */
.create-showcase-ico.is-current { border-color: var(--c-gold); outline: var(--px) solid var(--c-gold); outline-offset: var(--px); }
#ui-root button.create-showcase-ico:focus-visible { outline: var(--px) dotted var(--c-text); outline-offset: var(--px); }
/* a legenda fica sobre o céu e o cenário do mapa (o moinho da campina passa bem atrás dela): numa placa escura, que só
   existe com texto — vazia (a abertura da volta) não pinta nada */
.create-showcase-caption {
  min-height: calc(12 * var(--px));
  margin-top: calc(4 * var(--px));
  padding: 0 calc(4 * var(--px));
  color: var(--c-gold);
  text-align: center;
  text-shadow: var(--px) var(--px) 0 #000;
  white-space: nowrap;
  background: rgba(14, 12, 24, 0.78);
  border: var(--px) solid rgba(0, 0, 0, 0.6);
}
.create-showcase-caption:empty { background: none; border-color: transparent; }

/* ---- volume ---- o botão de volume da seleção e da criação (widgets/volumeButton.js; docs/specs/ui-gameplay.md §15). No
   canto ↘ da tela, fora do fluxo (a criação é uma grade): nas duas telas o canto é livre — a vitrine da criação acaba em
   y 318 e o rodapé das duas fica à esquerda do x 360. O controle abre À ESQUERDA do botão, na mesma linha: em cima dele
   ficaria por baixo da dica do toque, que nasce acima do botão. Alvos de 24 (botão, silenciar e a barra inteira). */
.volume-ctl {
  position: absolute;
  right: calc(4 * var(--px));
  bottom: calc(4 * var(--px));
  z-index: 3;
  display: flex;
  align-items: center;
  gap: calc(2 * var(--px));
  /* a altura do botão: a moldura do controle (24 + 2 × 4 de borda) transborda 4 em cima e 4 embaixo e o botão fica onde
     estava (com a caixa crescendo para 32 a partir do rodapé, ele subia 4 sob o dedo ao abrir — prova N2 de 2026-10-04) */
  height: calc(24 * var(--px));
}
.volume-ctl .btn.volume-btn,
.volume-ctl .btn.volume-mute {
  width: calc(24 * var(--px));
  height: calc(24 * var(--px));
  min-height: 0;
  padding: 0;
}
.volume-ctl .tooltip-ico { flex: none; }
.volume-ctl .btn.volume-btn.is-active,
.volume-ctl .btn.volume-mute.is-active { border-image-source: url(../assets/ui/buttons/btn_secondary.pressed.png); }
/* o silenciar LIGADO: afundado e com o aro de ouro do estado aceso (o afundado sozinho mal se via a 24 px) */
.volume-ctl .btn.volume-mute.is-active { outline: var(--px) solid var(--c-gold); outline-offset: 0; }
.volume-pop { display: flex; align-items: center; gap: calc(4 * var(--px)); }
/* a caixa da barra tem a altura do dedo; o trilho (6) e o punho (6×10) seguem os de base.css, centrados nela */
#ui-root input[type="range"].volume-range { width: calc(80 * var(--px)); height: calc(24 * var(--px)); margin: 0; }
/* silenciado: a barra guarda o valor e fica apagada (o estado também está no X do botão e no silenciar apertado) */
.volume-ctl.is-muted .volume-range { opacity: 0.55; }
/* O canto ↘ também é onde nasce a pilha de avisos (windows.css, `.toast-stack`, z 110, fundo opaco): na criação ela pousava
   em bottom 4 e na seleção em 28, e um aviso de rede tapava o botão, o silenciar e a barra (o toque passava, o jogador não
   via o que tocava). Nas duas telas ela pousa 2 px acima da moldura aberta — que vai de y 328 a 360 —, em bottom 34, e o
   `--toast-floor` do toast.js é o mesmo 34: com uma janela aberta a coluna nunca desce sobre o controle (o molde do
   replay.css). Os seletores pesam (2,3,0) para vencer a regra da seleção de windows.css (2,2,0), que vem depois na cascata;
   travado em client/tests/ui/volumeButton.test.js (docs/specs/ui-gameplay.md §15). */
#ui-root:has(#screen-select > .volume-ctl) .toast-stack:not(.is-explore),
#ui-root:has(#screen-create > .volume-ctl) .toast-stack:not(.is-explore) { bottom: calc(34 * var(--px)); --toast-floor: 34; }
