/* nine-slice.css — molduras da UI por border-image sobre a arte de assets/ui (architecture.md §3.3; art-pipeline.md §3.4).
   Regra: border-image: url(<asset>) <slice> [fill] / calc(<slice> * var(--px)) stretch; image-rendering: pixelated.
   Cada classe cita o asset-fonte (client/public/assets/ui/ui.json) em comentário.
   Arte ainda não entregue (ui-gameplay.md §9: panel_inset, tooltip, tab, btn_ability, toggle): a regra usa
   `var(--ui-<key>[-<estado>], url(<arte provisória>))`. O main.js gera `--ui-<key>` (URL absoluta) para TODA entrada de
   manifest.ui, então quando a peça entrar no manifesto a troca é automática, sem editar CSS. */

/* ---- painel: ui/windows/panel.png (slice 8, 24×24) ---------------------------------------------------- */
.panel {
  border: calc(8 * var(--px)) solid transparent;
  border-image: url(../assets/ui/windows/panel.png) 8 fill / calc(8 * var(--px)) stretch;
  image-rendering: pixelated;
  color: var(--c-text);
}

/* Painel rebaixado: ui/windows/panel_inset.png (slice 8, 24×24) — até existir, panel.png + sombra interna. */
.panel-inset {
  position: relative;
  border: calc(8 * var(--px)) solid transparent;
  border-image: var(--ui-panel_inset, url(../assets/ui/windows/panel.png)) 8 fill / calc(8 * var(--px)) stretch;
  image-rendering: pixelated;
}
.panel-inset::before {
  content: '';
  position: absolute;
  inset: calc(-4 * var(--px));
  background: rgba(0, 0, 0, 0.35);
  pointer-events: none;
}
.panel-inset > * { position: relative; }
/* Só a moldura: o centro fica transparente para o canvas aparecer atrás (prévia do paperdoll — criação e mochila). */
.panel-inset.is-see-through {
  border-image: var(--ui-panel_inset, url(../assets/ui/windows/panel.png)) 8 / calc(8 * var(--px)) stretch;
}
.panel-inset.is-see-through::before { content: none; }

/* ---- botões: ui/buttons/btn_<variante>.<estado>.png (slice 4, 12×12; estados normal/hover/pressed/disabled) -- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: calc(2 * var(--px));
  min-height: calc(16 * var(--px));
  padding: 0 calc(4 * var(--px));
  border: calc(4 * var(--px)) solid transparent;
  border-image: url(../assets/ui/buttons/btn_primary.normal.png) 4 fill / calc(4 * var(--px)) stretch;
  image-rendering: pixelated;
  background: none;
  color: var(--c-text-dark);
  text-shadow: none;
  cursor: pointer;
  line-height: 1;
  white-space: nowrap;
}
.btn:hover  { border-image-source: url(../assets/ui/buttons/btn_primary.hover.png); }
.btn:active { border-image-source: url(../assets/ui/buttons/btn_primary.pressed.png); }
.btn:disabled, .btn.is-disabled {
  border-image-source: url(../assets/ui/buttons/btn_primary.disabled.png);
  color: var(--c-text-dim);
  cursor: default;
}
/* O `disabled` do navegador não recebe ponteiro de jeito nenhum. O travado COM MOTIVO (`.is-disabled`,
   `widgets/button.js › setButtonReason`) RECEBE: é o hover e o clique nele que abrem o balão com o motivo
   (`widgets/hint.js`) — pedido do dono em 2026-09-30, "clicar ou manter o mouse no botão desativado mostra o motivo".
   Por isso hover e pressed não mudam o desenho do travado (o clique já não faz nada: a trava é o `aria-disabled`). */
.btn:disabled { pointer-events: none; }
.btn.is-disabled:hover, .btn.is-disabled:active { border-image-source: url(../assets/ui/buttons/btn_primary.disabled.png); color: var(--c-text-dim); }
.btn.is-disabled:active > * { transform: none; }
/* Bisel invertido: o conteúdo desce 1 px de arte. */
.btn:active > * { transform: translateY(var(--px)); }
.btn > * { display: inline-block; }

.btn-secondary { border-image-source: url(../assets/ui/buttons/btn_secondary.normal.png); color: var(--c-text); }
/* hover = miolo claro (#bcc8d6): o texto claro do estado normal caía para ~1,5:1 de contraste → texto escuro (~9:1) */
.btn-secondary:hover  { border-image-source: url(../assets/ui/buttons/btn_secondary.hover.png); color: var(--c-text-dark); }
.btn-secondary:active { border-image-source: url(../assets/ui/buttons/btn_secondary.pressed.png); }
.btn-secondary:disabled, .btn-secondary.is-disabled,
.btn-secondary.is-disabled:hover, .btn-secondary.is-disabled:active { border-image-source: url(../assets/ui/buttons/btn_secondary.disabled.png); }

.btn-danger { border-image-source: url(../assets/ui/buttons/btn_danger.normal.png); color: var(--c-text); }
.btn-danger:hover  { border-image-source: url(../assets/ui/buttons/btn_danger.hover.png); }
.btn-danger:active { border-image-source: url(../assets/ui/buttons/btn_danger.pressed.png); }
.btn-danger:disabled, .btn-danger.is-disabled,
.btn-danger.is-disabled:hover, .btn-danger.is-disabled:active { border-image-source: url(../assets/ui/buttons/btn_danger.disabled.png); }

.btn-block { display: flex; width: 100%; }

/* Botão da barra de habilidades e de poção (ui-gameplay.md §5.2): 36×36, ui/buttons/btn_ability.<estado>.png
   (slice 4, 12×12) — até existir, btn_secondary. A varredura de recarga é CSS (hud.css). */
.btn-ability {
  display: block;
  position: relative;
  width: calc(36 * var(--px));
  height: calc(36 * var(--px));
  padding: 0;
  border: calc(4 * var(--px)) solid transparent;
  border-image: var(--ui-btn_ability-normal, url(../assets/ui/buttons/btn_secondary.normal.png)) 4 fill / calc(4 * var(--px)) stretch;
  image-rendering: pixelated;
  background: none;
  color: var(--c-text);
  cursor: pointer;
  line-height: 1;
}
.btn-ability:hover  { border-image-source: var(--ui-btn_ability-hover, url(../assets/ui/buttons/btn_secondary.hover.png)); }
.btn-ability:active { border-image-source: var(--ui-btn_ability-pressed, url(../assets/ui/buttons/btn_secondary.pressed.png)); }
.btn-ability:disabled, .is-disabled > .btn-ability {
  border-image-source: var(--ui-btn_ability-disabled, url(../assets/ui/buttons/btn_secondary.disabled.png));
  cursor: default;
}

/* ---- slot de item: 36×36 em 1× (ícone 32 + moldura 2) ------------------------------------------------ */
/* Fundo por estado: ui/slots/slot_empty | slot_hover | slot_locked | slot_pending .png (slice 4, 12×12).
   `--spx` = px de arte DO SLOT: igual a `--px`, ou o dobro em `.slot-2x` (72 px — ampliação só por fator inteiro, §8.7). */
.slot {
  --spx: var(--px);
  position: relative;
  flex: none;
  width: calc(36 * var(--spx));
  height: calc(36 * var(--spx));
  border: calc(4 * var(--spx)) solid transparent;
  border-image: url(../assets/ui/slots/slot_empty.png) 4 fill / calc(4 * var(--spx)) stretch;
  image-rendering: pixelated;
  touch-action: none;   /* arrastar por toque entrega pointermove (dragdrop.js) */
}
.slot.slot-2x { --spx: calc(2 * var(--px)); }
.slot.has-item { cursor: pointer; }
.slot:hover, .slot:focus-visible { border-image-source: url(../assets/ui/slots/slot_hover.png); }
.slot.is-locked, .slot.is-locked:hover { border-image-source: url(../assets/ui/slots/slot_locked.png); }
.slot.is-pending, .slot.is-pending:hover,
.item-grid-wrap.is-loading .slot, .item-grid-wrap.is-loading .slot:hover { border-image-source: url(../assets/ui/slots/slot_pending.png); }
.slot.is-pending { cursor: progress; }
.slot.is-pending > .item-icon { opacity: 0.5; }
.slot.is-selected { outline: var(--px) solid var(--c-gold); outline-offset: calc(-1 * var(--px)); }
/* foco por teclado: aro de 2 px (1 dentro + 1 fora) — distingue da moldura dourada de Obra-prima e cabe no gap de 2 da grade */
.slot:focus-visible { outline: calc(2 * var(--px)) solid #fff5a8; outline-offset: calc(-1 * var(--px)); z-index: 1; }
.slot.is-dragging > .item-icon, .slot.is-dragging > .slot-frame, .slot.is-dragging > .slot-qty { opacity: 0.4; }
/* Equipamento que o personagem não pode usar: véu vermelho (§0.2). */
.slot.is-unusable::after {
  content: '';
  position: absolute;
  inset: calc(-2 * var(--spx));
  background: rgba(217, 72, 59, 0.35);
  pointer-events: none;
}
/* --- builds --- A peça do LUTADOR que mora na mochila, na corrida com build própria (docs/specs/loadouts.md §8;
   `itemSlot.js › SlotState.inUse`): o ícone apagado e, no MEIO da célula, o `ico_loadout` do selo do trilho — "esta é a
   peça que está lá". O meio, porque os quatro cantos têm dono (tier/efeito · seta/auto · pilha/+N · durabilidade). A
   frase vai no `aria-label` da célula (nenhuma informação só por imagem — ui-gameplay.md §8.7). */
.slot.is-in-build > .item-icon { opacity: 0.45; }
.slot.is-in-build::before {
  content: '';
  position: absolute;
  left: calc(6 * var(--spx));
  top: calc(6 * var(--spx));
  width: calc(16 * var(--spx));
  height: calc(16 * var(--spx));
  background: var(--ui-ico_loadout, none) center / 100% 100% no-repeat;
  image-rendering: pixelated;
  z-index: 1;
  pointer-events: none;
}

/* O conteúdo é posicionado a partir da caixa interna (dentro da borda de 4): −2 → 2 px da borda externa. */
.slot .item-icon, .slot .slot-ghost {
  position: absolute;
  left: calc(-2 * var(--spx));
  top: calc(-2 * var(--spx));
  width: calc(32 * var(--spx));
  height: calc(32 * var(--spx));
  background-repeat: no-repeat;
  background-size: 100% 100%;
  image-rendering: pixelated;
}
/* Silhueta do slot de equipar vazio: ui/slots/ghost_<slot>.png (32×32 fixo). */
.slot .slot-ghost { opacity: 0.55; pointer-events: none; }
/* --- linkchat --- A PEÇA ESTÁTICA do item (`widgets/itemArt.js › itemArtEl`; ui-gameplay.md §0.2-bis): o slot da Mochila SEM
   gestos — a base `slot_empty`, o ícone e a moldura de qualidade pelas MESMAS pintoras do slot (`paintItemIcon`,
   `paintItemFrame`) e o selo ↖. Dois tamanhos: 36 (ícone 32; a arte do cabeçalho do tooltip) e 20 com `is-mini` (ícone 16;
   o item linkado no chat — aro 0–2, ícone 2–18, selo em (3,3)). A `.slot-frame`, a `.slot-corrupt` e o `.slot-tier` valem
   como estão (inset −4 e borda 4 a partir da caixa interna): a moldura é SEMPRE a PNG em 1:1, nunca reduzida pela metade no
   CSS (o navegador escolheria os pixels — §8.7). ANTES de `.item-icon.is-missing`: com a mesma especificidade, declarada
   depois dele ela esticaria o xadrez do ícone ausente. */
.item-art {
  --spx: var(--px);
  position: relative;
  display: block;
  flex: none;
  box-sizing: border-box;
  width: calc(36 * var(--spx));
  height: calc(36 * var(--spx));
  border: calc(4 * var(--spx)) solid transparent;
  border-image: url(../assets/ui/slots/slot_empty.png) 4 fill / calc(4 * var(--spx)) stretch;
  image-rendering: pixelated;
}
.item-art.is-mini { width: calc(20 * var(--spx)); height: calc(20 * var(--spx)); }
.item-art > .item-icon {
  position: absolute;
  left: calc(-2 * var(--spx));
  top: calc(-2 * var(--spx));
  width: calc(32 * var(--spx));
  height: calc(32 * var(--spx));
  background-repeat: no-repeat;
  background-size: 100% 100%;
  image-rendering: pixelated;
}
.item-art.is-mini > .item-icon { width: calc(16 * var(--spx)); height: calc(16 * var(--spx)); }
/* --- /linkchat --- */
/* Ícone ausente no manifesto (§0.2): xadrez magenta — sintoma de R20 do data:validate (e da regra 52, que cobra a
   série de ícones de stat do tooltip: `client/public/js/ui/statIcons.js`). */
.item-icon.is-missing, .drag-ghost.is-missing, .tooltip-ico.is-missing {
  background-image: repeating-conic-gradient(#ff00ff 0 25%, #1a001a 0 50%);
  background-size: calc(8 * var(--px)) calc(8 * var(--px));
  background-repeat: repeat;
}

/* Moldura de qualidade = overlay ui/slots/slot_q_<qualityKey>.png (slice 4, 12×12).
   O JS escolhe o asset por manifest.ui["slot_q_" + key] e grava em --slot-frame; sem valor, cai em slot_q_normal. */
.slot-frame {
  position: absolute;
  inset: calc(-4 * var(--spx, var(--px)));
  border: calc(4 * var(--spx, var(--px))) solid transparent;
  border-image-source: var(--slot-frame, url(../assets/ui/slots/slot_q_normal.png));
  border-image-slice: 4;
  border-image-width: calc(4 * var(--spx, var(--px)));
  border-image-repeat: stretch;
  image-rendering: pixelated;
  pointer-events: none;
}
/* Shining: a moldura gira o matiz em passos (art-pipeline.md §3.4). */
.slot-frame.is-shining { animation: slot-shining 1.2s steps(12) infinite; }
@keyframes slot-shining {
  from { filter: hue-rotate(0deg); }
  to   { filter: hue-rotate(360deg); }
}

/* Quantidade da pilha: canto inferior direito. */
.slot-qty {
  position: absolute;
  right: calc(-2 * var(--px));
  bottom: calc(-3 * var(--px));
  font-size: var(--text-1);
  line-height: 1;
  color: var(--c-text);
  text-shadow: var(--px) var(--px) 0 #000, calc(-1 * var(--px)) 0 0 #000, 0 calc(-1 * var(--px)) 0 #000;
  pointer-events: none;
}

/* --- temper --- +N no canto inferior direito: mesma tipografia e sombra do .slot-qty (equipamento não empilha, então nunca disputam) */
.slot-refine {
  position: absolute;
  right: calc(-2 * var(--spx, var(--px)));
  bottom: calc(-3 * var(--spx, var(--px)));
  font-size: var(--text-1);
  line-height: 1;
  color: var(--c-gold);
  text-shadow: var(--spx, var(--px)) var(--spx, var(--px)) 0 #000, calc(-1 * var(--spx, var(--px))) 0 0 #000, 0 calc(-1 * var(--spx, var(--px))) 0 #000;
  pointer-events: none;
}
/* --- temper --- moldura de corrompida: mesma geometria da .slot-frame, POR CIMA dela; sem imagem no manifesto, não desenha nada */
.slot-corrupt {
  position: absolute;
  inset: calc(-4 * var(--spx, var(--px)));
  border: calc(4 * var(--spx, var(--px))) solid transparent;
  border-image-source: var(--slot-corrupt, none);
  border-image-slice: 4;
  border-image-width: calc(4 * var(--spx, var(--px)));
  border-image-repeat: stretch;
  image-rendering: pixelated;
  pointer-events: none;
}

/* Selo de TIER do item (I…X): canto SUPERIOR ESQUERDO do ícone, px (3,3)…(17,8) do slot 36 — a decisão do dono em
   2026-09-25, que por isso empurrou a seta de upgrade para o canto superior direito. É TEXTO, não arte: o numeral sai
   de `ui/format.js › formatRoman(tpl.tier)` e o tier vem do template (`shared/data/items/**`, faixas em
   `shared/data/config/tiers.json`). Nada de PNG — um asset por tier seria um número de balanceamento assado em arte
   (invariante 3 pelo avesso): o dia em que `tiers.json` ganhar um 11º tier, `XI` aparece sozinho. É o mesmo caminho que
   o `.slot-qty` já usa para o número da pilha.
   Tamanho: 6 px de arte, não os 8 de `--text-1`. A fonte da UI é `Courier New` enquanto `assets/fonts/ui.woff2` não
   existe (base.css › `has-ui-font`), e Courier tem avanço fixo de 0,6 em: a 8 px o pior numeral ("VIII", 4 glifos)
   mediria 19 px e cobriria MAIS DA METADE do ícone de 32; a 6 px mede 14,4 e cabe nos 15 px de `max-width` — a mesma
   pegada da marca de efeito de arma (12×12). `max-width` é teto, não corte: `overflow` continua visível para que um
   numeral maior apareça inteiro (e o teste reclame) em vez de sumir pela metade; a largura REAL encolhe no conteúdo
   (sem `padding`: o avanço do Courier já traz folga lateral dentro do glifo), senão "I" carregaria uma placa de 15 px
   quase toda vazia.
   Contraste — PLACA, não contorno, e isto foi MEDIDO no jogo em 2026-09-25 (art/previews/e2e/tier-seal-antes-8x.png, recorte a
   8×): o contorno de 8 sombras de 1 px de arte que este bloco tinha NÃO fechava. A 6 px de arte o traço do Courier é
   mais FINO que 1 px de arte, então as 8 cópias não encostam no glifo nem entre si e o que aparece atrás do numeral é
   uma GAIOLA de linhas pretas com vãos — uma mancha suja em volta de "VIII"/"IX"/"VII", pior quanto maior o `--px`.
   Aumentar o contorno só engrossaria a gaiola. Uma placa escura resolve de uma vez: o numeral claro sempre cai sobre a
   mesma base, tanto no diamante branco quanto no minério preto, e ela sobrevive ao véu vermelho de `.is-unusable`
   (`.slot.is-unusable::after` cobre TUDO, inclusive o selo — com a gaiola o numeral virava marrom sobre marrom), que é
   o caso comum na mochila de quem vê equipamento de outra classe. O `.slot-qty` não precisa de placa porque o canto
   inferior direito do ícone é quase sempre escuro; o superior esquerdo não é.
   `--spx` (e NÃO `--px`, a armadilha do `.slot-qty`): o selo dobra junto no slot 2× do weaponEffects.
   `.is-low` = o canto superior esquerdo está ocupado pela marca de efeito de arma (`.item-mark`, glifo em (2,2)…(13,13)
   por contrato de weapon-effects 03 §1.1): o selo desce para o canto inferior esquerdo, que é livre. Só acontece em arma
   encantada, e arma não empilha — por isso não disputa espaço com o `.slot-qty` (inferior DIREITO).
   Prova da posição: client/tests/ui/slotTierSeal.test.js. */
.slot-tier {
  position: absolute;
  left: calc(-1 * var(--spx, var(--px)));
  top: calc(-1 * var(--spx, var(--px)));
  max-width: calc(15 * var(--spx, var(--px)));
  height: calc(6 * var(--spx, var(--px)));
  font-size: calc(6 * var(--spx, var(--px)));
  line-height: 1;
  white-space: nowrap;
  color: var(--c-text);
  background: rgba(10, 9, 16, 0.82);
  pointer-events: none;
}
.slot-tier.is-low { top: auto; bottom: calc(-1 * var(--spx, var(--px))); }
.slot.is-dragging > .slot-tier { opacity: 0.4; }

/* Seta de item melhor que o equipado (game/formulas/upgrade.js; arte art/src/ui/upgrade_arrow.py): ui/icons/ico_upgrade.png
   9×10 no canto SUPERIOR DIREITO do ícone, px (24,3)…(32,12) do slot 36. Estava no canto superior ESQUERDO até
   2026-09-25 e mudou por dois motivos: (a) o canto superior esquerdo passou a ser do selo de tier (I…X); (b) ali a seta
   já caía EM CIMA da marca de efeito de arma (`.item-mark`, art/src/items/wmark_*.py), cujo glifo por contrato ocupa
   (2,2)…(13,13) do slot — a sobreposição sumia com a marca no item encantado. O canto superior direito estava livre: a
   pilha é embaixo à direita (`.slot-qty`, base do texto encostada em y 35) e o ornamento da moldura de qualidade fica em
   (32,2)/(33,2)/(33,3), fora da caixa da seta — e os cantos do PNG são transparentes de qualquer jeito.
   Ancorar por `right` (e não por `left`) mantém o espelho exato em `--spx` dobrado (`.slot-2x`, weaponEffects).
   A arte NÃO foi refeita: a seta aponta para cima (simétrica) e a luz vem de cima-esquerda como em toda a UI; só
   `META.slotCorner` do script mudou para [22, 1], que é onde o preview compõe a seta sobre ícones reais.
   O brilho (`ico_upgrade_lit`) acende ~15 % de um ciclo de 2,4 s; a seta nasce com a grade e só troca `is-on`
   (visibility), então todas pulsam juntas. Movimento reduzido: só a base.
   Prova da posição: client/tests/ui/slotUpgradeCorner.test.js. */
.slot-upgrade {
  position: absolute;
  right: calc(-1 * var(--spx, var(--px)));
  top: calc(-1 * var(--spx, var(--px)));
  width: calc(9 * var(--spx, var(--px)));
  height: calc(10 * var(--spx, var(--px)));
  background: var(--ui-ico_upgrade, none) no-repeat 0 0 / 100% 100%;
  image-rendering: pixelated;
  pointer-events: none;
  visibility: hidden;
}
.slot-upgrade::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--ui-ico_upgrade_lit, none) no-repeat 0 0 / 100% 100%;
  image-rendering: pixelated;
  opacity: 0;
  animation: slot-upgrade-lit 2.4s step-end infinite;
}
.slot-upgrade.is-on { visibility: visible; }
.slot.is-dragging > .slot-upgrade { opacity: 0.4; }
@keyframes slot-upgrade-lit {
  0%   { opacity: 0; }
  85%  { opacity: 1; }
  100% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .slot-upgrade::after { animation: none; }
}
/* --- auto-consumable --- Selo "Usar automaticamente" (architecture.md §6.7 F): ui/icons/ico_auto.png 8×8 — a MESMA peça da
   poção automática da barra rápida e do AUTO das habilidades (uma figura, uma ideia: "o jogo usa sozinho") — no canto
   SUPERIOR DIREITO do ícone, px (24,3)…(31,10) do slot 36. O canto é o da seta de upgrade, que só acende em equipamento;
   o selo só acende em consumível de efeito por tempo (só ele aceita auto-uso), então nunca disputam. `--spx` como a seta. */
.slot-auto {
  position: absolute;
  right: calc(-1 * var(--spx, var(--px)));
  top: calc(-1 * var(--spx, var(--px)));
  width: calc(8 * var(--spx, var(--px)));
  height: calc(8 * var(--spx, var(--px)));
  background: var(--ui-ico_auto, none) no-repeat 0 0 / 100% 100%;
  image-rendering: pixelated;
  pointer-events: none;
  visibility: hidden;
}
.slot-auto.is-on { visibility: visible; }
.slot.is-dragging > .slot-auto { opacity: 0.4; }
/* --- durability --- Marca de durabilidade (docs/specs/durability.md D15; arte art/src/ui/durability_marks.py): 9×9 no canto
   INFERIOR ESQUERDO da caixa do ícone — px (3,24)…(12,33) do slot 36 (exclusivos). Por que este canto: o inferior DIREITO é da pilha
   (.slot-qty) e do `+N` da Refinaria (.slot-refine, texto — temper D26), o superior esquerdo é do tier / da marca de efeito de arma, o
   superior direito é da seta de upgrade. O único vizinho do inferior esquerdo é o selo de tier `.is-low` (arma com efeito): quando a marca
   acende, ele sobe 9 px e fica em cima dela — (3,18)…(18,24) — sem tapar a marca de efeito (2,2)…(13,13). `--spx` como a seta (dobra no .slot-2x).
   `data-dur`: ok = nada · worn = âmbar · broken = vermelho. Prova da posição: client/tests/ui/slotDurCorner.test.js. */
.slot-dur {
  position: absolute;
  left: calc(-1 * var(--spx, var(--px)));
  bottom: calc(-1 * var(--spx, var(--px)));
  width: calc(9 * var(--spx, var(--px)));
  height: calc(9 * var(--spx, var(--px)));
  background: none no-repeat 0 0 / 100% 100%;
  image-rendering: pixelated;
  pointer-events: none;
  visibility: hidden;
}
.slot-dur[data-dur='worn'] { visibility: visible; background-image: var(--ui-ico_dur_worn, none); }
.slot-dur[data-dur='broken'] { visibility: visible; background-image: var(--ui-ico_dur_broken, none); }
/* Quebrado: o ícone apaga (não some — o jogador tem de reconhecer o item), e a marca vermelha diz o porquê. */
.slot.is-broken > .item-icon { filter: grayscale(0.85) brightness(0.6); }
.slot.is-dragging > .slot-dur { opacity: 0.4; }
/* o selo de tier deslocado (arma com efeito) sobe para cima da marca: bottom −1 + 9 = 8 → (3,18)…(18,24) */
.slot.is-worn > .slot-tier.is-low, .slot.is-broken > .slot-tier.is-low { bottom: calc(8 * var(--spx, var(--px))); }
/* --- /durability --- */

/* ---- abas: ui/tabs/tab.<estado>.png (slice 4, 12×12; normal/hover/active) — até existir, btn_secondary/primary -- */
.tab {
  display: inline-flex;
  align-items: center;
  min-height: calc(14 * var(--px));
  padding: 0 calc(4 * var(--px));
  border: calc(4 * var(--px)) solid transparent;
  border-image: var(--ui-tab-normal, url(../assets/ui/buttons/btn_secondary.normal.png)) 4 fill / calc(4 * var(--px)) stretch;
  image-rendering: pixelated;
  background: none;
  color: var(--c-text-dim);
  cursor: pointer;
  line-height: 1;
}
/* C26 (análise crítica de 2026-09-22, `B48-settings.png`): a aba sob o mouse trocava a moldura por
   `btn_secondary.hover.png` — clara como a `btn_primary.normal.png` da ATIVA. Com "Som" ativa e "Tela" sob o mouse a
   tela mostrava DUAS abas acesas e só a cor do texto as separava. O hover deixa de mexer na moldura: ele acende o
   texto e marca um traço de 1 px na base (a aba ativa é a única com moldura clara + texto dourado). A aba ATIVA sob o
   mouse não muda de nada — ela já está escolhida. `--ui-tab-hover` continua honrado para quando a arte própria de aba
   existir (aí ela é discreta por desenho). */
.tab:not(.is-active):hover,
.tab:not(.is-active):focus-visible {
  border-image-source: var(--ui-tab-hover, url(../assets/ui/buttons/btn_secondary.normal.png));
  color: var(--c-text);
  box-shadow: inset 0 calc(-1 * var(--px)) 0 0 var(--c-text-dim);
}
/* tab.active = miolo escuro com aro dourado → texto dourado (com a arte provisória btn_primary, clara, o texto escuro
   sumia na arte definitiva). */
.tab.is-active {
  border-image-source: var(--ui-tab-active, url(../assets/ui/buttons/btn_primary.normal.png));
  color: var(--c-gold);
}

/* ---- tooltip / menu de contexto / cartão de loot: ui/windows/tooltip.png (slice 4, 12×12, quase preto com aro
   claro de 1 px) — até existir, slot_empty (mesma slice, fundo escuro). */
.tooltip-frame {
  border: calc(4 * var(--px)) solid transparent;
  border-image: var(--ui-tooltip, url(../assets/ui/slots/slot_empty.png)) 4 fill / calc(4 * var(--px)) stretch;
  image-rendering: pixelated;
  color: var(--c-text);
}

/* ---- toggle (AUTO, "Só minha classe"): ui/toggles/toggle.<off|on>.png, 16×10 fixo (sem 9-slice) — até existir,
   pílula em CSS com o mesmo tamanho. `role="switch"` + aria-checked decide o estado. */
.toggle {
  display: inline-block;
  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;
  border: 0;
  padding: 0;
  cursor: pointer;
  vertical-align: middle;
}
.toggle[aria-checked="true"] {
  background-image: var(--ui-toggle-on, linear-gradient(90deg, #2a2118 0 60%, #e0b040 60% 100%));
}
.toggle:disabled, .toggle[aria-disabled="true"] { opacity: 0.5; cursor: default; }

/* ---- barras: moldura ui/bars/bar_frame.png (slice 3, 9×8) + preenchimento ui/bars/bar_fill_<tipo>.png (4×4) --- */
.bar {
  position: relative;
  height: calc(8 * var(--px));
  border: calc(3 * var(--px)) solid transparent;
  border-image: url(../assets/ui/bars/bar_frame.png) 3 fill / calc(3 * var(--px)) stretch;
  image-rendering: pixelated;
}
/* A calha começa 2 px de arte para dentro da moldura (anel de contorno + anel claro) e a borda tem 3: o preenchimento
   ocupa a calha INTEIRA (1 px para fora da caixa de conteúdo em cada lado) e `--value` recorta pela direita. Com
   `width: var(--value)` a porcentagem era da caixa de conteúdo (2 px mais estreita que a calha) e 100 % parava antes do fim. */
.bar-fill {
  position: absolute;
  top: calc(-1 * var(--px));
  bottom: calc(-1 * var(--px));
  left: calc(-1 * var(--px));
  right: calc(-1 * var(--px));
  clip-path: inset(0 calc(100% - var(--value, 0%)) 0 0);
  background: url(../assets/ui/bars/bar_fill_hp.png) repeat-x left top;
  background-size: calc(4 * var(--px)) calc(4 * var(--px));
  image-rendering: pixelated;
  transition: clip-path 0.15s linear;
}
.bar-mp  .bar-fill { background-image: url(../assets/ui/bars/bar_fill_mp.png); }
.bar-exp .bar-fill { background-image: url(../assets/ui/bars/bar_fill_exp.png); }
.bar-cast .bar-fill { background-image: url(../assets/ui/bars/bar_fill_cast.png); }

.bar-label {
  position: absolute;
  inset: calc(-3 * var(--px));
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-1);
  line-height: 1;
  color: var(--c-text);
  /* Contorno nos 4 lados, não só a sombra do canto: o número é escrito POR DENTRO da barra e a borda do preenchimento
     passa no meio dos dígitos — "1/50" no refino e "198/436" nas Profissões ficavam ilegíveis quando o preenchimento
     parava no meio do texto (B22 de volta; avaliação de UX, rodada 2). Mesmo contorno do `--hud-outline` de hud.css. */
  text-shadow: var(--px) 0 0 #000, calc(-1 * var(--px)) 0 0 #000, 0 var(--px) 0 #000, 0 calc(-1 * var(--px)) 0 #000, var(--px) var(--px) 0 #000;
  pointer-events: none;
}
/* Piscar (deny por falta de mana — ui-gameplay.md §5.2): 300 ms em degraus. */
.bar.is-flash .bar-fill { animation: bar-flash 0.3s steps(3); }
@keyframes bar-flash {
  from { filter: brightness(2.2); }
  to   { filter: brightness(1); }
}
