/* base.css — reset, tokens, fonte e o palco (canvas + #ui-root). architecture.md §3.
   Unidade de arte: 1 pixel de arte = var(--px) px CSS (= k pixels inteiros de dispositivo). O renderer publica
   --px e --ui-scale em <html>; os valores abaixo são só o padrão até ele assumir.
   Cores de qualidade (.q0, .q1, …) NÃO ficam aqui: o js/main.js gera um <style> a partir de config/qualities.json. */

:root {
  --px: 2px;
  --ui-scale: 2;

  --font-ui: 'Courier New', monospace;
  --text-1: calc(8 * var(--px));   /* corpo */
  --text-2: calc(16 * var(--px));  /* título */

  --c-bg: #000;
  --c-text: #f4f0e6;
  --c-text-dim: #b0a898;
  --c-text-dark: #2a2118;
  --c-gold: #e0b040;
  --c-danger: #d9483b;
  --c-ok: #4caf50;
  --c-shade: rgba(0, 0, 0, 0.55);
  /* --- focus --- o Foco (docs/specs/focus.md §8): o tom 3 do `ice` da paleta mestra (o vidro da ampulheta `ico_focus`) — a tira da
     carteira perto do tanque e o piscar da barra que o Foco concluiu */
  --c-focus: #a0e0f8;

  /* Campos de formulário (C19): as cores vêm de `client/public/assets/ui/windows/panel_inset.png` — a moldura é a
     própria arte (border-image), estes três são só o miolo, o contorno e o fundo da lista do <select>. */
  --c-field-bg: #0c0c12;
  --c-field-edge: #1a1423;
  --c-field-pop: #12101c;
  /* o miolo do painel de janela (`client/public/assets/ui/windows/panel.png`): o fundo de quem passa por cima de outra legenda no painel */
  --c-panel-fill: #221f33;

  /* A forja da Refinaria (css/temper.css; docs/specs/temper.md §11.1): tons da paleta mestra (art/palette/master.json) — a pedra
     do nicho (`stone` 0), a brasa do fio (`gold` 1), o violeta da corrupção (`arcane` 2) e o branco-ouro do Radiante (`holy` 3). */
  --c-forge-bg: #1e1e2a;
  --c-forge-rim: #a05a1a;
  --c-void: #8a3ae0;
  --c-radiant: #fff8c8;
}

/* Fonte pixel da UI (OFL) — auto-hospedada pela arte em assets/fonts/ui.woff2. O PHP põe `has-ui-font` no <html> só
   quando o arquivo existe (Boot::htmlClass): família declarada mas não usada não é baixada, logo sem o arquivo não há 404. */
@font-face {
  font-family: 'Pixelify Sans';
  src: url(../assets/fonts/ui.woff2) format('woff2');
  font-weight: 400 700;
  font-display: swap;
}

html.has-ui-font { --font-ui: 'Pixelify Sans', 'Courier New', monospace; }

*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  background: var(--c-bg);
  color: var(--c-text);
  font-family: var(--font-ui);
  font-size: var(--text-1);
  line-height: 1.25;
  overflow: hidden;
  -webkit-text-size-adjust: none;
  /* Gestos NATIVOS de página desligados no palco inteiro (decisão do dono 2026-10-05; ui-gameplay.md §8.7): isto é um jogo,
     não uma página. Sem seleção de texto (e sem a lupa do iPhone), sem o menu do toque longo (copiar · compartilhar ·
     pesquisar) e sem o realce azul do toque — nem no texto do chat. Herdam para todo o palco; o Safari do iOS só lê a
     seleção com o prefixo. A ÚNICA exceção são os campos de digitar (logo abaixo), e a outra metade da regra é a guarda
     do `contextmenu`/`selectstart`/`dragstart` (`js/ui/nativeGestures.js`). */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}

/* Os campos de digitar devolvem o que o palco tirou: selecionar, copiar e colar — e, no iOS, o menu do toque longo deles
   (o "Colar"). Sem o prefixo o iOS herda o `none` de cima e o campo nem aceita seleção. A guarda do JS
   (`nativeGestures.js › isTextField`) recorta mais fino: a caixa de marcar e a barra deslizante não têm texto, e nelas o
   menu nativo também é cancelado. */
input,
textarea,
[contenteditable]:not([contenteditable='false']) {
  -webkit-user-select: text;
  user-select: text;
  -webkit-touch-callout: default;
}

button, input, select, textarea {
  font: inherit;
  color: inherit;
}

img, canvas { display: block; }
/* a imagem não sai arrastada pelo navegador (o arrasto de item do jogo é outra coisa: `widgets/dragdrop.js`) */
img { -webkit-user-drag: none; }

/* Palco: letterbox preto com canvas e UI na mesma célula (centro), sem transform (borra texto). */
#stage {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--c-bg);
  overflow: hidden;
}

#game,
#ui-root {
  grid-area: 1 / 1;
  width: calc(640 * var(--px));
  height: calc(360 * var(--px));
}

#game {
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  background: #000;
}

#ui-root {
  position: relative;
  pointer-events: none;   /* só os filhos capturam; o canvas continua recebendo o pointer */
  overflow: hidden;
}

#ui-root > * { pointer-events: auto; }

.hidden { display: none !important; }

/* --- loading --- A TELA DE CARREGAMENTO e o VÉU DO PALCO (`#loading`, architecture.md §2.1; geometria em ui-gameplay.md
   §0.1; texto no caso 42 de docs/ux-casos.md). Pintada pelo PHP antes de qualquer JS: 1º filho do <body>, FORA do #stage, sobre
   o viewport inteiro — centrada sem JS (o #stage ainda não tem tamanho quando ela nasce; a caixa antiga de 640×360 na
   célula do grid nascia 1280×720 com o `--px` padrão e cortava o título no canto do celular). Fundo #000: a cor do
   letterbox e das cascas desktop e macOS (sem emenda ao abrir a janela). z 1000: acima de modal (100), toast (110) e dica (120).
   Escala própria `--boot-px`: o fator inteiro do renderer SEM o teto das Configurações. Antes do JS, o padrão por media
   query; depois, `engine/boot/loadingScreen.js` escreve o valor medido (`scaleMath.computeLayout`) no próprio #loading.
   Dentro dela `--px` = `--boot-px`, para a `.bar` e o `.btn` da série (nine-slice.css) escalarem junto, e TODA medida é
   `calc(N * var(--px))` resolvida aqui dentro — nunca `var(--text-1)`/`var(--text-2)`, que chegam resolvidos do :root. */
.loading {
  --boot-px: 1px;
  --px: var(--boot-px);
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  place-items: center;
  background: #000;
  color: var(--c-text);
  opacity: 1;
  transition: opacity 0.15s steps(3);
  /* fora do teste de acerto (só o botão pega clique): o retrato fora de janela mede a cobertura por `elementFromPoint`
     (`ui/widgets/portrait.js › covered`) — com a tela pegando o ponto, todo rosto da seleção nascia "coberto", a cena não o
     esperava e a tela saía com a inicial no lugar do rosto. Com o #ui-root `inert` por baixo, o ponto atravessa até o canvas. */
  pointer-events: none;
}
@media (min-width: 1280px) and (min-height: 720px) { .loading { --boot-px: 2px; } }
@media (min-width: 1920px) and (min-height: 1080px) { .loading { --boot-px: 3px; } }
/* saída: some em 3 degraus em 150 ms (LOADING.fadeMs); durante o fade não pega clique, o jogo atrás já responde */
.loading.is-leaving { opacity: 0; pointer-events: none; }
.loading.is-hidden { display: none !important; }

/* a caixa: 240 px de arte — o logo (o letreiro 128×54 da marca), título 16 (só no véu, com o destino), barra 200×8, passo 8,
   selo 8, botão 20, vãos de 4 */
.loading-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(4 * var(--px));
  width: calc(240 * var(--px));
  font-size: calc(8 * var(--px));
  line-height: 1;
  text-align: center;
}
/* o logo (art-pipeline.md §3.9): o PHP escreve o tamanho do PNG em `--art-w`/`--art-h` e a tela o amplia pelo fator
   inteiro dela — nunca esticado; 4 px de ar até a barra */
.loading-logo {
  width: calc(var(--art-w, 128) * var(--px));
  height: calc(var(--art-h, 54) * var(--px));
  margin-bottom: calc(4 * var(--px));
  image-rendering: pixelated;
  image-rendering: crisp-edges;
}
.loading-title {
  max-width: 100%;
  font-size: calc(16 * var(--px));
  line-height: calc(16 * var(--px));
  color: var(--c-gold);
  text-shadow: var(--px) var(--px) 0 #2a2118;
  overflow-wrap: anywhere;
}
/* a barra é a `.bar.bar-exp` da série (bar_frame + bar_fill_exp): só a largura é dela. `.loading .x` e não `.x`: a
   nine-slice.css vem DEPOIS desta folha e a `.bar`/`.btn` dela venceria o empate */
.loading .loading-bar { width: calc(200 * var(--px)); flex: none; }
.loading-step {
  min-height: calc(8 * var(--px));
  font-size: calc(8 * var(--px));
  line-height: calc(8 * var(--px));
  color: var(--c-text-dim);
}
/* o selo de estado (G2): moldura de 1 px + duas palavras, apagado — 'Conexão lenta', 'Download parado', 'Falha ao abrir',
   'Corrida em andamento' */
.loading-seal {
  padding: calc(1 * var(--px)) calc(3 * var(--px));
  border: var(--px) solid currentColor;
  font-size: calc(8 * var(--px));
  line-height: calc(8 * var(--px));
  color: var(--c-text-dim);
  white-space: nowrap;
}
.loading-seal[data-seal='stalled'],
.loading-seal[data-seal='failed'] { color: var(--c-danger); }
/* o botão da série (btn_primary) ao lado do selo que o motiva (N3): 20 de altura */
.loading .loading-action {
  min-height: calc(20 * var(--px));
  padding: 0 calc(6 * var(--px));
  font-size: calc(8 * var(--px));
  pointer-events: auto;
}
/* durante o fade (e escondida) nem o botão pega clique */
.loading.is-leaving .loading-action { pointer-events: none; }
/* --- pré-alpha --- "Pré-Alpha · <versão>" no canto de baixo à direita da tela e do véu (docs/specs/ui-gameplay.md §0.5): o véu (z 1000)
   cobre a etiqueta do jogo, e é aqui que aparece o "Tentar de novo" de quem vai relatar uma falha. Presa à janela, fora da caixa */
.loading-build {
  position: fixed;
  right: max(4px, env(safe-area-inset-right, 0px));
  bottom: 0;
  font-size: max(8px, calc(3.5 * var(--px)));
  line-height: 1;
  white-space: nowrap;
  color: var(--c-text);
  opacity: 0.8;
}
.loading-build:empty { display: none; }

/* movimento reduzido: a barra salta, nada brilha, a saída é instantânea (o JS nem põe o `is-leaving`) */
@media (prefers-reduced-motion: reduce) {
  .loading,
  .loading *,
  .loading *::before,
  .loading *::after {
    transition: none !important;
    animation: none !important;
  }
}

.noscript {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--c-bg);
  color: var(--c-text);
}

/* Arte pixel em <img>/background sempre sem filtro. */
.pixel, .item-icon, [class*="slot"], [class*="btn"], [class*="panel"], [class*="bar"] {
  image-rendering: pixelated;
}

/* Foco visível sem borda arredondada do navegador. */
:focus-visible { outline: var(--px) solid var(--c-gold); outline-offset: 0; }

.text-dim { color: var(--c-text-dim); }
.text-gold { color: var(--c-gold); }
.text-danger { color: var(--c-danger); }
.text-center { text-align: center; }

/* ================================================================================================================
   `.scroll-fade` — a metade "esta caixa ROLA" do mecanismo único de "o conteúdo não cabe" (UX 2026-09-23 §7.1:
   B74, B79, B87, B90, B91, B97, B107, B167, B180).

   A outra metade — o degradê que AVISA que a lista continua, e o respiro do fim — mora em `css/scroll-fade.css`, que
   `index.php` carrega por último e que tem `.scroll-fade` na sua lista. **Não duplique a máscara aqui**: foi copiá-la
   à mão para 9 folhas que produziu o B74, com as três listas mais longas do jogo de fora.

   O que ESTA metade garante, e que a máscara sozinha não dá: a caixa realmente rolar. Metade das 14 do relatório já
   desbotava ou já tinha barra e mesmo assim escondia conteúdo, porque faltava `overflow-y` (`.tw-advice-text` era
   `overflow: hidden` com 40 px de teto contra 60 de conteúdo, B87) ou faltava `min-height: 0` — sem ele um filho de
   `display: flex` cresce até o tamanho do conteúdo e NUNCA rola, que é o modo mais comum de a caixa "sumir" com o
   fim da lista. E a barra em pixel, que `scroll-fade.css` só dá a cinco seletores nomeados, aqui vale para toda caixa
   que use a classe.

   Marcar a caixa é uma linha no JS que a cria (`class: 'x scroll-fade'`), não uma regra nova de CSS por janela. */
.scroll-fade {
  overflow-y: auto;
  overflow-x: hidden;
  min-height: 0;
  scrollbar-width: thin;
  scrollbar-color: #575273 #12101c;
}

/* Barra em pixel (Chromium/WebKit), no mesmo tom do resto da UI; Firefox fica no `scrollbar-width: thin` acima.
   Neste Chromium ela é OVERLAY (não muda `clientWidth`) — por isso o degradê de `scroll-fade.css` é o aviso de
   verdade, e nenhuma lista encolhe por causa desta regra. */
@supports selector(::-webkit-scrollbar) {
  .scroll-fade { scrollbar-width: auto; scrollbar-color: auto; }
  .scroll-fade::-webkit-scrollbar { width: calc(6 * var(--px)); }
  .scroll-fade::-webkit-scrollbar-track { background: #12101c; }
  .scroll-fade::-webkit-scrollbar-thumb { background: #575273; border: var(--px) solid #12101c; }
  .scroll-fade::-webkit-scrollbar-thumb:hover { background: #8a84a8; }
}

/* ---- campo de texto: UM `.input` para o jogo inteiro (B188 e B108) -----------------------------------------------
   Mora aqui, e não mais em `auth.css`, porque a folha do login não é carregada como "a folha de base" por ninguém: o
   autor de uma janela nova olhava a vizinha, não achava campo nenhum em `base.css`, e escrevia o dele. Foi assim que
   nasceram `.friends-input` (#12101c/#575273), `.clan-input` (cópia literal do `.input`) e `.lo-name` — este último
   sem CSS NENHUM, com o cinza padrão do Chromium (`border: 2px inset`) contra pixel art em 19 janelas.
   Alturas medidas antes: 24 / 26 / 28 / 32 px na mesma resolução.
   As folhas de janela podem mudar LARGURA e FLEX do campo (é layout); cor, borda, altura e placeholder são daqui. */
.input {
  width: 100%;
  min-width: 0;
  min-height: calc(12 * var(--px));
  padding: calc(1 * var(--px)) calc(2 * var(--px));
  background: #0c0c12;
  border: var(--px) solid #3a3a48;
  border-radius: 0;
  color: var(--c-text);
  font: inherit;
  font-size: var(--text-1);
  outline: none;
}
.input:focus { border-color: var(--c-gold); }
.input:focus-visible { outline: var(--px) solid var(--c-gold); outline-offset: 0; }
.input.is-invalid { border-color: var(--c-danger); }
.input:disabled { color: var(--c-text-dim); opacity: 0.6; }
/* campo travado COM MOTIVO (só-leitura + `aria-disabled` + dica): mesmo desenho do `:disabled`, mas recebe o ponteiro
   e o foco que abrem o balão com o motivo (`widgets/hint.js`) */
.input[aria-disabled='true'] { color: var(--c-text-dim); opacity: 0.6; cursor: not-allowed; }
/* dica do campo ('3–16 letras, números ou _', 'mínimo 8 caracteres'): #5a5a6a sobre #0c0c12 sumia (contraste ~2,6:1).
   #8a8478 (o cinza das dicas do HUD, ~5:1) lê, e continua mais apagado que o texto digitado. */
.input::placeholder { color: #8a8478; opacity: 1; }
/* `<textarea class="input">`: o resto vem da regra acima; só a rolagem e a entrelinha são próprias. */
textarea.input { resize: none; line-height: calc(9 * var(--px)); }

/* ---- controles em pixel (C19 da análise crítica de 2026-09-22) ------------------------------------------------
   `select`, `input[type=number]`, `input[type=range]` e `input[type=checkbox]` chegavam com a aparência DO SISTEMA
   (caixa clara, seta cinza do Chromium, spinner, trilho azul) no meio de uma UI inteiramente em pixel art — Leilão,
   Refino, Livro de receitas, Clã e Replay, e também Forja, Configurações e Exploração avançada.

   O conserto é só de aparência: a moldura é a arte de verdade (`ui/windows/panel_inset.png`, as 2 px de FORA do
   nine-slice, fatiadas 1:1 com `--px` — nada de redimensionar pixel art por fator quebrado) e a seta do `select` é
   desenhada em pixel com quatro barras de 1 px (7·5·3·1), sem asset novo e escalando junto com `--px`.
   A SEMÂNTICA não muda: continuam sendo `select` e `input` nativos, então teclado (setas, Home/End, digitar para
   buscar a opção), `aria-label`, `:checked`, `:disabled` e leitor de tela seguem funcionando — nenhum `div` com
   `onclick`. Por isso nenhuma regra aqui zera o `outline`: o `:focus-visible` dourado lá de cima continua valendo,
   e as regras de foco abaixo só o repetem com a especificidade destes seletores.

   ESCOPO: tudo sob `#ui-root` (o palco do jogo, onde `#ui-screens` e `#ui-overlay` vivem — `ui/ui.js:200-203`).
   Fora dele — o painel do `sandbox.php`, por exemplo — o navegador segue nativo de propósito.
   `--ui-panel_inset` é publicada por `js/main.js:194-205` a partir de `manifest.ui`; a URL literal é o MESMO arquivo
   e só serve de reserva enquanto o manifesto não carregou.

   A lista aberta do `<select>` é desenhada pelo sistema operacional e não é estilizável além de `option` — é o
   limite da abordagem "sem trocar a semântica", e vale a troca. */

#ui-root select,
#ui-root input[type="number"],
#ui-root input[type="range"],
#ui-root input[type="checkbox"] {
  -webkit-appearance: none;
  appearance: none;
  border-radius: 0;
  font: inherit;
  color: var(--c-text);
  image-rendering: pixelated;
}

/* caixa rebaixada: `select` e `number` (14 de altura = 2 de moldura + 8 de texto + 2 de moldura) */
#ui-root select,
#ui-root input[type="number"] {
  min-height: calc(14 * var(--px));
  padding: 0 calc(3 * var(--px));
  border: calc(2 * var(--px)) solid transparent;
  border-image: var(--ui-panel_inset, url(../assets/ui/windows/panel_inset.png)) 2 / calc(2 * var(--px)) stretch;
  background-color: var(--c-field-bg);
  line-height: calc(10 * var(--px));
}

#ui-root input[type="number"] {
  -moz-appearance: textfield;
}
/* os spinnerzinhos do Chromium são a parte mais "sistema" do campo; as setas do TECLADO continuam valendo */
#ui-root input[type="number"]::-webkit-outer-spin-button,
#ui-root input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* Altura FIXA no `select` para a seta cair sempre nas mesmas linhas: a caixa de conteúdo tem 10 px e a seta ocupa
   as linhas 3–6, ou seja, 3 px de folga em cima e embaixo. `background-origin` é `padding-box` (o padrão). */
#ui-root select {
  height: calc(14 * var(--px));
  padding-right: calc(12 * var(--px));
  background-repeat: no-repeat;
  background-image:
    linear-gradient(var(--c-text-dim), var(--c-text-dim)),
    linear-gradient(var(--c-text-dim), var(--c-text-dim)),
    linear-gradient(var(--c-text-dim), var(--c-text-dim)),
    linear-gradient(var(--c-text-dim), var(--c-text-dim));
  background-size:
    calc(7 * var(--px)) var(--px),
    calc(5 * var(--px)) var(--px),
    calc(3 * var(--px)) var(--px),
    var(--px) var(--px);
  background-position:
    right calc(2 * var(--px)) top calc(3 * var(--px)),
    right calc(3 * var(--px)) top calc(4 * var(--px)),
    right calc(4 * var(--px)) top calc(5 * var(--px)),
    right calc(5 * var(--px)) top calc(6 * var(--px));
  cursor: pointer;
}
/* a seta acende ao passar o mouse e com foco — o mesmo ouro do `:focus-visible` */
#ui-root select:hover:not(:disabled),
#ui-root select:focus {
  background-image:
    linear-gradient(var(--c-gold), var(--c-gold)),
    linear-gradient(var(--c-gold), var(--c-gold)),
    linear-gradient(var(--c-gold), var(--c-gold)),
    linear-gradient(var(--c-gold), var(--c-gold));
}
/* a lista aberta: o que o sistema aceita pintar */
#ui-root select option { background-color: var(--c-field-pop); color: var(--c-text); }
#ui-root select option:disabled { color: var(--c-text-dim); }

#ui-root select:disabled,
#ui-root input[type="number"]:disabled,
#ui-root input[type="checkbox"]:disabled,
#ui-root input[type="range"]:disabled {
  color: var(--c-text-dim);
  opacity: 0.6;
  cursor: default;
}

/* caixa de marcar: 10 × 10 com a mesma moldura; marcada = bloco de ouro de 4 × 4 no centro dos 6 px do miolo */
#ui-root input[type="checkbox"] {
  flex: none;
  width: calc(10 * var(--px));
  height: calc(10 * var(--px));
  margin: 0;
  border: calc(2 * var(--px)) solid transparent;
  border-image: var(--ui-panel_inset, url(../assets/ui/windows/panel_inset.png)) 2 / calc(2 * var(--px)) stretch;
  background-color: var(--c-field-bg);
  cursor: pointer;
}
#ui-root input[type="checkbox"]:checked {
  background-image: linear-gradient(var(--c-gold), var(--c-gold));
  background-repeat: no-repeat;
  background-size: calc(4 * var(--px)) calc(4 * var(--px));
  background-position: center;
}

/* barra deslizante (volume das Configurações, linha do tempo do Replay): trilho rebaixado de 6 e punho de ouro
   6 × 10. Os pseudoelementos de trilho/punho são de motor: cada um na SUA regra, senão o navegador que não conhece
   um deles descarta a regra inteira. */
#ui-root input[type="range"] {
  height: calc(10 * var(--px));
  background: none;
  cursor: pointer;
}
#ui-root input[type="range"]::-webkit-slider-runnable-track {
  height: calc(6 * var(--px));
  border: calc(2 * var(--px)) solid transparent;
  border-image: var(--ui-panel_inset, url(../assets/ui/windows/panel_inset.png)) 2 / calc(2 * var(--px)) stretch;
  background-color: var(--c-field-bg);
}
#ui-root input[type="range"]::-moz-range-track {
  height: calc(6 * var(--px));
  border: calc(2 * var(--px)) solid transparent;
  border-image: var(--ui-panel_inset, url(../assets/ui/windows/panel_inset.png)) 2 / calc(2 * var(--px)) stretch;
  background-color: var(--c-field-bg);
}
#ui-root input[type="range"]::-moz-range-progress {
  height: calc(2 * var(--px));
  background-color: var(--c-gold);
}
/* o punho sobe 4 px sobre a caixa de conteúdo do trilho (2 de altura) para ficar centrado nos 6 px da moldura */
#ui-root input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: calc(6 * var(--px));
  height: calc(10 * var(--px));
  margin-top: calc(-4 * var(--px));
  border: var(--px) solid var(--c-field-edge);
  border-radius: 0;
  background-color: var(--c-gold);
}
#ui-root input[type="range"]::-moz-range-thumb {
  width: calc(6 * var(--px));
  height: calc(10 * var(--px));
  border: var(--px) solid var(--c-field-edge);
  border-radius: 0;
  background-color: var(--c-gold);
}

/* Foco: o mesmo aro de `:focus-visible` lá de cima, repetido com a especificidade destes seletores para que
   estilizar o controle NUNCA custe a marca de foco de quem navega por teclado. */
#ui-root select:focus-visible,
#ui-root input[type="number"]:focus-visible,
#ui-root input[type="range"]:focus-visible,
#ui-root input[type="checkbox"]:focus-visible {
  outline: var(--px) solid var(--c-gold);
  outline-offset: 0;
}

/* --- B143: camada de DICA (`ui/widgets/hint.js`) -----------------------------------------------------------------
   `title=` é tooltip de MOUSE: em toque não existe e por teclado não existe. Metade da explicação do jogo (o motivo
   de um botão travado, a fatia do medidor, a descrição do ofício, o grau trancado) vivia só ali. O balão abaixo é o
   mesmo texto, mostrado quando o FOCO chega ao elemento e quando um dedo o toca. Nunca captura o ponteiro: ele não
   pode roubar o clique do botão que está explicando. */
#ui-root #ui-hints { z-index: 120; }
#ui-root .hint-bubble {
  position: absolute;
  max-width: calc(180 * var(--px));
  padding: calc(2 * var(--px)) calc(3 * var(--px));
  border: var(--px) solid var(--c-gold);
  background: var(--c-field-bg);
  color: var(--c-text);
  font-size: var(--text-1);
  line-height: 1.3;
  pointer-events: none;
}

/* Só para leitor de tela: fica no fluxo (senão o `aria-live` não é anunciado), mas não ocupa pixel. */
#ui-root .sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Botão travado COM MOTIVO (`widgets/button.js › setButtonReason`): `aria-disabled` em vez de `disabled`, para o
   motivo chegar ao teclado e ao toque. O cursor diz que não adianta clicar; o desenho cinza já vem de
   `.btn.is-disabled` em nine-slice.css. */
#ui-root .btn[aria-disabled='true'] { cursor: not-allowed; }

/* ---- "há mais aqui": a PISTA do gesto de revelar (G4 de `docs/UX-GRAMATICA.md`) ---------------------------------
   Informação escondida sem pista é informação perdida. Todo elemento cujo texto por extenso foi para o gesto
   (`widgets/hint.js` → `data-hint` + `title` + `aria-live`) leva `.has-more`, e o pontilhado de 1 px de arte é o que
   diz ao olho que há mais ali. Nasceu em `mob-sprite.css` com o rework do chefe; está aqui porque vale para o jogo
   inteiro — número com ícone, selo de estado, rótulo que saiu da tela. */
#ui-root [data-hint].has-more:not(.btn) { border-bottom: var(--px) dotted rgba(244, 240, 230, 0.4); cursor: help; }
/* BOTÃO 9-slice: o `border-bottom` acima cai debaixo da moldura de `border-image` e NÃO aparece — a pista virava uma
   promessa invisível, que é o pior resultado possível do enxugamento (a §12.1 de `docs/UX-GRAMATICA.md` achou 18
   delas por outro caminho). O pontilhado vai para o RÓTULO, que é texto comum; o `data-hint` continua no botão, e é
   nele que o foco e o toque encontram a frase (`hintOf` sobe até 3 níveis). Visto na tela em 2026-09-26, na janela
   de Amigos: "Bloquear" com a explicação no gesto e nenhuma marca de que ela existia.
   E a regra de cima NÃO alcança o botão (`:not(.btn)`): o `border-bottom: 0` que a desfazia aqui zerava também o aro de
   4 px do `.btn` (nine-slice.css) — a moldura continuava desenhada (a `border-image` tem largura própria), mas o miolo
   ganhava 4 px embaixo, e ícone e rótulo do botão COM pista desciam 2 px de arte em relação aos vizinhos, com o ícone de
   16 saindo pela base: o "Guardar" descentralizado do Armazém (pedido do dono, 2026-10-01). A margem −1 do rótulo
   devolve o pixel do pontilhado: com ou sem pista o botão mede o mesmo e o texto fica na mesma linha do vizinho. */
#ui-root .btn[data-hint].has-more { cursor: pointer; }
#ui-root .btn[data-hint].has-more > span:last-child { border-bottom: var(--px) dotted rgba(244, 240, 230, 0.4); margin-bottom: calc(-1 * var(--px)); }
