/* windows.css — janelas arrastáveis (js/ui/windows/windowManager.js; ui-gameplay.md §0.1), widgets (tooltip, menu,
   qtyPrompt, toast, modal), grade de slots (§0.3), mochila (§1) e banqueiro (§3). Dona após a K2.0: K2a (§12).
   Medidas em px de arte: calc(N * var(--px)); posição de janela sempre left/top inteiros, nunca transform. */

/* Janela: tamanho externo fixo (tabela do §0.1, aplicado pelo JS); z = ordem em ui.windows (inline). A borda do
   `.panel` (8 px) fica fora do miolo; título e corpo entram 4 px na faixa da borda para a moldura não ficar gorda. */
.window {
  position: absolute;
  display: flex;
  flex-direction: column;
  min-width: calc(120 * var(--px));
  max-width: calc(600 * var(--px));
  max-height: calc(340 * var(--px));
  /* `visible`: título, corpo e rodapé entram de propósito na faixa da borda (margens negativas abaixo) e `hidden` os
     cortaria na caixa de padding — quem rola/corta o próprio conteúdo é o `.window-body`. */
  overflow: visible;
}
.window:focus { outline: none; }
.window.is-focused > .window-title { color: var(--c-gold); }
.window:not(.is-focused) > .window-title { color: var(--c-text-dim); }

.window-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: calc(4 * var(--px));
  /* −2 nas laterais (como .hud-panel-body): com −4 a primeira letra fica sob a moldura do painel */
  /* o botão fechar tem 16 de altura (texto 8 + borda 4 + 4): com −4 em cima e 2 embaixo o título ocupa 14 do miolo e o
     banco (rótulo 10 + 2 + grade 188 + 2 + abas 12 = 214) cabe nos 258 da janela */
  margin: calc(-4 * var(--px)) calc(-2 * var(--px)) calc(2 * var(--px));
  padding: 0 calc(2 * var(--px));
  color: var(--c-gold);
  cursor: move;
  user-select: none;
  touch-action: none;
}
.window-title-left {
  display: flex;
  align-items: baseline;
  gap: calc(4 * var(--px));
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
}
/* C7/S14/E11 (análise crítica de 2026-09-22): o subtítulo ERA a fala do NPC truncada na faixa do título
   ("Banqueiro “Seus tesouros ficam seguros comigo. O cofre é da con…"), repetindo o que o balão já dizia inteiro.
   Agora ele sai da tela e fica só para o leitor de tela (o texto completo continua no DOM, ao lado do título): a faixa
   do título mostra só o nome da janela. Fora de fluxo, 1 px de arte e recortado — não mexe na linha de base do título. */
.window-subtitle {
  position: absolute;
  width: var(--px);
  height: var(--px);
  margin: calc(-1 * var(--px)) 0 0;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.window-title-actions { display: flex; gap: calc(2 * var(--px)); flex-shrink: 0; }

/* O × é o ÚNICO botão de fechar da barra de título (B70, análise crítica de 2026-09-22): quem só alternava o MODO da
   janela era o ⇔ da Mochila, e ele não existe mais — a Mochila nasce maximizada nas duas telas. */
.window-close {
  min-height: calc(12 * var(--px));
  padding: 0 calc(2 * var(--px));
}

.window-body {
  flex: 1;
  min-height: 0;
  overflow: auto;
  margin: 0 calc(-2 * var(--px));
}

/* Sem margem horizontal negativa: o ornamento do canto inferior do painel cobriria o começo do texto. */
.window-footer {
  display: flex;
  align-items: center;
  gap: calc(6 * var(--px));
  margin: calc(2 * var(--px)) 0 calc(-4 * var(--px));
}
.window-footer > .btn:last-child { margin-left: auto; }
/* B187 — o vão explícito do rodapé (`ui/dom.js › footerSpacer`): tudo que vem depois dele vai para a direita. A regra
   acima só alcança o rodapé que TERMINA em botão; com o vão, o rodapé que termina em texto (trabalhadores, diário)
   segue o mesmo padrão sem precisar de uma regra por janela — e sem `style` inline, que é o que fez o `daily` ficar
   centrado e fora do alcance de qualquer conserto de folha. */
.window-footer .footer-spacer { flex: 1 1 auto; min-width: 0; }
.inventory-count.is-nearly-full, .storage-label.is-nearly-full { color: var(--c-gold); }
.inventory-count.is-full, .storage-label.is-full { color: var(--c-danger); animation: blink-2 0.8s steps(2) infinite; }

/* Mochila (420 no lobby, 360 na corrida) com o filtro de loot ligado: o selo "vender ≤ Excelente" entrava no rodapé e empurrava
   "Organizar" para fora da janela (x 761–880 × borda 848). Os botões nunca encolhem; quem cede espaço são os textos
   (ouro, contador, selo), com reticências.
   (C16, 2026-09-23: a 2.ª fileira PASSOU a caber — as abas de página já não moram no corpo, elas subiram para a barra
   de título; ver `.inventory-footer-row` abaixo. As reticências continuam como rede de segurança.)
   (2026-09-26: o rodapé é UM só — o modo compacto morreu junto com o ⇔, e com ele todas as regras `.is-compact`.) */
/* vão de 4 (o padrão do rodapé é 6): com o contador inteiro, "Venda rápida", "Filtro de loot", o mascote e
   "Organizar" a fileira passava 4 px da moldura e o "Organizar" saía cortado. Os 5 vãos devolvem 10. */
.inventory-footer { min-width: 0; gap: calc(4 * var(--px)); flex-wrap: wrap; row-gap: 0; }
.inventory-footer > .btn { flex-shrink: 0; }
/* C16: a fileira própria de Mascote + Filtro de loot (`inventory.js › footerRow`), nas duas telas. `flex-basis: 100 %`
   força a quebra ANTES dela — e é a quebra que devolve os ~180 px que faziam o contador "60/60" virar "60/…" ou "0".
   Orçamento vertical: miolo 242 − título 14 − (2 + 16 + 16 − 4) = 198, e a grade 6×5 ocupa 188 — a mesma conta vale na
   corrida, onde a janela também tem 258 de altura e a fileira própria leva só o Filtro. */
.inventory-footer-row {
  display: flex;
  align-items: center;
  gap: calc(4 * var(--px));
  flex: 1 1 100%;
  min-width: 0;
}
.inventory-footer-row > .btn { flex: 0 1 auto; min-width: 0; min-height: calc(16 * var(--px)); }
/* --- char-links --- Atalhos do PERSONAGEM (`windows/inventory.js`, ui-gameplay.md §1): Atributos, Habilidades,
   Talentos, Builds salvas e Profissões, à direita da 2.ª fileira do rodapé. SÓ ÍCONE (pedido do dono: menos palavra,
   mais ícone) — o nome vive no `title`/`data-hint`/`aria-label`.
   Caixa 24×16: 4 de moldura + o ícone de 16 (a margem −2 de `.btn-icon` é o que mantém o botão nos 16 de altura dos
   vizinhos) + 4. Cinco deles medem 5 × 24 + 4 × 2 = 128 — cabem nos 404 do rodapé do lobby ao lado de "Mascote" e
   "Filtro de loot" (~130) e nos 344 da corrida, onde a fileira leva só o Filtro. `padding: 0` e `gap: 0` porque o
   rótulo é vazio: com o vão de 2 do `.btn` os cinco cresciam 10 px de nada.
   ORÇAMENTO VERTICAL (medido no navegador em 2026-09-26, nas duas telas): o `.btn` com ícone de 16 mede 20 de altura
   (a margem −2 do `.btn-icon` desconta 4 dos 24 crus), então a 2.ª fileira passa de 16 para 20 e o rodapé de 30 para
   34 (2 + 16 + 20 − 4). O corpo cai de 198 para 194 e a grade 6×5 (188) continua INTEIRA, com 6 px de folga — as 30
   células da página estão lá. Crescer mais esta fileira come a grade: 194 é o piso. */
.inventory-links { display: flex; align-items: center; gap: calc(2 * var(--px)); margin-left: auto; flex: none; }
.inventory-links > .btn { position: relative; flex: none; padding: 0; gap: 0; }
/* botão SÓ de ícone travado: `.btn.is-disabled` só escurece a moldura e o `color` do RÓTULO — e aqui não há rótulo.
   Sem isto, Talentos travado na corrida se distinguia do destravado apenas pela moldura (medido no navegador). */
.inventory-links > .btn.is-disabled .btn-icon { opacity: 0.5; }
/* Selo de ponto a gastar POR DENTRO do botão (molde do `.bank-tab-n` e do `.rail-slot > .is-count`, screens.css): a
   fileira própria do rodapé CORTA o que sai dela (`.inventory-footer > :not(.btn)` tem `overflow: hidden` para dar
   reticências ao ouro e ao contador), e um selo no molde do `.lobby-badge` — que sai 9 px para cima — apareceria
   pela metade. Canto inferior direito: é onde o ícone de 16 tem menos desenho. */
.inventory-links .inv-link-badge {
  position: absolute;
  right: 0;
  bottom: 0;
  min-width: calc(6 * var(--px));
  height: calc(9 * var(--px));
  padding: 0;
  font-size: var(--text-1);
  line-height: calc(9 * var(--px));
  text-align: center;
  color: var(--c-text-dark);
  background: var(--c-gold);
  pointer-events: none;
}
/* --- /char-links --- */
/* "Organizar" continua colado na borda direita: com a fileira nova ele deixou de ser `:last-child` (a regra genérica
   `.window-footer > .btn:last-child` não o alcança mais), mas continua sendo o último BOTÃO da primeira fileira. */
.inventory-footer > .btn:last-of-type { margin-left: auto; }
.inventory-footer > :not(.btn) { flex-shrink: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.inventory-footer > .gold-label { flex-shrink: 0; }
/* O contador NUNCA encolhe. A regra acima põe reticências em todo texto do rodapé, e o contador é o mais curto:
   com a mochila cheia ele virava "60/…", e com o botão do Mascote ("Mascote · 20") no rodapé sumia por completo —
   justo o número que diz que não cabe mais nada. Quem cede espaço é a dica/selo, que já tem `flex: 1 1 0`. 44 px
   comportam "999/999" (avaliação de UX, rodada 2). */
.inventory-footer > .inventory-count { flex: 0 0 auto; overflow: visible; text-overflow: clip; white-space: nowrap; }
/* --- F7 tela-guardado --- O TEMPO GUARDADO (`widgets/storedBuffs.js`, ui-gameplay.md §1): UM chip na 1.ª fileira, logo à direita
   do ouro, só no lobby. ALTURA 16 = a da fileira (a dos botões): o rodapé segue com 34 e o corpo no piso 194.
   LARGURA: `flex-basis: 0` — o chip nunca empurra a fileira para a quebra (o `flex-wrap` do rodapé mandaria o "Organizar" para uma
   3.ª fileira, que come a grade) — e cresce só até o próprio conteúdo (`max-width: max-content`: a pista não vira listra). Sem folga,
   QUEM CEDE É ELE (o `overflow: hidden` da regra de cima corta o tempo e depois o ícone), nunca o contador (`flex: 0 0 auto`). Na
   coluna da v2 (`is-side`) o eixo do `flex` é a altura: o par min/max de 16 o segura numa fileira. */
.inventory-footer > .stored-buffs {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: calc(2 * var(--px));
  flex: 1 1 0;
  max-width: max-content;
  /* o vão do rodapé (4) colava o tempo ao contador: "4:59 15/60" se lia como um número só (olhado no celular, 2026-10-05) */
  margin-right: calc(4 * var(--px));
  height: calc(16 * var(--px));
  min-height: calc(16 * var(--px));
  max-height: calc(16 * var(--px));
}
/* o ícone de 32 em 16 (redução inteira 2:1, a mesma do chip de efeito do HUD) e o selo `N` POR DENTRO dele, no canto de baixo à
   direita (molde do `.inv-link-badge`: o chip corta o que sai dele) */
.stored-buffs-box { position: relative; flex: none; width: calc(16 * var(--px)); height: calc(16 * var(--px)); }
.stored-buffs-icon { position: absolute; inset: 0; background-repeat: no-repeat; background-size: 100% 100%; image-rendering: pixelated; }
.stored-buffs-time { flex: none; font-size: var(--text-1); line-height: calc(9 * var(--px)); color: var(--c-text); }
.stored-buffs-n {
  position: absolute;
  right: 0;
  bottom: 0;
  min-width: calc(6 * var(--px));
  height: calc(9 * var(--px));
  padding: 0;
  font-size: var(--text-1);
  line-height: calc(9 * var(--px));
  text-align: center;
  color: var(--c-text-dark);
  background: var(--c-gold);
  pointer-events: none;
}
/* a PISTA (G4) mora sob o TEMPO, não sob o chip: o pontilhado de base.css no chip de 16 comeria a última fileira do ícone */
#ui-root .stored-buffs[data-hint].has-more { border-bottom: 0; }
#ui-root .stored-buffs[data-hint].has-more > .stored-buffs-time { border-bottom: var(--px) dotted rgba(244, 240, 230, 0.4); }

/* Ouro com ícone (ui/icons/ico_gold.png, 9×9 fixo). */
.gold-label { display: inline-flex; align-items: center; gap: calc(2 * var(--px)); white-space: nowrap; }
.ico {
  display: inline-block;
  flex: none;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  image-rendering: pixelated;
}
.ico-gold { width: calc(9 * var(--px)); height: calc(9 * var(--px)); background-image: var(--ui-ico_gold, none); }
.itoken-label { display: inline-flex; align-items: center; gap: calc(2 * var(--px)); white-space: nowrap; }
.ico-itoken { width: calc(9 * var(--px)); height: calc(9 * var(--px)); background-image: var(--ui-ico_itoken, none); }
/* --- focus --- (docs/specs/focus.md §8) O Foco com ícone (ui/icons/ico_focus.png, 9×9: a ampulheta na armação), o arranjo do ouro
   (`ui/focus.js › focusLabel`). Acima do tanque o cadeado da série (`ico_lock` 8×8) toma o lugar do número. */
.focus-label { display: inline-flex; align-items: center; gap: calc(2 * var(--px)); white-space: nowrap; }
.ico-focus { width: calc(9 * var(--px)); height: calc(9 * var(--px)); background-image: var(--ui-ico_focus, none); }
.focus-lock { display: inline-block; flex: none; width: calc(8 * var(--px)); height: calc(8 * var(--px)); background: var(--ui-ico_lock, none) center / 100% 100% no-repeat; image-rendering: pixelated; }
/* O botão "Concluir ◈ N" (`widgets/focusButton.js`): o verbo e a ficha do preço; a ficha passa 1 px da linha do botão de 16 sem crescê-lo
   (a margem negativa do `Criar ◉ 66`, craft.css). A cara COMPACTA (`◈ N`, o cartão de vaga) aperta o padding e centra a ficha: no pior
   caso pagável (`◈ 5.000`) ela mede 34 nos 35 de miolo do botão de 45 (tela da frente, T3). */
.btn > .focus-btn-face > .focus-label > .ico-focus, .btn > .focus-label > .ico-focus { margin: calc(-1 * var(--px)) 0; }
.focus-btn-face { display: inline-flex; align-items: center; }
.focus-btn.is-compact { padding-left: calc(1 * var(--px)); padding-right: calc(1 * var(--px)); }
.focus-btn.is-compact .focus-label { gap: calc(1 * var(--px)); }
/* a carta do mar: o saldo do Foco depois do ouro, com o ponto médio decorativo entre as famílias (G1) */
.node-map-sep, .node-map-focus { flex: none; }
.node-map-sep { color: var(--c-text-dim); }
@keyframes blink-2 {
  from { opacity: 1; }
  to   { opacity: 0.35; }
}

/* Grade 6×5 (§0.3): 30 células de 36 com gap 2 = 226×188; abas de página embaixo. */
.item-grid-wrap {
  display: flex;
  flex-direction: column;
  gap: calc(2 * var(--px));
}
.item-grid {
  display: grid;
  grid-template-columns: repeat(var(--cols, 6), calc(36 * var(--px)));
  gap: calc(2 * var(--px));
}
.grid-tabs { display: flex; gap: calc(2 * var(--px)); }
/* uma página só: a faixa das abas continua ocupando a altura (mesma geometria em toda janela) */
.grid-tabs.is-single { visibility: hidden; }
/* B186 — o SELETOR DE PÁGINA da grade ("1 2") mora na barra que titula a grade, nunca embaixo dela. São dois hosts,
   e a regra é a mesma nos dois: `.window-title-tabs` (barra de título da janela — Mochila) e `.storage-label` (o
   rótulo de cada coluna do Banqueiro, que é a barra de título daquela grade). Como o host é uma LINHA DE TEXTO, o
   pager entra colado à direita dela e não empurra o contador.
   `.is-single` continua invisível, mas aqui NÃO pode reservar altura: numa linha de texto ele reservaria 12 px de
   nada em toda janela de uma página só. */
.window-title-tabs, .storage-label { display: flex; align-items: center; gap: calc(3 * var(--px)); }
/* colado ao texto do rótulo, e não empurrado para a direita da coluna: a coluna da mochila é mais larga que a grade
   dela, e com `margin-left: auto` o "2" saía pela borda da janela (medido em 1280). */
.storage-label > .grid-pager { margin-left: calc(2 * var(--px)); }
.storage-label > .grid-pager.is-single { display: none; }
.window-title-tabs > .grid-pager.is-single { display: none; }
/* numa linha de texto de 9 px a aba de 12 estouraria a faixa do rótulo: no host de texto ela é de 9 */
.storage-label > .grid-pager > .grid-tab { height: calc(9 * var(--px)); min-width: calc(12 * var(--px)); padding: 0; }
/* --- bag-tabs --- "+" no FIM da fileira de páginas: comprar uma aba é comprar uma página. Mesma altura da aba de
   página (12) para não engordar a barra de título, e largura de 12 porque o rótulo é um sinal só. */
.window-title-tabs > .inv-buy-tab { height: calc(12 * var(--px)); min-width: calc(12 * var(--px)); min-height: 0; padding: 0; justify-content: center; }
/* aba de página baixa (12): o texto de 8 invade 2 px da borda de 4 em cima e embaixo — a arte da aba tem miolo liso */
.grid-tab { min-width: calc(16 * var(--px)); min-height: 0; height: calc(12 * var(--px)); justify-content: center; }

/* Mochila (§1): equipPanel | grade, sem a margem negativa do `.window-body` (a coluna de slots começaria 2 px dentro
   da moldura). LOBBY: miolo de 404 (420 − 2 × 8) = 172 + 6 + 226. CORRIDA: miolo de 352 (368 − 2 × 8) = 112 do trilho
   (`.equip-layout.is-rail`) + 6 + 8 de respiro + 226 — as mesmas 30 células da página, no corredor livre do HUD. */
.inventory-body {
  display: flex;
  align-items: flex-start;
  gap: calc(6 * var(--px));
  margin: 0;
  overflow: hidden;
}
/* --- lobby-ilha --- (04 §5.6c, D-E2) A CAIXA CRESCEU de 74 para 98 px de altura (o boneco, menos o `INSET = 8` de
   `widgets/equipPanel.js`, passa de 80×58 para 80×82). Motivo: com a praça fora de cena, esta é a ÚNICA janela do
   jogo, fora da corrida, em que o jogador se vê de corpo inteiro e vestido — e é o que dá sentido a equipar.
   A MEDIDA, na coluna do centro (96 px de largura, 188 de altura = 5 × 36 + 4 × 2, que são as 5 fileiras de vagas das
   colunas 1 e 3): a ficha curta gasta 50 (3 fileiras de 16 + 2 vãos de 1), os anéis gastam a 5.ª fileira (36) e
   sobram dois vãos de 2 → 188 − 50 − 36 − 4 = **98**, o maior valor que não empurra a ficha para fora da janela.
   `WINDOW_SPECS.inventory` NÃO muda (D5): a Mochila continua 420×258 no lobby e 368×258 na corrida, e o trilho
   (`.equip-layout.is-rail`, sem prévia) não é tocado. A prévia passa a ocupar as fileiras 1–3 e a ficha desce para o
   PÉ das fileiras 3–4 (`align-self: end`): as duas dividem a fileira 3 sem se encostar, com o vão de 2 de sempre. */
.equip-preview {
  width: calc(96 * var(--px));
  height: calc(98 * var(--px));
  grid-column: 2;
  grid-row: 1 / span 3;
  align-self: start;
  display: grid;
  place-items: center;
}
/* Ficha curta (UX-GRAMATICA §G1): cinco fichas `[ícone] valor` no lugar de cinco linhas `rótulo escrito … valor`.
   DUAS COLUNAS fixas, não um flex que quebra: com quebra livre as cinco fichas paravam em x diferentes e a ficha
   virava ziguezague — o defeito que a §18 mediu na coluna de preço do Leilão ("lista só se lê em coluna quando a
   coluna existe"). Em 2 × 46 px o número fica encostado à direita da sua coluna e o ícone à esquerda: dois pares
   alinhados e o quinto sozinho. Três fileiras de 16 px (50 px) cabem nos 74 px das duas fileiras da grade, então a
   fileira dos anéis continua onde estava. Ícone de 16 é o piso da altura — por isso 5 linhas de 16 (80 px) não cabem. */
.equip-sheet {
  /* --- lobby-ilha --- (04 §5.6c) fileiras 3–4, colada ao PÉ delas: a prévia cresceu para dentro da fileira 3 e a
     ficha desce os 24 px que a caixa tomou. As três fileiras de 16 continuam onde estavam em relação aos anéis. */
  grid-column: 2;
  grid-row: 3 / span 2;
  align-self: end;
  width: calc(96 * var(--px));
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-content: start;
  gap: calc(1 * var(--px)) calc(4 * var(--px));
}
.equip-stat { display: flex; align-items: center; gap: calc(2 * var(--px)); white-space: nowrap; min-width: 0; height: calc(16 * var(--px)); }
.equip-stat-value { margin-left: auto; overflow: hidden; }
/* Ícone da série de 16×16 (`ui/statIcons.js`), inerte: nunca informa sozinho — a palavra está no `aria-label`. */
.equip-stat-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;
}
/* Potencial: 9×9, a MESMA peça do HUD do lobby (ele não é stat de `config/stats.json` e não tem peça de 16). */
.equip-stat-ico.is-small { width: calc(9 * var(--px)); height: calc(9 * var(--px)); margin: 0 calc(3.5 * var(--px)); }
/* --- lobby-ilha --- (04 §5.6c) a 5.ª fileira do centro, explícita: com a prévia e a ficha posicionadas por linha,
   os anéis dizem onde ficam em vez de depender da sobra do preenchimento automático. */
.equip-rings { display: flex; gap: calc(2 * var(--px)); grid-column: 2; grid-row: 5; }
.equip-preview-note { text-align: center; }
/* --- skins --- (docs/specs/skins.md §10) O botão-selo `Skin` na 6.ª casa da ficha (fileira 3, coluna 2, a que sobrava): selo
   de OURO (G2: moldura + palavra + cor; o ouro do `Vestindo` da Loja e o da porta) encostado à direita da coluna, como os números
   acima dele. É `<button>` (Tab, Enter, toque) sem a moldura 9-slice: a moldura é a do selo. A pista (o gesto com o nome da skin)
   pontilha a borda de baixo do PRÓPRIO selo — a regra geral (base.css) poria um 2.º risco cinza sob a moldura (o caso
   `.create-seal`, UX-GRAMATICA G4). O mesmo selo, sem porta, na ficha de Inspecionar e do Ranking (`.inspect-skin-seal`). */
.equip-skin-seal,
.inspect-skin-seal {
  display: inline-flex;
  align-items: center;
  height: calc(12 * var(--px));
  padding: 0 calc(3 * var(--px));
  border: var(--px) solid currentColor;
  background: var(--c-field-bg);
  color: var(--c-gold);
  font: inherit;
  line-height: 1;
  white-space: nowrap;
}
.equip-skin-seal { position: relative; justify-self: end; align-self: center; cursor: pointer; }
/* O selo é uma PORTA: o desenho fica em 12, o ALVO vai a 24 (o mínimo do lint W9.1; o molde `inset` negativo de rank.css). O `::after`
   cresce 9 para cima — sobre o número da Vida, que não é controle — e só 3 para baixo: a fileira dos anéis (vagas) está a 4 do selo
   (2 da sobra da fileira de 16 + o vão de 2 da grade) e não perde nenhum pixel do toque. A v2 desliga isto: lá o próprio botão tem 46. */
.equip-skin-seal::after { content: ''; position: absolute; inset: calc(-10 * var(--px)) calc(-1 * var(--px)) calc(-4 * var(--px)); }
.equip-skin-seal:hover, .equip-skin-seal:focus-visible { background: rgba(224, 176, 64, 0.18); }
.equip-skin-seal:active > span { transform: translateY(var(--px)); }
#ui-root .equip-skin-seal[data-hint].has-more,
#ui-root .inspect-skin-seal[data-hint].has-more { border-bottom: var(--px) dotted currentColor; }
#ui-root .equip-skin-seal[data-hint].has-more { cursor: pointer; }
/* Na ficha: a fileira dos anéis (36) tem 37 livres de cada lado (148 − 74); o selo mora no da DIREITA, rente à borda do palco e no
   meio da altura da fileira — colado ao boneco que ele explica, sem empurrar os anéis. Fora do furo do palco (furo é máscara: DOM
   dentro dele sumiria). */
.inspect-card .inspect-center { position: relative; }
.inspect-skin-seal { position: absolute; right: calc(2 * var(--px)); bottom: calc(12 * var(--px)); font-size: calc(7 * var(--px)); cursor: help; }

/* Banqueiro (§3): armazém (loc 2) ⇄ mochila (loc 0) — 226 + 2 + 8 + 2 + 226 = 464 = miolo (480 − 2 × 8). */
.storage-body {
  display: flex;
  align-items: flex-start;
  gap: 0;
  margin: 0;
  overflow: hidden;
}
/* Vãos de 1 e rótulo de 9 (eram 2 e 10): a janela encurtou de 284 para 276 para não cobrir o selo "60/60" da barra do
   lobby, e o corpo passava 8 px do miolo — a última fileira da grade e as abas de página saíam fatiadas ao meio.
   Os 8 px vieram daqui e da margem negativa da fileira do topo, sem encolher célula, ícone nem botão (rodada 2); a margem
   saiu em 2026-10-01 (`.storage-topbar`), paga pela folga que o seletor de página deixou ao subir para o rótulo (B186). */
.storage-col { display: flex; flex-direction: column; gap: var(--px); }
.storage-label { color: var(--c-text-dim); line-height: calc(9 * var(--px)); }
/* carregando / erro do armazém: véu sobre a grade (as células ficam em slot_pending — nine-slice.css) */
.storage-grid-host { position: relative; }
.storage-status {
  position: absolute;
  left: calc(24 * var(--px));
  right: calc(24 * var(--px));
  top: calc(72 * var(--px));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(4 * var(--px));
  padding: calc(2 * var(--px));
  text-align: center;
}
.storage-arrow { align-self: center; width: calc(8 * var(--px)); text-align: center; }
.storage-note { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.window-footer > .storage-note { flex: 1; min-width: 0; }

/* --- banqueiro-v2 --- fileira do topo (modo + abas), abas com ícone, painel de materiais. 480 × 284 (docs/specs/ui-gameplay.md §3). */
/* Sem margem negativa embaixo (era −4): com ela os botões de 24 desciam 4 px sobre a linha dos rótulos, e a partir de 4 abas
   a fileira, ancorada à direita, cobria o "1 2" da Mochila; com 9, o próprio "Mochila 15/60" (medido em 2026-10-01). Os 4 px
   existem desde que o seletor de página subiu para o rótulo (B186): o corpo de 232 tinha 13 de folga sob as grades e fica com 9. */
.storage-topbar { display: flex; align-items: center; gap: calc(2 * var(--px)); height: calc(24 * var(--px)); }
.storage-topbar .bank-tabs { margin-left: auto; }
/* 24 e não 20: o .tab tem aro de 4 px por lado e box-sizing border-box — num botão de 20 o miolo seria 12 e cortaria o ícone de 16. */
.bank-tab, #storage-buy-tab, #storage-tab-icon, .bankmat-cat.is-icon { position: relative; width: calc(24 * var(--px)); height: calc(24 * var(--px)); min-height: 0; padding: 0; justify-content: center; }
.bank-tab-ico, .bankmat-cat-ico { width: calc(16 * var(--px)); height: calc(16 * var(--px)); background-size: 100% 100%; image-rendering: pixelated; }
.bank-tab-n { position: absolute; right: calc(-2 * var(--px)); bottom: calc(-3 * var(--px)); font-size: var(--text-1); line-height: 1; pointer-events: none; }
.bankmat { display: flex; flex-direction: column; gap: calc(2 * var(--px)); width: calc(226 * var(--px)); height: calc(188 * var(--px)); }
.bankmat-cats { display: flex; flex-wrap: nowrap; gap: calc(2 * var(--px)); }
.bankmat-row.is-shared .bankmat-name { color: var(--c-text-dim); }
#bankmat-list { flex: 1; min-height: 0; overflow-y: auto; }
.bankmat-row { display: flex; align-items: center; gap: calc(4 * var(--px)); padding: 0 calc(2 * var(--px)); cursor: pointer; }
.bankmat-row:hover, .bankmat-row:focus-visible { color: var(--c-text); background: rgba(255, 255, 255, 0.06); }
.bankmat-row .bankmat-qty { margin-left: auto; }
/* o selo de tier no ícone 16×16 do material: a MESMA peça do slot de item (nine-slice.css .slot-tier; bank.md §2), ancorada
   no canto superior esquerdo do ícone — o contêiner é quem dá o `position` que o selo absoluto procura.
   Fundo de mini-slot (o mesmo de `.tooltip-use`, "Usado em"): sem a casinha, a arte de 32 reduzida tem margem transparente e
   o selo parecia flutuar ACIMA da figura, como sobrescrito; com ela o selo volta a ler como o canto do slot da Mochila. */
.bankmat-ico { position: relative; flex: none; width: calc(16 * var(--px)); height: calc(16 * var(--px)); background-color: rgba(0, 0, 0, 0.35); box-shadow: inset 0 0 0 var(--px) rgba(255, 255, 255, 0.08); }
.bankmat-ico > .work-item-icon { display: block; }
.bankmat-ico > .slot-tier { left: 0; top: 0; }
.bank-icon-pick { display: inline-flex; align-items: center; gap: calc(4 * var(--px)); }
/* --- /banqueiro-v2 --- */

.window-tabs {
  display: flex;
  gap: calc(2 * var(--px));
  margin-bottom: calc(4 * var(--px));
}

/* Grade de slots: 10 colunas por padrão (inventário 60 = 10×6). */
.slot-grid {
  display: grid;
  grid-template-columns: repeat(var(--cols, 10), calc(36 * var(--px)));
  gap: calc(2 * var(--px));
}

/* Paperdoll da janela de personagem: posições de equipar ao redor da prévia. */
.equip-layout {
  display: grid;
  grid-template-columns: calc(36 * var(--px)) calc(96 * var(--px)) calc(36 * var(--px));
  grid-auto-rows: calc(36 * var(--px));
  gap: calc(2 * var(--px));
  align-items: center;
  justify-items: center;
}
/* TRILHO da corrida (widgets/equipPanel.js › `rail`): as MESMAS 11 vagas em 3 colunas × 4 linhas, sem prévia e sem
   ficha. 3 × 36 + 2 × 2 = 112 de largura, 4 × 36 + 3 × 2 = 150 de altura — é o que sobra do corredor de 378 px do HUD
   da corrida depois da grade de 226, do vão de 6 e dos 16 da moldura (windowLayout.js › `inventory.exploreSize`). */
/* CORRIDA — o trilho precisa de um RESPIRO além do vão de 6 do `.inventory-body` (olhado no navegador em 2026-09-26,
   Mochila aberta na corrida): com 6, as 3 colunas de vagas e as 6 colunas da grade viravam UMA grade
   de 9 colunas para os olhos — o vão entre elas (6) era quase o vão entre células (2), e as vagas vazias, que o lobby
   separa com a prévia do paperdoll e a ficha, passavam por células vazias da mochila. 8 px a mais (14 no total, 7 × o
   vão interno) devolvem a quebra de coluna sem tirar nada da grade: a janela da corrida cresce de 360 para 368 e
   termina em x 508, ainda à esquerda do painel de números (x 518). */
.equip-layout.is-rail { grid-template-columns: repeat(3, calc(36 * var(--px))); margin-right: calc(8 * var(--px)); }
/* --- builds --- CORRIDA COM BUILD PRÓPRIA (docs/specs/loadouts.md §8; `widgets/equipPanel.js › seal`): o trilho mostra o
   lutador, e o selo `[ico_loadout] Build da Masmorra` mora EMBAIXO dele, na sobra de altura da coluna (a grade tem 188 =
   5 × 36 + 4 × 2; o trilho, 150), fora do `role="grid"` das vagas. Ele acompanha o respiro do trilho e quebra em duas
   linhas se precisar ("Build da" / "Masmorra"): o nome vai inteiro, nunca cortado (UX-GRAMATICA §3). */
.equip-rail-col { display: flex; flex-direction: column; gap: calc(4 * var(--px)); flex: none; }
.equip-build-seal { display: flex; align-items: center; gap: calc(2 * var(--px)); margin-right: calc(8 * var(--px)); padding: var(--px) calc(2 * var(--px)) var(--px) var(--px); border: var(--px) solid var(--c-text-dim); background: var(--c-field-bg); line-height: calc(8 * var(--px)); }
.equip-build-seal-ico { flex: none; width: calc(16 * var(--px)); height: calc(16 * var(--px)); background: var(--ui-ico_loadout, none) center / 100% 100% no-repeat; image-rendering: pixelated; }
.equip-build-seal-text { min-width: 0; }
/* O selo das PORTAS (`widgets/buildSeal.js`, §9 d): moldura + o `ico_loadout` da série + palavra curta (G2); `Incompleta`
   em perigo. Na ficha da Masmorra e da Torre ele tem a largura do que diz (G4: a pista não vira listra). */
.build-seal { display: inline-flex; align-items: center; gap: calc(2 * var(--px)); flex: none; padding: 0 calc(3 * var(--px)) 0 var(--px); border: var(--px) solid var(--c-text-dim); background: var(--c-field-bg); color: var(--c-text); line-height: calc(8 * var(--px)); white-space: nowrap; }
.build-seal.is-incomplete { border-color: var(--c-danger); color: var(--c-danger); }
.build-seal-ico { flex: none; width: calc(16 * var(--px)); height: calc(16 * var(--px)); background: var(--ui-ico_loadout, none) center / 100% 100% no-repeat; image-rendering: pixelated; }
.dg-facts > .build-seal, .tw-facts > .build-seal { align-self: flex-start; }
/* A PISTA NUM SELO (a regra do `.create-seal`, screens.css): o pontilhado de `base.css` cairia sobre a moldura sólida —
   promessa invisível. A moldura de baixo é que vira pontilhada, na cor do selo. */
#ui-root .build-seal[data-hint].has-more, #ui-root .equip-build-seal[data-hint].has-more { border-bottom: var(--px) dotted var(--c-text-dim); }
#ui-root .build-seal.is-incomplete[data-hint].has-more { border-bottom-color: var(--c-danger); }

.stat-list {
  display: grid;
  grid-template-columns: auto auto;
  column-gap: calc(8 * var(--px));
  row-gap: calc(1 * var(--px));
}
.stat-list dt { color: var(--c-text-dim); }
.stat-list dd { margin: 0; text-align: right; }

/* Tooltip de item (§2): moldura `.tooltip-frame` (nine-slice.css), largura 176, nunca captura eventos. */
.tooltip {
  position: absolute;
  z-index: 50;
  max-width: calc(200 * var(--px));
  pointer-events: none;
}
.tooltip-body { display: flex; flex-direction: column; gap: calc(1 * var(--px)); line-height: 1.15; }
.tooltip-name { overflow-wrap: anywhere; }
/* o corpo é flex em coluna e estica os filhos: sem isto a caixa do nome Shining ocupava a linha inteira (176) e o texto
   pegava só ~1/5 do gradiente — lia como uma cor só. Com a caixa do tamanho do texto o arco-íris cobre o nome. */
.tooltip-name.is-shining-text { align-self: flex-start; max-width: 100%; }
/* --- tooltip-v3 --- O CABEÇALHO DE CARTÃO, em TODO tooltip de item (docs/specs/ui-gameplay.md §2; o rework do card "Rework visual
   nos itens", 2026-10-06): a arte é a `.tooltip-head-art.item-art` de 36 — a peça estática do slot (`widgets/itemArt.js ›
   itemArtEl`, nine-slice.css › .item-art): o PNG 32×32 inteiro, a moldura de qualidade `slot_q_<key>` em 1:1 (modelo e
   material, a base neutra) e o +N da Refinaria no canto ↘ (o `.slot-refine` do slot) —, à esquerda do nome, da meta e do
   VEREDITO. Nenhuma regra daqui desenha a arte: o fio de 1 px na cor de texto que morava aqui saía branco no Normal e no
   Radiante. SEM o selo de tier: o "Tier V" da meta mora ao lado (G6). Até 2026-10-06 o cabeçalho só existia no chip do chat. */
.tooltip-head { display: flex; align-items: center; gap: calc(4 * var(--px)); }
.tooltip-head-text { display: flex; flex-direction: column; gap: calc(1 * var(--px)); flex: 1; min-width: 0; }
/* O VEREDITO (a última linha do cabeçalho): `[◆] ΔPotencial · [¤] preço · Nv N`, um vão de 4 entre as fichas (G1: o ponto
   médio decorativo custaria 2 caracteres numa coluna de 26) e o Nv encostado à direita; nunca quebra. O ◆ reserva a largura de
   "+99.999" (9 + 2 + 7 × 4,8): ele chega depois de abrir (`itemTooltip.js › schedulePower`, "…" até lá) e o preço ao lado não
   pula. O número em negrito (o "Gear Score" da ref-3, sem crescer a linha: a Courier New tem o mesmo avanço no negrito).
   O selo do cadeado que não cabe (◆ reservado + ¤ de 6 + "Nv 50" = 133 na coluna de 128: o `nowrap` o empurrava 5 px para fora
   da moldura) fica só com o número — a conta é de `itemTooltip.js › fitLevel`, nesta mesma régua. O `flex-wrap` é a rede: se a
   letra da máquina não for a da régua, o Nv desce de linha em vez de sair da moldura. */
.tooltip-verdict { display: flex; flex-wrap: wrap; align-items: center; gap: var(--px) calc(4 * var(--px)); white-space: nowrap; overflow-wrap: normal; }
.tooltip-power, .tooltip-price { display: inline-flex; align-items: center; gap: calc(2 * var(--px)); flex: none; }
.tooltip-power { min-width: calc(45 * var(--px)); }
.tooltip-power-value { font-weight: bold; }
.tooltip-price-stack { margin: 0 var(--px); }
.tooltip-level { margin-left: auto; flex: none; }
.tooltip-ico9 { display: inline-block; flex: none; width: calc(9 * var(--px)); height: calc(9 * var(--px)); background: center / 100% 100% no-repeat; image-rendering: pixelated; }
.tooltip-ico9.is-power { background-image: var(--ui-ico_power, none); }
.tooltip-ico9.is-coin { background-image: var(--ui-ico_gold, none); }
/* O ESTADO da peça na linha da meta (consertar tooltip-impl, 2026-10-06; `itemView.tooltipLines › states`): o selo `Corrompida`
   (`.tooltip-seal.is-bad`) e a ficha do desgaste `[marca] N%` — a marca é a do canto ↙ do slot (`ico_dur_worn`, a placa rachada,
   durability_marks.py), em cinza enquanto a peça não está desgastada e âmbar com o número vermelho quando está. Eram duas linhas do
   corpo ("Corrompida — não muda mais.", "Durabilidade 8%"), e a arma de fim de jogo passava do palco; ao lado do "Tier V" elas não
   custam altura (a fila quebra entre as fichas quando não cabe). */
.tooltip-meta.has-states { display: flex; flex-wrap: wrap; align-items: center; gap: var(--px) calc(4 * var(--px)); }
.tooltip-state { flex: none; }
.tooltip-dur { display: inline-flex; align-items: center; gap: calc(2 * var(--px)); white-space: nowrap; }
.tooltip-ico9.is-dur { background-image: var(--ui-ico_dur_worn, none); }
.tooltip-dur.is-dim > .tooltip-ico9 { filter: grayscale(1); opacity: 0.8; }
/* --- polimento-tt (5) --- Sem lugar ao lado da meta (a 2H de meta em duas linhas) e com folga no fim do NOME que quebrou, os selos
   moram lá (`itemTooltip.js › render`, a régua `lastLinePx`/`statesPx`): o nome vira texto em linha e os selos seguem a última
   linha dele — a linha que eles abririam sozinhos custava 10 px no pior caso (o teto de 344 do §2). */
.tooltip-name-row { overflow-wrap: anywhere; }
.tooltip-name-row > .tooltip-name.is-inline { display: inline; }
.tooltip-name-row > .tooltip-state { margin-left: calc(4 * var(--px)); vertical-align: middle; }
/* O selo de moldura tem 10 px (a letra de 8 + o aro) numa linha de 9,2: a margem de −1 em cima e embaixo o deixa sobre a linha sem
   esticá-la (no fim do nome, na fila da meta e no adicional, `.tooltip-tag` abaixo) — cada linha com selo crescia ~1 px. */
.tooltip-name-row > .tooltip-seal, .tooltip-meta.has-states > .tooltip-seal { margin-top: calc(-1 * var(--px)); margin-bottom: calc(-1 * var(--px)); }
/* Selo de ESTADO no corpo do tooltip (G2: moldura + palavra curta): `Slot vazio` no "vs", `Empate` no ◆. */
.tooltip-seal { display: inline-block; padding: 0 calc(2 * var(--px)); font-size: var(--text-1); line-height: 1; color: var(--c-text-dim); border: var(--px) solid currentColor; white-space: nowrap; vertical-align: middle; font-weight: normal; }
.tooltip-line { color: var(--c-text); overflow-wrap: anywhere; }
.tooltip .is-good, .tooltip-line.is-good { color: var(--c-ok); }
.tooltip .is-bad, .tooltip-line.is-bad { color: var(--c-danger); }
.tooltip .is-dim, .tooltip-line.is-dim { color: var(--c-text-dim); }
.tooltip .is-gold { color: var(--c-gold); }
/* A régua `.tooltip-sep` FOI EMBORA (2026-09-25): eram 4 linhas de zero informação numa arma. Quem separa os grupos
   agora é o vão de `.is-block` — a primeira linha de cada grupo (base, adicionais, efeito, requisito, comparação,
   uso, material, detalhes) pede 3 px acima de si. `:first-child` não paga: o nome já é o começo do corpo. */
.tooltip-body > .is-block:not(:first-child) { margin-top: calc(3 * var(--px)); }

/* LINHA DE ATRIBUTO / ADICIONAL — `[ícone] Nome ……… valor` (pedido do dono: "um ícone seguido pelo nome do atributo").
   Grade de 3 colunas e não flex solto: com flex, um ícone a menos (stat sem arte) desalinhava a coluna do nome e a
   lista inteira ficava torta — é justamente o efeito que o arranjo quer evitar. `auto` na 3ª coluna encosta TODOS os
   números à direita, que é o que faz a lista ler como tabela. A 4ª coluna é o Δ contra a vestida (--- tooltip-v3 ---; antes o
   "(+N%)" da qualidade × refino, que saiu da tela — P2 do dono). */
.tooltip-stat {
  display: grid;
  grid-template-columns: calc(16 * var(--px)) 1fr auto auto;
  align-items: center;
  column-gap: calc(3 * var(--px));
}
.tooltip-stat-label { min-width: 0; color: var(--c-text-dim); overflow-wrap: anywhere; }
.tooltip-stat-value { color: var(--c-text); text-align: right; }
/* --- tooltip-v3 --- o Δ POR LINHA (ref-1): 4ª coluna, presente em TODA linha (vazia nas outras) e do tamanho do texto —
   "▲2.760" verde, "▼0,15" vermelho, "▲" sozinho (a vestida não tem: tudo é ganho). ▲/▼ e não "+"/"−": o adicional já escreve
   "+374" (G1); a seta é forma, não só cor (§8.7). RESERVAR largura fixa para ela foi tentado e desfeito com a imagem na mão
   (no "(+N%)" de antes): 29 art-px sobre os 168 úteis faziam "Velocidade de ataque" quebrar em duas linhas. Como está, o par
   valor + Δ é que encosta à direita, e o Δ lê como anotação do número. */
.tooltip-stat-delta { text-align: right; white-space: nowrap; }
/* adicional: mesma grade, cor de "ganho" no nome e no número (o losango "◆" saiu — o ícone já marca a linha) */
.tooltip-stat.is-affix > .tooltip-stat-label, .tooltip-stat.is-affix > .tooltip-stat-value { color: var(--c-ok); }
/* --- temper --- a Maldição (pool maldita, valor negativo — temper.md §7): o nome e o número em perigo, com o selo Maldição */
.tooltip-stat.is-affix.is-bad > .tooltip-stat-label, .tooltip-stat.is-affix.is-bad > .tooltip-stat-value { color: var(--c-danger); }
/* --- temper --- selo curto DENTRO do rótulo do adicional (G2: estado é selo, não frase): Fixado / Corrompida. Filho do rótulo, não da linha:
   a linha é grade de 4 colunas e um 5.º filho a desalinharia. `white-space: nowrap` para o selo não quebrar sozinho quando o nome quebra. */
.tooltip-stat-label > .tooltip-tag { display: inline-block; margin: calc(-1 * var(--px)) 0 calc(-1 * var(--px)) calc(3 * var(--px)); padding: 0 calc(2 * var(--px)); font-size: var(--text-1); line-height: 1; color: var(--c-gold); border: var(--px) solid currentColor; white-space: nowrap; vertical-align: middle; }
.tooltip-stat-label > .tooltip-tag.is-corrupted { color: var(--c-danger); }
/* ícone 16×16 da série de stats (art/src/ui/icons_*.py; `ui/statIcons.js` → `manifest.ui`). `flex: none`/`grid` já
   fixam a coluna; `is-missing` (arte ausente) cai no xadrez magenta de nine-slice.css. */
.tooltip-ico {
  width: calc(16 * var(--px));
  height: calc(16 * var(--px));
  background: center / calc(16 * var(--px)) calc(16 * var(--px)) no-repeat;
  image-rendering: pixelated;
}
/* --- tooltip-v3 --- O QUE SE PERDE: o que só a vestida tem, UMA linha de fichas "ícone + ▼N" — nunca quebra (o que não cabe vira
   "+N", `itemView.tooltipLines › lost`, e o grupo diz TODAS no `aria-label`): a fila nunca passa de uma linha (o teto do §2). */
.tooltip-lost { display: flex; flex-wrap: nowrap; align-items: center; gap: 0 calc(6 * var(--px)); overflow: hidden; white-space: nowrap; }
.tooltip-chip { display: inline-flex; align-items: center; gap: calc(2 * var(--px)); flex: none; }
.tooltip-lost-more { flex: none; color: var(--c-text-dim); }
/* A ficha usa o ícone NO TAMANHO DA SÉRIE (16), como as linhas de atributo logo acima. Reduzi-la para 12 foi tentado
   e desfeito com a imagem na mão: sem rótulo ao lado, `ico_attr_str` (punho/braço) e `tal_atk` (punho) viram a MESMA
   mancha bege a 12 px, e a faixa "punho +14.152 · punho +18" fica ilegível — dois tamanhos de ícone no mesmo tooltip
   custavam a única coisa que a ficha tem para se explicar. A 16 px a faixa quebra em duas linhas; vale a troca. */
/* Silenciável: volta com Shift (`is-detailed`) ou com o tooltip fixado por toque. A pista fica visível — texto
   escondido sem pista é texto perdido — e vira "Shift: menos" quando o bloco está aberto; FIXADO ela some (os detalhes já
   estão abertos e o Shift não muda nada: `itemTooltip.js › applyDetails`, --- mercador ---). */
.tooltip-body > .is-detail { display: none; }
.tooltip.is-detailed .tooltip-body > .is-detail { display: block; }
.tooltip.is-detailed .tooltip-body > .tooltip-stat.is-detail { display: grid; }
/* --- recipe-uses --- "Usado em" com o gesto: fila dos ícones do que as receitas FAZEM com o ingrediente
   (itemView.tooltipLines › kind 'uses'). Ícone de item 32×32 reduzido a 16 (o tamanho da série do tooltip, via
   `.tooltip-ico`), selo de tier no canto superior esquerdo como no slot (`.slot-tier`), "+N" no fim. 8 ícones
   (`USES_MAX`) + vãos de 2 + "+NN" cabem nos 168 úteis numa linha só. */
.tooltip-uses { display: flex; flex-wrap: wrap; align-items: center; gap: calc(2 * var(--px)); }
.tooltip.is-detailed .tooltip-body > .tooltip-uses.is-detail { display: flex; }
/* fundo de mini-slot: sem ele o ícone solto no fundo escuro fazia o selo de tier parecer flutuar ACIMA da peça (a arte
   32×32 tem margem transparente) — com a casinha o selo volta a ler como canto, igual ao slot. Vem depois de
   `.tooltip-ico` (que zera a cor pelo atalho `background`), por isso só a cor e a borda interna. */
.tooltip-use { position: relative; flex: none; background-color: rgba(0, 0, 0, 0.35); box-shadow: inset 0 0 0 var(--px) rgba(255, 255, 255, 0.08); }
.tooltip-use-tier {
  position: absolute;
  left: 0;
  top: 0;
  height: calc(5 * var(--px));
  font-size: calc(5 * var(--px));
  line-height: 1;
  white-space: nowrap;
  color: var(--c-text);
  background: rgba(10, 9, 16, 0.82);
}
.tooltip-uses-more { color: var(--c-text-dim); margin-left: var(--px); }
/* --- /recipe-uses --- */
.tooltip-more { margin-top: calc(3 * var(--px)); color: var(--c-text-dim); opacity: 0.75; }
/* --- tooltip-v3 --- aberto, a pista ENCOSTA À DIREITA na última linha da dica (`itemTooltip.js › applyDetails` a põe dentro da
   `.tooltip-hint`): o float sobe para a linha em que cabe (não coube, desce) e o `flow-root` conta a altura dele — altura zero. A
   margem de cima da pista sai: no float ela a empurraria para baixo da linha. */
.tooltip.is-detailed .tooltip-body > .tooltip-hint.is-detail { display: flow-root; }
.tooltip-hint > .tooltip-more { float: right; margin: 0 0 0 calc(3 * var(--px)); }
/* --- tooltip-v3 --- A CAIXA DO EFEITO DE ARMA (ref-3 "LEGENDARY EFFECT"): a matéria da casinha "Usado em" (`.tooltip-use`: fundo
   escuro e aro de 1), com a marca 12×12 e o nome; a descrição entra nela com o gesto. Custa 2 px (o respiro de 1 em cima e embaixo). */
.tooltip-effect-box { display: flex; flex-direction: column; gap: var(--px); padding: var(--px) calc(2 * var(--px)); background-color: rgba(0, 0, 0, 0.35); box-shadow: inset 0 0 0 var(--px) rgba(255, 255, 255, 0.08); }
.tooltip-effect-box > .is-detail { display: none; }
.tooltip.is-detailed .tooltip-effect-box > .is-detail { display: block; }
/* --- tooltip-v3 --- P1 (o dono, 2026-10-06): o PAR da ref-2 — no Shift do PC, a peça VESTIDA aberta ao lado, compacta, com o selo
   "Equipado" (G2: estado é selo; altura zero). --- polimento-tt --- O selo abre o VEREDITO da vestida, no lugar do ◆ que ela não
   tem (`itemTooltip.js › ensurePair`): pendurado 7 acima da moldura, encostava no número da pilha do slot de cima. É o
   `.tooltip-seal` em ouro. Com o par à vista, a linha "vs" sai da vista (fica no leitor de tela, `.sr-only`) e a lista abre o grupo
   dela com o vão de 3. */
.tooltip-pair-seal { flex: none; color: var(--c-gold); }
.tooltip-compare.sr-only + .tooltip-stat { margin-top: calc(3 * var(--px)); }
.tooltip.is-pinned { outline: var(--px) solid var(--c-gold); outline-offset: calc(-1 * var(--px)); }

/* Nome Shining: gradiente de manifest.shining.bands (`--shining-gradient`, gerado no boot) varrendo em steps(16). */
.is-shining-text {
  /* véu branco de 40 % por cima das faixas: o azul/roxo puro das bandas some no fundo escuro do tooltip */
  background: linear-gradient(rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.4)), var(--shining-gradient, linear-gradient(90deg, #fff, #fff));
  background-size: 200% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* `.q<id>` (gerada no boot, depois desta folha) também define `color`: o preenchimento do texto decide */
  -webkit-text-fill-color: transparent;
  display: inline-block;
  animation: shining-text 1.333s steps(16) infinite;
}
/* um período inteiro: a imagem tem 2× a largura do texto, então o deslocamento de −2W é `200%` (offset = (W − 2W) × p);
   com −100% o laço dava um salto de meia volta a cada ciclo */
@keyframes shining-text {
  from { background-position: 0 0; }
  to   { background-position: 200% 0; }
}

/* Menu de contexto (§0.4): moldura tooltip, entradas em coluna; desabilitada com o motivo no tooltip de texto, ao lado
   (hover, foco, clique e toque — `contextMenu.js`). */
.ctx-menu {
  position: absolute;
  z-index: 60;
  display: flex;
  flex-direction: column;
  min-width: calc(64 * var(--px));
}
.ctx-menu-item {
  background: none;
  border: 0;
  padding: calc(1 * var(--px)) calc(3 * var(--px));
  text-align: left;
  color: var(--c-text);
  cursor: pointer;
}
.ctx-menu-item:hover, .ctx-menu-item:focus { background: rgba(255, 255, 255, 0.12); outline: none; }
.ctx-menu-item:active { background: rgba(255, 255, 255, 0.2); }
.ctx-menu-item:disabled, .ctx-menu-item[aria-disabled="true"] { color: #6d6878; cursor: default; }
.ctx-menu-item[aria-disabled="true"]:hover, .ctx-menu-item[aria-disabled="true"]:focus { background: rgba(255, 255, 255, 0.05); }
/* Cabeçalho do menu (§0.4): o NOME do item no topo, na cor da qualidade (equipamento, `.q<id>`) ou em dourado (`.text-gold`)
   — o menu encosta no ícone e tapa o nome escrito ao lado dele (a linha do material na Fornalha, a do banco, a do livro), e
   na Mochila o slot nem tem nome escrito. Não é entrada: `aria-hidden` e fora da ordem das setas (`contextMenu.js`). Nome
   comprido QUEBRA em duas linhas dentro do teto de largura: reticências cortariam o nome sem gesto para ler o resto (o
   mais longo do acervo, "Pincel de Escama de Dragão e Pergaminho", tem 39 letras). */
.ctx-menu-head {
  flex: none;
  max-width: calc(140 * var(--px));
  padding: calc(1 * var(--px)) calc(3 * var(--px));
  border-bottom: calc(1 * var(--px)) solid rgba(255, 255, 255, 0.18);
  white-space: normal;
  overflow-wrap: anywhere;
  cursor: default;
}
/* Separador de grupo (§0.4: ações do contexto ─ "Onde conseguir" · "Procurar no Leilão" ─ Descartar): um fio de 1 px de
   arte com margem curta, a mesma régua de `pvp.css › .pvp-sep`. `role="separator"`, fora da ordem das setas
   (`contextMenu.js` anda só pelas entradas). */
.ctx-menu-sep {
  flex: none;
  height: calc(1 * var(--px));
  margin: calc(1 * var(--px)) calc(2 * var(--px));
  background: rgba(255, 255, 255, 0.18);
}
/* --- auto-consumable --- entrada liga/desliga (`menuitemcheckbox`, "Usar automaticamente"): o estado é a peça `toggle` da série
   (ui/toggles/toggle.<off|on>.png, 16×10 — a MESMA do AUTO das habilidades: uma figura, uma ideia), à direita do rótulo.
   O leitor de tela lê o `aria-checked` da própria entrada; a peça é `aria-hidden`. */
.ctx-menu-item.is-check { display: flex; align-items: center; justify-content: space-between; gap: calc(4 * var(--px)); }
.ctx-menu-toggle {
  flex: none;
  width: calc(16 * var(--px));
  height: calc(10 * var(--px));
  background: var(--ui-toggle-off, linear-gradient(90deg, #d8c8a0 0 40%, #2a2118 40% 100%)) no-repeat;
  background-size: 100% 100%;
  image-rendering: pixelated;
}
.ctx-menu-item[aria-checked="true"] > .ctx-menu-toggle {
  background-image: var(--ui-toggle-on, linear-gradient(90deg, #2a2118 0 60%, #e0b040 60% 100%));
}
/* --- vender --- (docs/specs/bulk-sell.md §9.4) a entrada Vender com o PREÇO à direita do rótulo: `Vender (●) 29` — a moeda 9×9
   da série (`.ico-gold`) e o número exato em ouro, a unidade nunca escrita (UX G1). O leitor de tela lê o `aria-label` por
   extenso da entrada ("Vender por 29 de ouro"); a moeda é `aria-hidden`. Entrada travada não desenha preço. */
.ctx-menu-item.is-price { display: flex; align-items: center; justify-content: space-between; gap: calc(4 * var(--px)); }
.ctx-menu-price { display: inline-flex; align-items: center; gap: calc(2 * var(--px)); color: var(--c-gold); white-space: nowrap; }

/* Prompt de quantidade (§0.4, P1): painel pequeno centralizado sobre um backdrop leve. */
.qty-backdrop { background: rgba(0, 0, 0, 0.3); }
.qty-prompt { min-width: calc(150 * var(--px)); }
.qty-row { display: flex; align-items: center; justify-content: center; gap: calc(3 * var(--px)); }
.qty-step { min-width: calc(16 * var(--px)); }
.qty-value { min-width: calc(48 * var(--px)); text-align: center; }
.qty-total { margin-top: calc(4 * var(--px)); text-align: center; }
.confirm-body { max-width: calc(240 * var(--px)); }

/* Venda rápida (bulk-sell): um degrau por qualidade. A cor é a classe `.q<id>` que o main.js gera de qualities.json.
   No DEGRAU a cor fica no marcador, não no texto: `.q<id>` só pinta `color`, e texto verde/azul/roxo sobre o miolo do
   botão secundário (e sobre o miolo claro do hover) era ilegível — num controle que decide uma venda irreversível. */
.bulk-sell-prompt { min-width: calc(210 * var(--px)); max-width: calc(270 * var(--px)); }
.bulk-sell-tiers { display: flex; flex-wrap: wrap; gap: calc(2 * var(--px)); }
.bulk-sell-tier[aria-pressed="true"] { outline: var(--px) solid var(--c-text); }
.bulk-sell-swatch { width: calc(5 * var(--px)); height: calc(5 * var(--px)); background: currentColor; flex: none; outline: var(--px) solid var(--c-text-dark); }
.bulk-sell-totals { margin: calc(4 * var(--px)) 0; text-align: center; }
/* Sem teto próprio: com 56 px e rolagem sem barra a 6.ª linha ("Shining ×11 · 1.184.712 ouro") sumia e o jogador
   confirmava a venda sem vê-la. Seis qualidades × 11 px cabem; se um dia não couberem, quem rola é o corpo do modal. */
.bulk-sell-breakdown { display: flex; flex-direction: column; gap: var(--px); }
.bulk-sell-row { display: flex; align-items: center; gap: calc(2 * var(--px)); }
.bulk-sell-dot { width: calc(4 * var(--px)); height: calc(4 * var(--px)); background: currentColor; flex: none; }
/* Sem nada a vender o painel explica o porquê (acima do teto, pilha fora, travas) em vez de mostrar um zero mudo. */
.bulk-sell-empty { display: flex; flex-direction: column; gap: var(--px); }
.bulk-sell-empty:empty { display: none; }
.bulk-sell-check-row { display: flex; align-items: center; gap: calc(2 * var(--px)); margin: calc(4 * var(--px)) 0 0; }
.bulk-sell-note { margin-top: calc(3 * var(--px)); }

/* Filtro de loot: o painel (interruptor + degraus de tier) mora em css/loot.css desde 2026-10-01. */
/* O Mascote (o alforje, o canil, o palco) mora em css/pet.css desde a v3 (docs/specs/pet.md §8). */
/* Distintivo do rodapé da Mochila: o filtro NUNCA está ligado em silêncio. */
.inventory-filter-badge { color: var(--c-gold); }
.inventory-filter-badge.hidden { display: none; }

/* Toasts no canto inferior direito, empilhados. Largura máxima de 200 (x ≥ 436): sem ela um toast longo alargava a
   pilha inteira (x 284→636) e cobria "Exploração avançada", "Mochila" e "Menu" da barra de modos (que acaba em x 424).
   O texto quebra em até duas linhas em vez de esticar; fundo opaco com moldura de 1 px (o translúcido deixava o rótulo
   dos botões atravessar a frase). z 110: acima do resumo da run (42), do menu (60), das janelas e TAMBÉM dos modais
   (100).
   S4/B4 (análise crítica de 2026-09-22): a pilha ficava em z 90, sob o backdrop do modal, e o aviso do sussurro
   rápido demais ("Aguarde um instante…") era escrito no store, criado no DOM e NUNCA visto — o jogador clicava
   Enviar duas vezes, perdia o texto e não tinha explicação nenhuma. O receio antigo ("o aviso não pode cobrir a
   pergunta") não se aplica: a pilha é `pointer-events: none` e vive no canto inferior direito, enquanto o modal é
   centrado — ela nunca rouba clique nem tapa os botões da pergunta. Um erro invisível é pior que um erro no canto. */
.toast-stack {
  position: absolute;
  z-index: 110;
  right: calc(4 * var(--px));
  bottom: calc(4 * var(--px));
  max-width: calc(200 * var(--px));
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: calc(2 * var(--px));
  pointer-events: none;
}
.toast {
  max-width: 100%;
  padding: calc(1 * var(--px)) calc(3 * var(--px));
  line-height: calc(10 * var(--px));
  white-space: normal;
  overflow-wrap: anywhere;
  background: #0e0c14;
  box-shadow: 0 0 0 var(--px) #4a4458;
  color: var(--c-text);
  animation: toast-in 0.15s steps(3) both;
}
/* no lobby a pilha nasce ACIMA da linha da barra de modos: com bottom 4 ela cobria "Jogadores online" (x ≥ 540,
   y 340…348). 28 = a pilha termina em y 332, o topo da barra — o aviso cai sobre o cenário, nunca sobre um botão. */
/* 8 px da borda direita (era 4): com 4 a pilha encostava no bisel da moldura da janela aberta embaixo dela e as duas
   linhas se liam como uma só (medido sobre a janela de PvP — avaliação de UX, rodada 2). Sair da área da janela não dá:
   as janelas do lobby vão de x 4 a x 636 e a faixa y 332…360 é da barra de modos e de "Jogadores online"; a pilha é
   passageira e fica acima delas (z 90). */
#ui-root:has(#screen-lobby) .toast-stack:not(.is-explore) { bottom: calc(28 * var(--px)); right: calc(8 * var(--px)); }
/* na seleção de personagem a pilha descia até y 357 de 360 (colada na borda do palco): mesma margem do lobby */
#ui-root:has(#screen-select) .toast-stack:not(.is-explore) { bottom: calc(28 * var(--px)); }
/* com o resumo da run aberto (painel em x 140…500, até y 352) a pilha fica à direita dele, fora do retângulo */
#ui-root:has(.summary-backdrop) .toast-stack:not(.is-explore) { max-width: calc(132 * var(--px)); }
.toast.is-error { color: var(--c-danger); }
@keyframes toast-in {
  from { transform: translateY(calc(4 * var(--px))); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
/* RECIBO COM ITENS (js/ui/widgets/toast.js › fillItems; a coleta do refino — pedido do dono, 2026-10-01: "exiba também o ícone
   com tier e nome do item, para deixar a notificação mais dinâmica e legal"): a cabeça curta, apagada, e UMA LINHA POR ITEM —
   o mini-slot 16×16 do banco (`.bankmat-ico` com o selo de tier ↖: a mesma casinha da pílula de entrega, dos chips do Pronto e
   da lista do Banqueiro), o nome e o `×N` encostado à direita (a coluna das quantidades lê como recibo). Linha de 16 (a
   figura); com 4 linhas, 2 + 10 + 2 + 4 × 16 + 3 × 2 + 3 = 87 de altura — sob as janelas de 276 não cabe, e a pilha vai para
   o lado livre (js/ui/widgets/toast.js › toastStackSpot; client/tests/ui/toastStack.test.js §B73d). As linhas pousam uma depois da outra (deslizam 6 px
   em 3 degraus) e o ícone de cada uma pisca um brilho com o aro em ouro (2 degraus) — com movimento reduzido, nada se mexe. */
.toast.has-items {
  display: flex;
  flex-direction: column;
  gap: calc(2 * var(--px));
  padding: calc(2 * var(--px)) calc(3 * var(--px)) calc(3 * var(--px));
}
.toast-head { color: var(--c-text-dim); }
.toast-items { display: flex; flex-direction: column; gap: calc(2 * var(--px)); }
.toast-item {
  display: flex;
  align-items: center;
  gap: calc(3 * var(--px));
  min-height: calc(16 * var(--px));
  animation: toast-item-in 0.15s steps(3, jump-none) both;
  animation-delay: calc(0.12s + var(--i, 0) * 0.08s);
}
.toast-item-name { flex: 1 1 auto; min-width: 0; }
.toast-item-qty { flex: none; padding-left: calc(3 * var(--px)); white-space: nowrap; }
/* "+N": os itens além do teto (a frase do leitor de tela diz todos) — apagado e alinhado aos nomes (16 do ícone + 3 do vão) */
.toast-item.toast-item-more { min-height: 0; padding-left: calc(19 * var(--px)); color: var(--c-text-dim); }
.toast-item > .bankmat-ico { animation: toast-item-glint 0.32s steps(2, jump-none) both; animation-delay: calc(0.27s + var(--i, 0) * 0.08s); }
@keyframes toast-item-in {
  from { transform: translateX(calc(-6 * var(--px))); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
@keyframes toast-item-glint {
  from { filter: brightness(1.8); box-shadow: inset 0 0 0 var(--px) var(--c-gold); }
  to   { filter: none; box-shadow: inset 0 0 0 var(--px) rgba(255, 255, 255, 0.08); }
}
@media (prefers-reduced-motion: reduce) {
  .toast.has-items, .toast-item, .toast-item > .bankmat-ico { animation: none; }
}
/* AVISO COM ÍCONE (js/ui/widgets/toast.js › iconOf; `ToastEntry.icon`): a peça 16×16 de `manifest.ui` na FRENTE da frase — a
   habilidade que o trabalhador aprendeu, com a mesma peça da ficha e do Capataz (docs/specs/workers-nodes.md §8). A frase mora
   numa caixa só (`.toast-text`): a 2.ª linha não passa por baixo do ícone, que fica centrado na altura dela. */
.toast.has-icon { display: flex; align-items: center; gap: calc(3 * var(--px)); }
.toast-ico { width: calc(16 * var(--px)); height: calc(16 * var(--px)); }
.toast-text { min-width: 0; }

/* Modal: escurece o palco e centraliza o painel. */
.modal-backdrop {
  position: absolute;
  inset: 0;
  z-index: 100;
  display: grid;
  /* trilha com altura definida (o palco): sem ela o `max-height` em % do `.modal` não tem base e não vale */
  grid-template-rows: minmax(0, 1fr);
  place-items: center;
  background: var(--c-shade);
}
/* Nenhum modal passa do palco: coluna com teto (8 de respiro em cima e embaixo), o CORPO rola e a fileira de botões
   (`.modal-actions`) fica sempre visível. Sem isto o seletor de título com 18 títulos media 474 px de arte e deixava
   "Fechar" e os 4 últimos títulos fora da tela. */
.modal {
  min-width: calc(160 * var(--px));
  max-width: calc(400 * var(--px));
  display: flex;
  flex-direction: column;
  max-height: calc(100% - 16 * var(--px));
}
.modal > .window-title, .modal > .modal-actions { flex: none; }
/* 4 px de recuo lateral: o `.window-body` puxa −2 de cada lado para entrar na faixa da moldura, e no modal o texto ia
   de bisel a bisel — no "Apagar personagem" as aspas do nome encostavam na moldura dos DOIS lados e o campo de
   digitação começava exatamente onde o relevo acaba (família do B26; avaliação de UX, rodada 2). */
.modal > .window-body { flex: 1 1 auto; min-height: 0; padding: 0 calc(4 * var(--px)); overflow-x: hidden; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #575273 #12101c; }
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: calc(4 * var(--px));
  margin-top: calc(6 * var(--px));
}

/* Arrastar item: fantasma segue o ponteiro. */
.drag-ghost {
  position: absolute;
  z-index: 200;
  width: calc(32 * var(--px));
  height: calc(32 * var(--px));
  pointer-events: none;
  opacity: 0.85;
  image-rendering: pixelated;
}
.drag-ghost { filter: drop-shadow(var(--px) var(--px) 0 rgba(0, 0, 0, 0.6)); }
/* Conteúdo = clones do slot de origem (dragdrop.js `fillGhost`): o ícone ocupa os 32×32 do fantasma e a moldura de
   qualidade fica 2 px para fora, como no slot (36×36). `is-shining`/`has-glow` valem pelos seletores de `.item-icon` e
   `.slot-frame`; o xadrez de ícone ausente (`.is-missing`, nine-slice.css) mantém o próprio background-size. */
.drag-ghost > .item-icon { position: absolute; inset: 0; background-repeat: no-repeat; }
.drag-ghost > .item-icon:not(.is-missing) { background-size: 100% 100%; }
.drag-ghost > .slot-frame { inset: calc(-2 * var(--px)); }
.slot.is-drop-target { outline: var(--px) solid var(--c-gold); outline-offset: calc(-1 * var(--px)); }
.slot.is-drop-invalid { outline: var(--px) solid var(--c-danger); outline-offset: calc(-1 * var(--px)); }
.is-item-dragging, .is-item-dragging * { cursor: grabbing !important; }

/* Ícone de item: Shining e brilho próprio sem WebGL (architecture.md §3.3; art-pipeline.md §5).
   Intensidade e alcance no DOM: um ícone 32×32 cabe inteiro em 2/3 do tile de 16 faixas, então com o `amp` cheio
   (0,8) e a máscara `shine` (255 em quase todo pixel opaco) ele vira arco-íris chapado e ilegível. Duas reduções,
   ambas dentro da fórmula normativa `screen(c, s)`: (1) `opacity` k no layer em screen ≡ `s × k` (screen(c, k·s) =
   (1−k)·c + k·screen(c, s)) — amp efetivo 0,8 × 0,55 ≈ 0,44; (2) 2ª camada de máscara (gradiente diagonal ↖→↘, em
   `mask-composite: intersect`) limita o alcance ao lado iluminado, como um reflexo — o lado sombreado guarda a cor
   do item. As faixas continuam varrendo em steps(16) como no shader. */
.item-icon { position: relative; image-rendering: pixelated; }
.item-icon.is-shining::after {
  content: '';
  position: absolute;
  inset: 0;
  background: url(../assets/ui/fx/shining_bands.png) repeat;
  background-size: calc(48 * var(--px)) calc(48 * var(--px));
  mix-blend-mode: screen;
  opacity: var(--shining-dom-amp, 0.55);
  -webkit-mask-image: var(--icon-mask), linear-gradient(135deg, #000 0%, #000 30%, rgba(0, 0, 0, 0.45) 60%, rgba(0, 0, 0, 0.12) 100%);
  mask-image: var(--icon-mask), linear-gradient(135deg, #000 0%, #000 30%, rgba(0, 0, 0, 0.45) 60%, rgba(0, 0, 0, 0.12) 100%);
  -webkit-mask-size: 100% 100%, 100% 100%;
  mask-size: 100% 100%, 100% 100%;
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
  animation: shining-bands 1.333s steps(16) infinite;
  pointer-events: none;
}
@keyframes shining-bands {
  from { background-position: 0 0; }
  to   { background-position: calc(-48 * var(--px)) 0; }
}
.item-icon.has-glow::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--glow-color, #fff);
  -webkit-mask-image: var(--icon-glow);
  mask-image: var(--icon-glow);
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  animation: glow-pulse var(--glow-period, 1.2s) steps(6) infinite alternate;
  pointer-events: none;
}
@keyframes glow-pulse {
  from { opacity: 0.35; }
  to   { opacity: 1; }
}
