/* =============================================================
   Deck Sandbox — Layout da Mesa
   Camada 2: esqueleto (topbar / trilhos / battlefield / mão).
   ============================================================= */

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

html, body {
  height: 100%;
  margin: 0;
}

body {
  background: var(--bg-app);
  color: var(--fg);
  /* A LETRA saiu daqui e virou token em 2026-09-13 (ADR-0239 D2): esta
     regra era a única fonte da tipografia do projeto, e o site não carrega
     esta folha. O valor computado é o mesmo — o que mudou é onde ele mora. */
  font: var(--font-ui-size)/var(--font-ui-line) var(--font-ui);
  overflow: hidden;              /* a mesa nunca rola; cada zona rola por si */
  -webkit-font-smoothing: antialiased;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

/* -------------------------------------------------------------
   Grade mestra
   linhas:  topbar | mesa | mão
   colunas: mini-mapas (0px na Fase 1) | battlefield | zonas
   ------------------------------------------------------------- */
.app {
  display: grid;
  /* A coluna precisa ser declarada: uma coluna implícita `auto` seria
     dimensionada pelo min-content do topbar e esticaria a página
     inteira além da viewport em telas estreitas. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: var(--topbar-h) minmax(0, 1fr) var(--hand-h);
  height: 100dvh;
}

/* LINHA explícita, pelo mesmo motivo das colunas logo abaixo: o painel de
   deck (`deck.css`) ocupa a mesma faixa POR CIMA, e item posicionado à mão
   não participa do auto-placement. Sem esta linha e a gêmea em `.hand`, o
   painel entra na linha 2 e EMPURRA a mesa e a mão para linhas implícitas —
   medido: a faixa de `1fr` colapsa para 0 e o painel fica com 268px de
   altura em vez da tela inteira. */
.table {
  /* LINHA e COLUNA explícitas. A linha impede que o painel de deck empurre
     a mesa para uma faixa implícita; a COLUNA impede a mesma coisa no outro
     eixo, e essa metade faltava — medido em 2026-08-17: em modo deck a
     `.table` ia para uma coluna implícita e virava `l=768 w=132` AO LADO do
     painel, mostrando o trilho inteiro. Foi o que o dono do produto
     fotografou no celular (RV-5/RV-6, ADR-0101).

     Item posicionado à mão não participa do auto-placement; item que só tem
     metade da posição participa da outra metade. */
  grid-row: 2;
  grid-column: 1;
  display: grid;
  /* 1: mini-mapas (0px na Fase 1) | 2: battlefield | 3: zonas */
  /* `relative` por causa dos dois controles da gaveta de boards (UX-44):
     eles são irmãos do trilho para poder disputar o empilhamento com ele, e
     `absolute` sem ancestral posicionado ancoraria na viewport — foi assim
     que o chevron foi parar em cima da topbar na primeira versão. */
  position: relative;
  grid-template-columns: var(--minimap-w) minmax(0, 1fr) var(--rail-w);
  min-height: 0;
  /* Sem transition aqui de propósito: animar grid-template-columns
     força relayout de toda a mesa a cada frame e, durante a
     interpolação, o valor computado perde os nomes de linha. A
     entrada do trilho é animada no próprio trilho (opacidade). */
}

/* Colunas explícitas por índice: sem isso, esconder o trilho de
   mini-mapas na Fase 1 tira o item do fluxo e o auto-placement
   empurra o battlefield para a coluna errada. */
.minimap-rail { grid-column: 1 / 2; }
.battlefield  { grid-column: 2 / 3; }
.rail         { grid-column: 3 / 4; }

/* =============================================================
   Topbar
   ============================================================= */
.topbar {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  min-width: 0;
  padding-inline: var(--sp-4);
  background: var(--bg-surface);
  border-bottom: 1px solid var(--line);
  /* A ADR-0056 encolheu a barra de 1264px para 388px e concluiu que ela
     "não precisa mais de válvula". Errado, e medido em aparelho: a
     validação foi feita a 500px, e celular em pé reporta 360–412px de CSS.
     A barra rola em vez de transbordar (ADR-0060).

     `pan-x` é o que faz o gesto lateral ser DA BARRA: sem ele o dedo
     arrastava a página inteira, que foi exatamente a queixa. */
  overflow-x: auto;
  scrollbar-width: none;
  touch-action: pan-x;
}
.topbar::-webkit-scrollbar { display: none; }

.topbar__brand {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-weight: 650;
  letter-spacing: -0.01em;
  flex: none;
}

/* O LOGO na barra da mesa.

   AQUI A ALTURA É TETO, e não escolha: a `--topbar-h` é 52px e ela é linha de
   GRID (`table.css:45`) — crescer o conteúdo não cresce a barra, transborda
   dentro dela. 30px deixa 11px de respiro de cada lado.

   E é por isso que a marca não alcança aqui o tamanho que alcança no site: com
   o logo a 30px as cinco cartas ficam com ~21px, bem abaixo dos 42px em que
   elas se separam (ADR-0277 D4). Na mesa quem identifica o produto é o NOME
   desenhado ao lado; a marca é presença, não leitura. Em tela estreita só ela
   fica — e aí, sozinha, ela usa a altura inteira. */
.topbar__home {
  display: flex;
  align-items: center;
  flex: none;
  border-radius: var(--radius-sm);
}
.topbar__home:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.topbar__logo {
  display: block;
  width: auto;
  height: 30px;
}
.topbar__brand small {
  color: var(--fg-dim);
  font-weight: 450;
  font-size: 11px;
}

.topbar__spacer { flex: 1; }

/* --- vida / contadores --- */
.counter {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--sp-1);
  padding: 3px 4px 3px 10px;
  background: var(--bg-surface-2);
  border: 1px solid var(--line);
  border-radius: 999px;
}
.counter__label {
  display: flex;
  align-items: center;
  gap: 3px;
  color: var(--fg-dim);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
/* Duas grafias do mesmo rótulo, e o CSS escolhe por largura (ADR-0060).
   O ícone nasce escondido: na tela larga a palavra é melhor. */
.counter__icon { display: none; font-size: 13px; letter-spacing: 0; }
.counter__value {
  min-width: 26px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  font-weight: 650;
  font-size: 15px;
}
.counter__btn {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  color: var(--fg-muted);
  line-height: 1;
}
.counter__btn:hover { background: var(--bg-surface-3); color: var(--fg); }

/* O rótulo do contador de jogador é um GATILHO (ADR-0057): ele troca a
   espécie. Herda o tamanho e o tom do rótulo comum de propósito — o que
   muda é o cursor e o realce, não o peso na barra. */
.counter__pick {
  padding: 2px 2px 2px 0;
  border-radius: var(--radius-sm);
  white-space: nowrap;
}
.counter__pick:hover { color: var(--fg); }

/* Fichas das espécies com valor que NÃO estão na caixa. Existem para o
   seletor não trocar um problema de espaço por um de informação: sem
   elas, olhar para "Veneno 0" esconderia 3 de energia da barra inteira.
   Na mesa comum tudo é zero e elas não custam nada. */
.pc-chips {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--sp-1);
}
.pc-chip {
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 2px 7px;
  background: var(--bg-surface-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--fg-muted);
  font-size: 11px;
  line-height: 1.4;
  white-space: nowrap;
}
.pc-chip:hover { background: var(--bg-surface-3); color: var(--fg); }
.pc-chip__n { font-variant-numeric: tabular-nums; font-weight: 650; }

/* A FICHA DO DANO LETAL DE COMANDANTE (AR-2, ADR-0236).

   Ela só existe ao cruzar 21, então o destaque é o ponto dela: uma ficha
   discreta ali não avisaria nada. Mesma forma das outras — a faixa é uma
   só, porque um segundo contêiner custaria um `gap` permanente numa barra
   que já rola a 500px.

   `--danger` é o mesmo vermelho do relógio atrasado e dos comandos
   destrutivos: a barra já tem um vocabulário de alerta, e inventar um
   segundo faria o usuário aprender duas cores para a mesma ideia. */
/* O DANO DE COMANDANTE MORA NA CAIXA DA VIDA (V-180, ADR-0237).

   Ele nasceu solto entre os contadores e o dono do produto o achou com
   dificuldade. Junto da vida ele se explica sozinho: são as duas formas de
   perder, e os dois números respondem à mesma pergunta.

   O GLIFO TEM CLASSE PRÓPRIA e é SEMPRE visível — `.counter__icon` é a
   grafia alternativa do rótulo (`display: none` acima do breakpoint, para
   a palavra aparecer no lugar, ADR-0060), e num botão sem palavra ela
   deixava só o `▾` na tela. Era esse o sintoma relatado. */
.counter__cmd {
  gap: 3px;
  margin-left: 2px;
  padding-inline: 5px;
  border-left: 1px solid var(--line);
  border-radius: 0 999px 999px 0;
  /* O TOM É O DOS BOTÕES AO LADO, e não o do rótulo. Ele herdava
     `--fg-dim` do `.counter__label`, que é a cor de quem só NOMEIA —
     e isso o deixava com cara de desabilitado ao lado de um `+` mais
     claro. Este botão AGE, como o `−` e o `+`: mesma cor, mesmo peso.
     Foi a outra metade do "difícil de visualizar" do `V-180`. */
  color: var(--fg-muted);
}
.counter__cmd:hover { color: var(--fg); }
.counter__glyph { display: inline-flex; font-size: 15px; letter-spacing: 0; }

/* O ALERTA É O PRÓPRIO BOTÃO, e só existe ao cruzar o limiar: enquanto o
   dano é normal não há número nem cor, que é a escolha do dono do produto.
   `--danger` é o mesmo vermelho do relógio atrasado e dos comandos
   destrutivos — a barra já tem um vocabulário de alerta, e um segundo
   faria aprender duas cores para a mesma ideia. */
.counter__cmd[data-lethal="1"] {
  color: var(--danger);
  background: rgba(229, 84, 75, 0.14);
}
.counter__cmd[data-lethal="1"]:hover { color: #ff8078; }

/* A VIDA EM ZERO, com o MESMO vermelho (JG-26, ADR-0268).

   Reusa o `data-lethal` do dano de comandante em vez de inventar uma marca
   nova: é o mesmo estado — "este jogador está morto pelas regras normais" —
   dito por dois caminhos, e a ADR-0237 já recusou aprender duas cores para
   a mesma ideia.

   SEM fundo, ao contrário do `counter__cmd`: aquele é um BOTÃO que nasce ao
   cruzar o limiar, e a cápsula o destaca; a vida é um número que já está na
   tela o tempo todo, e um fundo ali competiria com a caixa que a envolve. */
.counter__value[data-lethal="1"] { color: var(--danger); }

/* O CAMPO QUE SE DIGITA (JG-27/JG-28, ADR-0269).

   Ele ocupa a MESMA caixa do rótulo que substitui — mesma fonte, mesma
   largura mínima, mesmo alinhamento —, e é isso que faz a topbar não pular
   ao abrir. A topbar já rola a 360px; um campo mais largo que o número
   empurraria o resto na hora exata em que o usuário está mirando.

   `appearance: none` e `-moz-appearance` porque um `type="text"` herda o
   preenchimento do sistema em alguns temas, e aqui ele tem de parecer o
   número que estava ali. */
.counter__value[data-action] { cursor: text; }
.counter__value[data-action]:hover { color: var(--fg); }
.counter__value[data-action]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

.counter__input,
.action-bar__input {
  appearance: none;
  -moz-appearance: textfield;
  min-width: 0;
  padding: 0;
  border: 0;
  border-bottom: 1px solid var(--accent);
  background: none;
  color: var(--fg);
  font: inherit;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.counter__input { width: 40px; }
.action-bar__input {
  width: 46px;               /* o mesmo `min-width` do `.action-bar__label` */
  font-size: 10px;
  font-weight: 650;
}
/* `display: flex`/`grid` nos pais vence o atributo `hidden` — mesma
   armadilha do `.action-bar__group[hidden]` logo acima. */
.counter__input[hidden],
.action-bar__input[hidden] { display: none; }
.counter__cmdn { font-variant-numeric: tabular-nums; font-weight: 650; font-size: 13px; }

/* --- o relógio: turno + fase (ADR-0055 / ADR-0056) ---
   A fita de fases inteira morava aqui e custava 384px — e era o ÚNICO
   item flexível da topbar, então era sempre ela quem pagava o aperto: em
   1500px três das sete fases já não apareciam, e numa janela de 756px ela
   renderizava a 6px dos 384 que precisa. Virou dropdown.

   O botão fechado mostra turno E fase ativa: não é preciso abrir para
   saber onde se está, que é a condição para a fita poder sumir. */
.clock {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--sp-1);
}
.clock__now {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 4px 8px;
  background: var(--bg-surface-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--fg);
  white-space: nowrap;
}
.clock__now:hover { background: var(--bg-surface-3); }
.clock__turn {
  font-variant-numeric: tabular-nums;   /* o número não dança ao virar o turno */
  font-weight: 650;
  font-size: 14px;
}
.clock__sep { color: var(--fg-dim); }
.clock__phase { font-size: 12px; color: var(--fg-muted); }
.clock__caret { font-size: 9px; color: var(--fg-dim); }

/* --- ponto de estado (ADR-0056) ---
   As duas etiquetas somavam 145px de prosa dizendo, quase sempre, que
   está tudo bem. Isso é cor, não palavra. O texto mora no hint e no
   clique — e a falha da partida volta a escrever por extenso sozinha,
   porque a ADR-0042 exige que ela seja visível. */
.status-dot {
  flex: none;
  width: 10px;
  height: 10px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--fg-dim);
  /* A área CLICÁVEL é maior que o desenho: 10px é alvo impossível no
     dedo, e a regra dos 24px da suíte de layout vale aqui também. */
  box-shadow: 0 0 0 7px transparent;
  outline-offset: 4px;
}
.status-dot[data-tone="ok"]     { background: var(--ok); }
.status-dot[data-tone="warn"]   { background: var(--c-gold); }
.status-dot[data-tone="danger"] { background: var(--danger); }
.status-dot:hover { box-shadow: 0 0 0 7px var(--bg-surface-3); }

/* --- ações --- */
.btn {
  /* Nunca encolhe. Enquanto a fita de fases existia, era ELA quem pagava
     todo o aperto da topbar — e pagava até sumir. Com a ADR-0056 não há
     mais nada aqui grande o bastante para servir de válvula: a barra
     inteira cabe, e é essa a diferença. */
  flex: none;
  padding: 6px 12px;
  background: var(--bg-surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--fg-muted);
  font-size: 12px;
  white-space: nowrap;
}
.btn:hover { background: var(--bg-surface-3); color: var(--fg); border-color: var(--line-strong); }
.btn--ghost { background: none; border-color: transparent; }
.btn--icon {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border-radius: 50%;
  font-size: 15px;             /* as setas ↶ ↷ desenham pequeno demais a 12px */
  font-weight: 700;
  line-height: 1;
}
.btn--accent { background: var(--accent-soft); border-color: var(--accent-line); color: #cfe0ff; }
/* Tom de PERIGO no botão, com a mesma paleta que o menu do campo já usa
   para `Mesa vazia` e `Importar partida` (`--danger`). Só a cor e a borda:
   um fundo cheio faria dele o alvo mais chamativo de uma tela onde ele é o
   último recurso. */
.btn--danger { border-color: var(--danger); color: var(--danger); }
.btn--danger:hover {
  background: rgba(229, 84, 75, 0.14);
  border-color: var(--danger);
  color: #ff8078;
}
/* Desabilitado precisa parecer desabilitado no lugar onde mais aparece:
   desfazer/refazer nascem assim e passam a maior parte do tempo assim. */
.btn:disabled { opacity: 0.35; cursor: not-allowed; }
.btn:disabled:hover { background: var(--bg-surface-2); color: var(--fg-muted); border-color: var(--line); }

/* =============================================================
   BATTLEFIELD — três camadas, cada uma com um único trabalho.

   .battlefield  viewport: RECORTA. É a moldura da visão.
   .bf-stage     caixa de LAYOUT já escalada (1600·s x 900·s), que se
                 centraliza sozinha e carrega o deslocamento da visão.
                 Existe porque `transform: scale()` não altera a caixa
                 de layout: sem o stage, centralizar o tabuleiro seria
                 centralizar um bloco de 1600px dentro de uma coluna
                 menor, e o alinhamento cairia para o início.
   .bf-canvas    plano lógico 1600x900 tu. É o ÚNICO ancestral
                 posicionado das cartas — a origem de position.x/y.
   ============================================================= */
.battlefield {
  position: relative;
  overflow: hidden;
  /* O toque aqui é NOSSO (UX-22): sem isto o navegador fica com a pinça e
     com o arrasto de dois dedos, e o que cresce é a página inteira —
     topbar e mão junto —, que é o oposto de enquadrar o campo. Também
     devolve o duplo toque, que o zoom nativo comia.

     Só o campo. Fora dele o zoom da página continua sendo do usuário: ele
     é recurso de acessibilidade, e desligá-lo no documento inteiro (o
     `user-scalable=no` da meta viewport) seria tirar de quem precisa para
     resolver um problema de quem não precisa. */
  touch-action: none;
  background:
    radial-gradient(120% 90% at 50% 0%, #16202c 0%, var(--bg-field) 62%),
    var(--bg-field);
  z-index: var(--z-field);
}

.bf-stage {
  /* Centralizado por posicionamento, e não mais por `place-items: center`
     no pai. Enquanto o stage sempre COUBE na viewport, o grid centralizava
     de graça; com o zoom (ADR-0033) ele passa a ser maior que ela, e aí a
     faixa automática do grid cresce até o tamanho do item e ancora no
     início — o campo aproximado grudava na borda esquerda e não havia
     como deslocar para lá. Aqui o centro é o centro em qualquer tamanho.

     O DESLOCAMENTO da visão mora neste transform, e não no canvas: o
     stage é a caixa "real" do tabuleiro (ADR-0003), então deslocá-lo é
     deslocar o que se vê. Mantém "um transform, um trabalho" (ADR-0004):
     stage centraliza e desloca, canvas escala, carta posiciona. */
  position: absolute;
  left: 50%;
  top: 50%;
  width: calc(var(--bf-w) * var(--bf-scale) * 1px);
  height: calc(var(--bf-h) * var(--bf-scale) * 1px);
  border-radius: 10px;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.06);
  transform:
    translate3d(calc(var(--bf-pan-x) * 1px), calc(var(--bf-pan-y) * 1px), 0)
    translate(-50%, -50%);
}

.bf-canvas {
  position: absolute;                 /* <- origem das coordenadas X/Y */
  top: 0;
  left: 0;
  width: calc(var(--bf-w) * 1px);
  height: calc(var(--bf-h) * 1px);
  border-radius: 10px;
  /* Aqui morava `background: rgba(255, 255, 255, 0.012)` — uma lavagem de
     branco a 1,2% para o tabuleiro ler como superfície. Ela SAIU (UX-31),
     por observação em aparelho do dono do produto: era a opacidade que
     produzia a quebra visível na lateral do campo, e removê-la corrige.

     Faz sentido com o que a medição já dizia: 1,2% de alfa sobre o fundo é
     uma diferença de ~3/255, e composição rasa assim é justamente o que
     quantiza mal em painel de celular — o mesmo motivo pelo qual a faixa
     nunca reproduziu no headless, que é sRGB de 8 bits sem painel.

     Quem diz onde o tabuleiro está passa a ser SÓ o contorno de 1px do
     `.bf-stage`, e é por isso que ele fica. */
  transform: scale(var(--bf-scale));  /* escala uniforme, sem distorcer */
  transform-origin: 0 0;              /* casa com o canto do stage */
}

/* malha de apoio ao snap (mesma granularidade de --bf-snap * 4) */
.bf-canvas::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background-image: radial-gradient(circle, rgba(255, 255, 255, 0.055) 1px, transparent 1px);
  background-size: 40px 40px;
  mask-image: radial-gradient(85% 75% at 50% 50%, #000 55%, transparent 100%);
}

/* =============================================================
   Barra de ação da seleção

   Fixa na viewport, como a camada de arrasto: ancorada ao tabuleiro ela
   herdaria --bf-scale e viraria texto de 10px num notebook. Aqui os
   alvos têm tamanho real em qualquer escala — que é o que a torna
   utilizável no toque também.
   ============================================================= */
.action-bar {
  position: fixed;
  top: 0;
  left: 0;
  transform: translate3d(calc(var(--ab-x, 0) * 1px), calc(var(--ab-y, 0) * 1px), 0);
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 4px;
  background: var(--bg-surface-2);
  border: 1px solid var(--line-strong);
  /* 20px E NÃO `999px` (ADR-0176), e com UMA LINHA os dois são idênticos:
     o navegador limita o raio a metade da menor dimensão, e a barra de uma
     linha tem 40px de altura — `999px` já resultava em 20.

     A diferença aparece EMPILHADA: a 74px de altura o `999px` viraria 37, e
     o canto quase dobraria de curvatura só porque a barra ficou mais alta.
     Com o número fixo ela lê como "a mesma barra, mais alta", em vez de
     outro objeto — que é o que o dono do produto notou ao ver as pontas
     redondas demais no aparelho.

     O número é METADE DA ALTURA DE UMA LINHA. Se os botões mudarem de
     tamanho, ele muda junto — é a única amarração que ele tem. */
  border-radius: 20px;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.55);
  z-index: var(--z-overlay);
  white-space: nowrap;
  /* QUEBRA EM DUAS LINHAS QUANDO NÃO COUBER (JG-2, ADR-0174).

     A barra tinha 379px de largura fixa e nada nela se adaptava: cabia por
     7px a 390, cortava 23px a 360 e 63px a 320 — larguras de celular real,
     abaixo do clampe de 500px do Chromium, e por isso invisíveis para a
     bateria (só o `narrow.sh` chega lá). O relato veio com print.

     `--ab-max` é escrito pelo `updateActionBar` e é a LARGURA DO CAMPO menos
     a mesma folga de 4px que o clampe da posição usa. Amarrar ao campo e não
     a `100vw` é o que mantém uma fonte só para "onde a barra pode viver":
     no desktop com o trilho aberto o campo é mais estreito que a janela, e
     `100vw` deixaria a barra nascer maior do que o lugar onde ela cabe.

     Acima de 390px NADA muda — medido: uma linha, 40px. A 360 e a 320 ela
     vira duas linhas de 72px, e o cálculo da posição já usa `b.height`,
     então ela continua sem tapar a carta selecionada. */
  flex-wrap: wrap;
  justify-content: center;
  max-width: var(--ab-max, calc(100vw - 8px));
}
.action-bar[hidden] { display: none; }

.action-bar__count {
  display: grid;
  place-items: center;
  min-width: 22px;
  height: 22px;
  margin-right: 2px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--accent-soft);
  box-shadow: inset 0 0 0 1px var(--accent-line);
  color: #cfe0ff;
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Segunda grandeza da contagem: "2 cartas · 7 un.". Aparece só quando as
   duas divergem — sem token, repetir o mesmo número seria ruído. */
.action-bar__units {
  margin-right: 2px;
  color: var(--fg-dim);
  font-size: 10px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}

/* `display: grid`/`flex` abaixo vencem o atributo `hidden` — sem estas
   linhas, esconder um controle pelo JS não teria efeito nenhum. Mesma
   armadilha de `.pile[hidden]`. */
.action-bar__units[hidden],
.action-bar__group[hidden],
.action-bar__btn[hidden] { display: none; }

.action-bar__btn {
  display: grid;
  place-items: center;
  min-width: 30px;
  height: 30px;
  padding: 0 6px;
  border-radius: 999px;
  color: var(--fg-muted);
  font-size: 14px;
  line-height: 1;
}
.action-bar__btn:hover { background: var(--bg-surface-3); color: var(--fg); }
.action-bar__btn:active { background: var(--accent-soft); color: #cfe0ff; }

/* O marcador é um controle só: −, rótulo, +. Agrupar deixa claro que o
   rótulo diz sobre o que os sinais agem — e é ele que muda para
   "Lealdade" quando a seleção é planeswalker. */
.action-bar__group {
  display: flex;
  align-items: center;
  gap: 0;
  padding: 0 2px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.25);
}
/* CAIXA-BAIXA AQUI, E NÃO NA TABELA DE TEXTOS — pedido do dono do produto
   em 2026-09-15: *"'Level' está com a letra inicial maiúscula e 'units'.
   Seria bom padronizarmos, me parece que tudo minúsculo ficou mais bonito"*.

   Ele está certo sobre a inconsistência, e ela é REAL: o rótulo do marcador
   vem de `counter.*` (`Nível`, `Capítulo`, `Lealdade`) e o de quantidade de
   `actionBar.units` (`unidades`) — duas chaves escritas por pessoas
   diferentes, em momentos diferentes, para o mesmo lugar da tela.

   MAS A CORREÇÃO NÃO PODE SER NA TABELA, e é por isso que ela mora aqui: as
   MESMAS chaves `counter.*` servem o `title` e o `aria-label` do selo na
   carta, e o rótulo do menu de contexto. Ali "Nível" com maiúscula é a
   forma certa — é o nome da coisa, não um rótulo de controle. Baixar a
   caixa na origem consertaria a régua e estragaria os outros três.

   `text-transform` é presentação e não muda o texto do DOM, então o leitor
   de tela continua recebendo o nome próprio pelo `aria-label`, que é outro
   atributo.

   E ISTO É SEGURO PORQUE ESTE SLOT NUNCA MOSTRA DADO DO USUÁRIO: quem o
   preenche é `counterLabel(selectionCounterKind())`, e o `selectionCounterKind`
   só devolve espécie EMBUTIDA (a `TYPE_COUNTER` ou o padrão `p1p1`). O
   rótulo que o usuário digita no "Marcador com rótulo" chega ao selo e ao
   menu, nunca aqui — e baixar a caixa do texto DELE seria mexer no dado
   dele, que a ADR-0201 separa do nosso. */
.action-bar__label {
  min-width: 46px;
  text-align: center;
  color: var(--fg-muted);
  font-size: 10px;
  font-weight: 650;
  letter-spacing: 0.01em;
  text-transform: lowercase;
}

.action-bar__sep {
  width: 1px;
  height: 18px;
  margin: 0 3px;
  background: var(--line-strong);
}

/* A QUEBRA ACONTECE NO SEPARADOR, e não onde a linha acabar (JG-2, ADR-0175).

   A primeira versão deixava o `flex-wrap` decidir sozinho, e o dono do
   produto viu o resultado no aparelho: só o `⋯` caía para a segunda linha,
   sozinho e centrado. *"A quebra natural seria após a lupa de inspecionar,
   pois depois dela já temos um pipe dividindo bloco de ícones"* — e ele está
   certo: o separador já ERA a fronteira, declarada no markup desde a
   ADR-0023. Quebrar nele é usar a estrutura que já existia em vez de
   inventar uma segunda.

   `flex-basis: 100%` num item de flex empurra tudo o que vem depois para a
   linha seguinte. Ele fica invisível quando faz esse papel: um risco de 1px
   atravessando a barra inteira leria como uma divisória entre duas barras,
   e é uma só.

   Só sob `.is-stacked`, que o `updateActionBar` liga quando a barra não cabe
   numa linha — sem isso o desktop, onde ela cabe, ganharia duas linhas de
   graça. */
.action-bar.is-stacked .action-bar__sep {
  /* `flex-basis: 100%` é o que força a quebra; `width: 100%` fazia o mesmo
     e estava junto, redundante — achado ao mutar, porque trocar só o
     `flex-basis` não acusava nada. Duas declarações para o mesmo efeito
     são duas chances de alguém achar que mexeu e não ter mexido. */
  flex-basis: 100%;
  height: 0;
  margin: 0;
  background: none;
}

/* =============================================================
   Menu de contexto — a camada 3 da ADR-0023

   Mesmas coordenadas de tela da barra de ação, pelo mesmo motivo: alvo
   de toque com tamanho real em qualquer zoom. A posição é escrita pelo
   JS depois de MEDIR a caixa, porque virar para cima/para a esquerda
   depende de quanto o menu ocupa — e isso muda com o alvo.
   ============================================================= */
/* --- hint próprio (UX-1, ADR-0056) ---
   Em coordenadas de TELA, como o menu e a barra de ação: ancorado ao
   tabuleiro herdaria `--bf-scale` e viraria texto de 8px com o campo
   afastado. `pointer-events: none` porque ele nasce perto do cursor —
   um balão que intercepta o clique do botão que ele explica seria uma
   armadilha. */
.hint {
  position: fixed;
  top: 0;
  left: 0;
  transform: translate3d(calc(var(--hint-x, 0) * 1px), calc(var(--hint-y, 0) * 1px), 0);
  z-index: var(--z-overlay);
  max-width: 260px;
  padding: 5px 8px;
  background: var(--bg-surface-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  box-shadow: var(--sh-2);
  color: var(--fg);
  font-size: 11px;
  line-height: 1.35;
  pointer-events: none;
}

.menu {
  position: fixed;
  top: 0;
  left: 0;
  transform: translate3d(calc(var(--menu-x, 0) * 1px), calc(var(--menu-y, 0) * 1px), 0);
  z-index: var(--z-overlay);
  min-width: var(--menu-w);
  max-width: calc(var(--menu-w) + 60px);
  max-height: var(--menu-max-h);
  overflow-y: auto;
  padding: 4px;
  background: var(--bg-surface-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.6);
}
.menu[hidden] { display: none; }

/* Diz sobre o que o menu age. Em toque não há cursor visível para
   ancorar a resposta, então sem isto "isto vai virar qual carta?" não
   tem como ser respondido (ADR-0039, decisão 1). */
.menu__title {
  padding: 6px 10px 7px;
  margin-bottom: 3px;
  border-bottom: 1px solid var(--line);
  color: var(--fg-muted);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Item que marca ONDE SE ESTÁ (ADR-0056): as fases. O ponto entra antes
   do rótulo e o espaço fica reservado mesmo quando apagado, senão a lista
   inteira dança 12px conforme a fase muda. */
.menu__item[aria-checked]::before {
  content: "•";
  width: 10px;
  margin-left: -4px;
  color: transparent;
  font-size: 15px;
  line-height: 1;
}
.menu__item[aria-checked="true"]::before { color: var(--accent); }
.menu__item[aria-checked="true"] .menu__label { color: var(--fg); font-weight: 650; }

.menu__item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  /* 32px de altura: o alvo de toque confortável que a ADR-0023 mediu em
     ~24px, com folga. É a razão de a cauda longa caber aqui e não em
     pontinhos na carta. */
  min-height: 32px;
  /* O padding vertical não muda o item de uma linha (o `min-height`
     vence), mas dá respiro ao de duas: sem ele a dica de um encosta no
     rótulo do seguinte, e os dois viram um bloco só de texto. */
  padding: 4px 10px;
  border-radius: var(--radius-sm);
  color: var(--fg);
  font-size: 12.5px;
  text-align: left;
  white-space: nowrap;
}
.menu__item:hover,
.menu__item:focus-visible { background: var(--bg-surface-3); outline: none; }
.menu__item:active { background: var(--accent-soft); color: #cfe0ff; }

/* Deletar. Último, depois de um separador, e com cor própria — a única
   coisa daqui que apaga uma carta de deck (ADR-0039, decisão 7). */
.menu__item[data-tone="danger"] { color: var(--danger); }
.menu__item[data-tone="danger"]:hover { background: rgba(229, 84, 75, 0.14); color: #ff8078; }

.menu__text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.menu__label { display: block; }

/* Segunda linha, só nos comandos cuja diferença não se adivinha pelo
   rótulo (hoje os dois zeros da partida). O item cresce em altura em vez
   de a dica virar `title` nativo: aquele atrasa um segundo, não é
   estilizável e não existe em toque — é o item UX-1 do backlog, e não faz
   sentido criar mais um caso dele aqui. */
.menu__hint {
  color: var(--fg-dim);
  font-size: 10.5px;
  line-height: 1.25;
  white-space: normal;
}
.menu__item:hover .menu__hint,
.menu__item:focus-visible .menu__hint { color: var(--fg-muted); }
.menu__item[data-tone="danger"] .menu__hint { color: var(--fg-dim); }

/* A dica de tecla é RENDERIZADA da tabela de atalhos (ADR-0024/0039):
   nunca escrita no rótulo do item. */
.menu__key {
  color: var(--fg-dim);
  font-size: 10px;
  font-family: inherit;
}
.menu__key kbd {
  padding: 1px 5px;
  border: 1px solid var(--line-strong);
  border-radius: 4px;
  background: var(--bg-surface);
  font-family: inherit;
  font-size: 10px;
}

.menu__sep {
  height: 1px;
  margin: 4px 6px;
  background: var(--line);
}

/* Laço de seleção. Vive DENTRO do canvas e é posicionado em unidades
   lógicas, como as cartas — assim não precisa de conversão própria e
   acompanha zoom e resize de graça. */
.marquee {
  position: absolute;
  left: 0;
  top: 0;
  width: calc(var(--mw, 0) * 1px);
  height: calc(var(--mh, 0) * 1px);
  transform: translate3d(calc(var(--mx, 0) * 1px), calc(var(--my, 0) * 1px), 0);
  border: 1px solid var(--accent);
  background: var(--accent-soft);
  border-radius: 3px;
  pointer-events: none;
  z-index: 500;
}

body.is-marqueeing { cursor: crosshair; }

/* estado de drop */
.bf-canvas.is-over { box-shadow: inset 0 0 0 2px var(--accent-line); }

/* Indicador de escala — e, fora do ajuste, o botão de VOLTAR para ele.

   É a saída de emergência da ADR-0033: pinça de trackpad é acidental com
   facilidade, e quem caiu no zoom sem querer olha justamente para o canto
   que informa a escala. No ajuste ele volta a ser texto inerte
   (`pointer-events: none`), porque botão que não faz nada é pior que
   botão ausente — a lição do preview da Fase 2 na topbar. */
.battlefield__scale {
  position: absolute;
  right: var(--sp-3);
  bottom: var(--sp-3);
  padding: 3px 8px;
  border: 0;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.45);
  color: var(--fg-dim);
  font: inherit;
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}

.battlefield__scale[data-zoomed="true"] {
  pointer-events: auto;
  cursor: pointer;
  color: var(--fg);
  background: rgba(0, 0, 0, 0.72);
  box-shadow: 0 0 0 1px var(--accent-line);
}

.battlefield__scale[data-zoomed="true"]:hover { background: rgba(0, 0, 0, 0.88); }

/* Durante o arrasto ele sai da frente: é um alvo de clique por cima do
   campo, e `dropZoneAt` resolve por `elementFromPoint` — soltar uma carta
   naquele canto cancelaria o arrasto em vez de posicioná-la. */
body.is-dragging .battlefield__scale { pointer-events: none; }

/* Deslocar a visão: o cursor é o único aviso de que o modo existe. */
body.is-pan-ready .battlefield,
body.is-pan-ready .battlefield * { cursor: grab; }
body.is-panning .battlefield,
body.is-panning .battlefield * { cursor: grabbing; }

/* =============================================================
   Painéis recolhíveis (UX-23)

   Um componente só para o trilho e para a mão, porque a pergunta é a
   mesma nos dois: "fecha isto e me devolve o campo". O chevron não troca
   de glifo — ele GIRA. O trilho é coluna à direita numa tela e faixa
   embaixo na outra (ver o responsivo no fim do arquivo), e um glifo por
   direção seria quatro literais para manter em sincronia com um layout
   que o CSS já decide sozinho.
   ============================================================= */
.panel-toggle {
  display: grid;
  place-items: center;
  flex: none;
  /* 24px é a régua de alvo que a suíte de layout cobra nos botões da
     topbar. Aqui ela vale mais: recolhido, este é o ÚNICO controle do
     painel — errar o alvo deixa a zona fechada sem saída visível. */
  width: 24px;
  height: 24px;
  padding: 0;
  border-radius: var(--radius-sm);
  color: var(--fg-dim);
  line-height: 1;
}
.panel-toggle:hover { background: var(--bg-surface-3); color: var(--fg); }
.panel-toggle:focus-visible { outline: 2px solid var(--accent-line); outline-offset: -2px; }

/* A variante que engole o RÓTULO. O botão de 24px é a régua de alvo, não
   um alvo confortável: no aparelho, acertar a seta ao lado da palavra
   "Mão" — que não fazia nada — foi queixa de uso. Aqui a seta, a palavra
   e a contagem são o mesmo botão, e o alvo passa de 24 para ~90px. */
.panel-toggle--wide {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  width: auto;
  padding: 0 8px 0 4px;
  color: inherit;                /* herda o tom da barra em que mora */
}
.panel-toggle__word { text-transform: inherit; letter-spacing: inherit; }

.panel-toggle__caret {
  font-size: 13px;
  line-height: 1;
  transition: transform 120ms var(--ease);
}
/* Trilho em COLUNA: fechar empurra para a direita. */
[data-rail="collapsed"] .rail .panel-toggle__caret { transform: rotate(180deg); }
/* A mão é sempre faixa embaixo: fechar empurra para baixo. */
.hand .panel-toggle__caret { transform: rotate(90deg); }
[data-hand="collapsed"] .hand .panel-toggle__caret { transform: rotate(-90deg); }

/* =============================================================
   Trilho de zonas (direita): Grimório, Cemitério, Exílio, Command
   ============================================================= */
.rail {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-3);
  overflow-y: auto;
  background: var(--bg-surface);
  border-left: 1px solid var(--line);
  z-index: var(--z-rail);
}

/* `display: grid` abaixo vence o atributo `hidden` do HTML — sem esta
   linha, esconder uma pilha pelo JS não teria efeito nenhum. */
.pile[hidden] { display: none; }

.pile {
  position: relative;
  display: grid;
  /* SEM `grid-template-rows: auto auto` (UX-29). A pilha tem dois filhos,
     mas o slot é `display: none` em dois formatos — na faixa horizontal
     (ADR-0060) e na janela baixa. Declarar as duas linhas mantinha a
     SEGUNDA viva e vazia, e com ela o `gap`: o conteúdo ficava 6px acima
     do centro da caixa, que foi o "estão próximos ao topo ao invés de no
     meio" do teste em aparelho.

     Medido antes: folga de 9px acima contra 15 abaixo, e 4 contra 10 no
     recolhido — os mesmos 6px do `gap` nos dois. Com as linhas implícitas,
     o grid só cria o que existe e o `gap` só separa o que é separável.

     E SEM `align-content: center` por cima: tirar a linha fantasma já
     centraliza, porque a caixa passa a ter exatamente a altura do
     conteúdo. Verificado por mutação — remover o `align-content` passa
     verde nos três formatos, e devolver o `grid-template-rows` derruba a
     asserção com os −6px na mensagem. Terceira linha morta que a varredura
     de mutação achou nesta frente esta semana (ADR-0068 e 0069 foram as
     outras), e a régua é a mesma: linha que não pode falhar engana quem
     vier depois. */
  gap: 6px;
  min-width: 0;                /* deixa encolher quando o trilho vira faixa */
  padding: var(--sp-2);
  background: var(--bg-surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: border-color 120ms var(--ease), background 120ms var(--ease);
}
.pile:hover { border-color: var(--line-strong); }
.pile.is-over {
  background: var(--accent-soft);
  border-color: var(--accent-line);
}

/* Altura (não largura) manda: é ela que decide quantas zonas cabem no
   trilho sem scroll. O `14vh` original foi calibrado para 3 pilhas; com
   sideboard e comando o trilho pode chegar a 5, e a mesma altura empurra
   as últimas para fora da dobra. O JS escreve `data-piles` com a contagem
   VISÍVEL e a altura acompanha. */
.rail { --pile-slot-h: clamp(76px, 14vh, 186px); }
.rail[data-piles="4"] { --pile-slot-h: clamp(64px, 11vh, 150px); }
.rail[data-piles="5"] { --pile-slot-h: clamp(56px, 9vh, 122px); }

.pile__slot {
  position: relative;
  display: grid;
  place-items: center;
  justify-self: center;
  height: var(--pile-slot-h);
  max-width: 100%;
  aspect-ratio: 63 / 88;
  border-radius: var(--radius-sm);
  border: 1px dashed var(--line-strong);
  background: rgba(0, 0, 0, 0.25);
  overflow: hidden;
}
/* a "espessura" da pilha */
.pile__slot::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow:
    3px 3px 0 -1px var(--bg-surface-3),
    6px 6px 0 -2px var(--bg-surface-2);
  opacity: 0;
  transition: opacity 120ms var(--ease);
  pointer-events: none;
}
.pile[data-count]:not([data-count="0"]) .pile__slot::after { opacity: 1; }

/* A carta do topo preenche o slot exatamente — inset:0 evita a
   ambiguidade de resolver width/height em % dentro de um grid. */
.pile__slot > .card {
  position: absolute;
  inset: 0;
  width: auto;
  height: auto;
  cursor: default;
}

.pile__empty { color: var(--fg-dim); font-size: 11px; }

.pile__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  /* Item de GRID, e item de grid também nasce com `min-width: auto`: sem
     esta linha a faixa da grade cresce até o max-content da linha inteira
     e o `min-width: 0` do nome abaixo nunca chega a ser consultado. É a
     mesma pegadinha do `minmax(0, 1fr)` que a `.app` já carrega. */
  min-width: 0;
}
.pile__name {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;                /* quem cede é a PALAVRA, nunca a contagem */
  font-size: 11px;
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
/* O ÍCONE É PERMANENTE, e a palavra é que vai e vem (UX-50, ADR-0218).

   Até 2026-09-07 as duas grafias eram EXCLUDENTES — o ícone nascia
   escondido porque "onde a palavra cabe, ela é melhor" (ADR-0062) — e no
   lugar dele, onde a palavra aparecia, ficava um ponto de cor. O pedido do
   dono do produto inverte a premissa e a razão é de PRODUTO, não de espaço:
   quem aprende o ícone no celular tem de reencontrá-lo no desktop, e duas
   grafias que nunca se cruzam não ensinam uma a outra.

   A COR VEM PARA CÁ e o ponto sai. Ele nunca foi par do ícone: era o
   substituto dele, e o CSS os alternava. Com o ícone sempre presente o
   ponto fica sem trabalho, e manter os dois seria a primeira superfície do
   app com as duas grafias somadas. De quebra o celular GANHA cor, que lá
   não existia — o ponto era justamente o que sumia na faixa. */
.pile__icon {
  display: inline;
  font-size: 13px;
  line-height: 1;
  color: var(--pile-color, var(--fg-dim));
}

/* A palavra corta com reticências; a contagem não encolhe nunca.
   Sem estas duas linhas, "GRIMÓRIO" empurrava o número para fora do
   trilho estreito — e o `overflow-y: auto` do trilho faz o eixo
   horizontal também recortar, então ele sumia sem deixar rastro. O
   trilho é justamente onde se lê "quantas cartas tenho no grimório".
   Achado por PRINT, com a suíte verde. */
.pile__word {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pile__meta .pile__count { flex: none; }
/* AQUI MORAVA O PONTO DE COR (`::before` de 6px), e ele saiu no UX-50
   (ADR-0218). O `--pile-color` não ficou órfão: mudou de consumidor para o
   `.pile__icon`, acima. */
.pile__count {
  min-width: 24px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--bg-surface-3);
  text-align: center;
  font-variant-numeric: tabular-nums;
  font-weight: 650;
  font-size: 12px;
}

.pile[data-zone="library"]   { --pile-color: var(--zone-library); }
.pile[data-zone="graveyard"] { --pile-color: var(--zone-graveyard); }
.pile[data-zone="exile"]     { --pile-color: var(--zone-exile); }
.pile[data-zone="command"]   { --pile-color: var(--zone-command); }
.pile[data-zone="sideboard"] { --pile-color: var(--zone-sideboard); }

/* A CONTAGEM É O BOTÃO DE VER (UX-29).

   Aqui morava `.pile__actions`: um `👁` absoluto no canto, `opacity: 0`
   fora do `:hover`. No dedo não há hover, então ele era **invisível e
   clicável** — o pior dos dois mundos, e a queixa foi exatamente essa:
   *"se clicar na posição dele ele funciona, mas precisa saber a posição
   sem poder visualizá-lo"*. A ADR-0058 já tinha resolvido metade disto
   dando ação principal à pilha, mas no grimório a principal é COMPRAR, e
   ver o grimório continuava sem caminho visível fora do menu.

   Juntar em vez de revelar: a contagem e o olho respondiam sobre a mesma
   zona e ocupavam dois cantos da pilha. Como um botão só, o alvo cresce,
   o olho ganha um lugar onde sempre aparece, e a pilha ocupa MENOS espaço
   do que ocupava com os dois separados.

   O `data-action="view"` continua vencendo o `pile-primary` do contêiner
   sem nenhuma guarda nova: o despacho resolve por `closest`, e o botão
   está dentro da pilha (ADR-0058). */
.pile__view {
  display: flex;
  align-items: center;
  gap: 5px;
  flex: none;
  color: inherit;
}
.pile__view:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* A CÁPSULA É DE QUEM É BOTÃO DE VERDADE (UX-48, ADR-0195).

   Ela valia nas cinco zonas, e nas cinco ela prometia a mesma coisa: "aqui
   tem um segundo alvo". Só que `pilePrimary` faz `DRAW` no grimório e
   `openViewer` em TODAS as outras — quer dizer que em quatro das cinco o
   botão e o corpo da pilha executam o MESMO comando. A cápsula ali era o
   *igual desenhado diferente* da ADR-0083 ao contrário: coisas diferentes
   desenhadas iguais.

   O SELETOR É O `data-action`, e não o nome da zona. É o mesmo dado que
   decide o comportamento, então a aparência não pode divergir dele — e no
   dia em que outra zona ganhar ação principal própria, ela ganha a cápsula
   sem ninguém lembrar de vir aqui. Nome de zona no CSS seria a segunda
   verdade de sempre.

   Ideia do dono do produto em 2026-09-04, ao validar o V-133: *"se ele é o
   único clicável, faz sentido ser o único com uma diferença no ícone que
   deixe visível que o ícone funciona como botão"*. */
.pile__view[data-action] {
  /* A régua de alvo do projeto, a mesma que a suíte de layout cobra nos
     botões da topbar. Sem ela o botão nasce com 19px na faixa, que é o
     tamanho do texto — e um alvo de toque não pode sair do tamanho da
     fonte. Custa 1,6px de faixa (36,4 → 38), medido, e é o item pagando
     de volta uma fração pequena dos 32px que ele acabou de devolver. */
  min-height: 24px;
  padding: 1px 6px 1px 7px;
  border-radius: 999px;
  background: var(--bg-surface-3);
  transition: background 120ms var(--ease), color 120ms var(--ease);
}
.pile__view[data-action]:hover { background: var(--accent); color: #fff; }
/* Dentro do botão a contagem não desenha a própria cápsula — quem a
   desenha agora é o botão, senão seriam duas pílulas concêntricas.

   ESCOPADA AO `[data-action]` (UX-48): a razão escrita acima só existe
   quando HÁ pílula de fora. Nas quatro zonas em que o botão deixou de ser
   botão não há, e sem o escopo a contagem ficaria como texto solto — o
   badge de "quantas cartas tenho no cemitério" some, e ele é o que se lê no
   trilho desde a ADR-0056. */
.pile__view[data-action] .pile__count { padding: 0; background: none; border-radius: 0; }
.pile__eye {
  font-size: 11px;
  line-height: 1;
  opacity: 0.75;   /* presente sempre; discreto para não competir com o número */
}
.pile__view[data-action]:hover .pile__eye { opacity: 1; }

/* --- trilho RECOLHIDO (UX-23) ---
   O que sobra de cada zona é **ponto de cor + contagem**. Não é enfeite:
   a contagem é o que se lê no trilho ("quantas cartas tenho no
   grimório") e o ponto é a identidade que a ADR-0056 já tinha decidido
   ser cor e não palavra, no estado da partida.

   O que NÃO sai: a caixa da pilha. Ela é `data-dropzone` e
   `data-action="pile-primary"` — recolher a zona não pode tirar o alvo
   para onde se arrasta a carta que vai ao cemitério, e é exatamente por
   isso que `OPTIONAL_PILES` só esconde sideboard e comando, que nunca
   recebem arrasto. Recolhido, o toque continua comprando no grimório.

   A palavra sai por `.pile__word` e o ÍCONE fica — e desde o UX-50
   (ADR-0218) ele já estava lá antes de recolher, então recolher deixou de
   ser uma TROCA de grafia e passou a ser só a saída da palavra. As duas
   linhas que ligavam o ícone e desligavam o ponto saíram daqui por terem
   virado no-op, não por mudança de intenção. */
#app[data-rail="collapsed"] { --rail-w: var(--rail-collapsed-w); }

[data-rail="collapsed"] .rail { gap: var(--sp-1); padding: var(--sp-1); }
[data-rail="collapsed"] .pile { padding: 3px 4px; }
[data-rail="collapsed"] .pile__slot,
[data-rail="collapsed"] .pile__word { display: none; }
[data-rail="collapsed"] .pile__meta { justify-content: center; gap: 4px; }
[data-rail="collapsed"] .pile__count { min-width: 20px; padding: 0 4px; font-size: 11px; }

/* =============================================================
   MÃO
   ============================================================= */
.hand {
  /* Gêmea de `.table` — ver o comentário lá. Linha E coluna. */
  grid-row: 3;
  grid-column: 1;
  position: relative;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  background: linear-gradient(180deg, var(--bg-surface) 0%, #10151c 100%);
  border-top: 1px solid var(--line);
  z-index: var(--z-hand);
  transition: background 120ms var(--ease), box-shadow 120ms var(--ease);
}
.hand.is-over { box-shadow: inset 0 2px 0 0 var(--accent-line); }

/* Alça de altura da mão (UX-13). Faixa fina no topo da MÃO, e não do
   `.hand__cards`: ela é absoluta dentro de `.hand`, cuja primeira linha é
   a barra. Medido em 2026-08-15, ao fazer o UX-28 — a alça ocupa
   `1..9` e a barra `1..29`, então ela mora sobre a BARRA, não sobre o
   respiro das cartas.

   O comentário anterior dizia o contrário ("dentro da folga que o
   `padding` do `.hand__cards` já reservava"), e a diferença importa: era
   ele que fazia o respiro de 24px parecer intocável. Não é — zerá-lo não
   encosta na alça. O que continua valendo, e é a razão de verdade, é que
   ela fica DENTRO da mão: para fora comeria a faixa de baixo do campo,
   que é alvo de clique e de drop.

   Efeito de borda conhecido, anterior a este item: os 8px cobrem o topo
   do `#handToggle` (3..27), então o toque nos 6px de cima do botão
   redimensiona em vez de recolher. Não é do UX-28 e fica registrado.

   O traço é desenhado sempre, e não só no hover: a mão é a única borda
   arrastável da mesa, e afordância que só aparece com o cursor em cima não
   é descoberta por quem não sabe que ela existe (é a queixa do UX-4). */
.hand__grip {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: var(--hand-grip-h);
  z-index: 1;
  cursor: ns-resize;
  touch-action: none;          /* o dedo aqui redimensiona, não rola a página */
}
.hand__grip::before {
  content: "";
  position: absolute;
  top: 3px;
  left: 50%;
  width: 40px;
  height: 2px;
  margin-left: -20px;
  border-radius: 1px;
  background: var(--line);
  transition: background 120ms var(--ease), width 120ms var(--ease);
}
.hand__grip:hover::before,
.hand__grip:focus-visible::before { background: var(--accent-line); width: 64px; }
.hand__grip[data-dragging="1"]::before { background: var(--accent); width: 64px; }
.hand__grip:focus-visible { outline: 2px solid var(--accent-line); outline-offset: -2px; }

.hand__bar {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 6px var(--sp-4);
  color: var(--fg-dim);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.hand__bar .pile__count { text-transform: none; letter-spacing: 0; }
/* O botão tem 24px de ALVO e contribui com 16px de LINHA: sem a margem
   negativa a barra cresceria de 28 para 36px, e os 8px sairiam da carta
   da mão — que é justamente o recurso que este item existe para devolver.
   O alvo continua inteiro; só a caixa de layout encolhe. */
.hand .panel-toggle { margin-block: -4px; }

/* O menu de ações da mão (JG-9). A MESMA conta do botão acima, e é ela que
   faz a carta pagar zero: 24px de ALVO, 16px de LINHA. Um `.btn--icon` de
   28px aqui levaria a barra de 28 para 40px, e os 12px sairiam da altura da
   carta em toda janela em pé — medido, 227→215px a 1500×940 e 183→171 a
   500×760. A barra é o teto da carta, e a ADR-0068 já pagou caro por cada
   pixel dela.

   A margem esquerda é o respiro de DEDO (`--hand-actions-gap`): ela se soma
   ao `gap` da barra, não o substitui. Ver o comentário do token. */
.hand__actions {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  padding: 0;
  margin-block: -4px;
  margin-left: var(--hand-actions-gap);
  border-radius: 50%;
  font-size: 13px;
  line-height: 1;
}

/* --- mão RECOLHIDA (UX-23 / UX-26) ---
   Sobra a barra: contagem e o botão de reabrir. A mão continua sendo
   `data-dropzone="hand"` — mandar uma carta de volta para a mão pelo
   arrasto continua funcionando com ela fechada, pela mesma razão que a
   pilha recolhida continua alvo.

   A alça (UX-13) sai: não há altura para arrastar, e uma alça que não
   move nada é pior que alça nenhuma. Reabrir devolve a altura PEDIDA —
   a fração de quem arrastou, ou o padrão —, porque recolher é um estado
   à parte e não uma altura (`hand.f` não é tocado). */
#app[data-hand="collapsed"] { --hand-h: var(--hand-collapsed-h); }
[data-hand="collapsed"] .hand__cards,
[data-hand="collapsed"] .hand__grip { display: none; }

.hand__cards {
  display: flex;
  align-items: flex-end;
  /* `safe center` e NÃO `center` (JG-6, ADR-0172).

     Centrar é o desenho da mão quando ela cabe, e continua sendo. O problema
     é o que `center` faz quando ela NÃO cabe: a sobra é dividida igualmente
     nos dois lados, e a da esquerda fica **inalcançável**, porque `scrollLeft`
     não vai a negativo. Com a barra toda no começo, a primeira carta já
     estava cortada — medido a 500px: 138px escondidos numa carta de 131,
     ou seja, mais de uma carta inteira fora do alcance, contra um
     `scrollMax` de 154.

     O `safe` é exatamente para isto: enquanto couber, centra; quando
     transbordar, alinha ao começo e a rolagem passa a alcançar tudo. É por
     isso que o defeito só aparecia no APARELHO — no desktop a mão cabe, e
     `center` e `safe center` são a mesma coisa quando não há transbordo. */
  justify-content: safe center;
  gap: 0;
  /* O RESPIRO DO LIFT É EMPRESTADO DE CIMA, NÃO TIRADO DA CARTA (ADR-0068).
     A margem negativa e o `padding` são o mesmo `--hand-lift`, e é o par
     que faz a conta fechar:

       · a margem negativa sobe a caixa por cima da BARRA e a deixa
         `lift` px mais alta — a região de recorte cresce para cima;
       · o `padding` devolve esses mesmos px por dentro, então a caixa de
         conteúdo, e portanto a CARTA, fica exatamente onde estava.

     A margem entra na conta da faixa do grid (`margem + borda = a faixa`),
     então `--hand-h` não se mexe, `handChrome()` continua medindo 41px e
     o piso e a detenção não sentem nada. O lift volta de graça.

     Por que não bastava `overflow-y: visible`: `overflow-x: auto` obriga o
     outro eixo a não ser `visible` (spec de CSS — `visible` computa para
     `auto` quando o par não é `visible` nem `clip`). Quem recorta a carta
     levantada nunca foi a barra; é o próprio contêiner que rola. Não dava
     para deixar a carta transbordar, então a caixa é que subiu. */
  margin-top: calc(-1 * var(--hand-lift));
  padding: var(--hand-lift) var(--sp-4) var(--sp-3);
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
}

/* O `+7` DA MÃO VAZIA (JG-9). Mora dentro do `#handCards`, que é a faixa
   que — só neste estado — não tem carta nenhuma para disputar. É por isso
   que ele é barato: com uma carta na mão ele não existe.

   `align-self: center` e não `flex-end`: o `.hand__cards` alinha as cartas
   pela BASE, que é o desenho do leque; um botão de acesso rápido colado no
   chão da mão ficaria pendurado na borda em vez de no meio. É o único filho
   dessa caixa quando aparece, então centrar não desalinha nada.

   `flex: 0 0 auto` porque o contêiner rola na horizontal e um filho
   encolhível viraria uma pastilha achatada na janela estreita.

   O tamanho é fixo e generoso de propósito — 56px passa o alvo mínimo de
   dedo com folga, e ele cabe na mão do formato curto (125px de faixa), que
   é a mais baixa da mesa. Não deriva da carta: não há carta aqui, e derivar
   de uma medida que vale zero é a armadilha da ADR-0133. */
.hand__draw {
  align-self: center;
  flex: 0 0 auto;
  display: grid;
  /* Duas linhas — ícone em cima, número embaixo (V-119). Quem centra é o
     `place-content` e NÃO o `place-items` sozinho: com duas linhas é o
     CONJUNTO que precisa ficar no meio do círculo, e a diferença entre os
     dois é invisível com uma linha só. */
  grid-auto-flow: row;
  place-content: center;
  justify-items: center;
  gap: 1px;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  line-height: 1;
  letter-spacing: 0;
  color: var(--fg);
  background: var(--surface-2);
  border: 1px dashed var(--line);
  cursor: pointer;
  /* O ícone é `1em` (ADR-0083), então quem o dimensiona é esta linha. */
  font-size: 17px;
}
/* O número é menor que o ícone de propósito: o desenho carrega o "de onde",
   e o número carrega o "quantas". Lidos juntos, o ícone é quem se reconhece
   primeiro — é a mesma hierarquia da pilha do trilho, onde o glifo manda e a
   contagem acompanha (ADR-0070). */
.hand__draw-n {
  font-size: 14px;
  font-weight: 700;
}
/* Tracejado, e é semântico: o contorno cheio é de botão que age sobre o que
   está ali, e aqui não há nada — a borda diz "espaço vazio a preencher", que
   é a mesma linguagem do slot de drop. */
.hand__draw:hover { border-style: solid; border-color: var(--accent-line); color: #cfe0ff; }
.hand__draw:focus-visible { outline: 2px solid var(--accent-line); outline-offset: 2px; }

/* A caixa das cartas agora cobre os `lift` px de baixo da barra, e ali mora
   o botão de recolher (`3..27`). Sem isto ele perderia 18 dos seus 24px de
   alvo para uma faixa de `padding` vazia — trocar o lift pelo alvo do botão
   seria um péssimo negócio, e foi o único custo real desta solução.

   Só o BOTÃO sobe, não a barra inteira: se a barra viesse junto, a carta
   levantada passaria por trás dela em vez de por cima, que é o oposto do
   que o lift existe para fazer. O botão é estreito e fica na ponta
   esquerda; a carta levantada passa por cima dele só quando a mão está
   rolada até o começo — a carta é `position: relative` com `z-index` no
   hover, então ela ganha de qualquer jeito, e ali é ela que se quer ver.

   `position: relative` sozinho, sem `z-index`: posicionado ganha de não
   posicionado no mesmo contexto de empilhamento, e um `z-index: 1` aqui
   seria um número que não decide nada. Verificado por mutação — tirar só
   o `z-index` passa verde nas três janelas; tirar a regra inteira derruba
   as três. Número que não pode falhar é o `FULL_MIN = 130` da ADR-0062
   esperando para acontecer. */
/* O MENU DE AÇÕES ENTRA NA MESMA REGRA, e ele nasceu de fora dela — foi o
   defeito do V-118 (2026-09-02): *"tanto no desktop quanto no aparelho só
   está possível utilizar essas ações quando a mão está fechada"*.

   A causa é o parágrafo acima, inteiro: com a mão ABERTA o `.hand__cards`
   sobe 20px por cima da barra, e o menu era `static` — perdia o hit test
   para ele em toda a sua altura. Fechada, o `.hand__cards` é `display:
   none` e o botão funcionava, que é exatamente o que o relato descreve.

   Quem mexer aqui: **todo controle da `.hand__bar` precisa desta linha.**
   Não é enfeite de empilhamento — é o que torna o alvo alcançável. */
.hand .panel-toggle,
.hand__actions { position: relative; }

/* Aqui morava um `@media (hover: none) { --hand-lift: 0px }` (ADR-0066), e
   ele saiu na ADR-0067. A ausência fica registrada porque a media query era
   o coração daquela decisão, e quem for procurá-la merece achar o motivo em
   vez de um silêncio.

   A ADR-0068 tornou a pergunta INÚTIL, que é melhor que respondê-la: o
   respiro não sai mais da carta em formato nenhum, então não há mais o que
   economizar zerando o lift no aparelho. Perguntar "este ponteiro faz
   hover?" só fazia sentido enquanto a resposta custava pixels de carta. */
/* A carta da mão sai da ALTURA disponível, não de `--card-w`. Duas
   consequências: ela cresce junto com a mão (é o que torna a alça de
   redimensionar barata, UX-13), e `--card-w` continua sendo só a unidade
   do plano lógico — que é o que o CLAUDE.md exige que ela continue sendo.

   A matemática do arrasto sobrevive porque nunca dependeu do tamanho na
   tela: o ponto de pega é normalizado em 0..1 e a conversão do drop usa
   a largura LÓGICA (ADR-0006). */
.hand__cards > .card {
  height: 100%;
  width: auto;
  aspect-ratio: 63 / 88;
  /* A ARBITRAGEM da mão no toque (UX-26): o gesto HORIZONTAL é da
     rolagem, o VERTICAL é do arrasto da carta.

     `.card` declara `touch-action: none` (card.css) e é o certo em toda
     zona menos esta: aqui a carta é conteúdo de um `overflow-x: auto`, e
     com o dedo tomando tudo não sobrava nenhum jeito de rolar a mão —
     puxar de lado levava uma carta junto, e a alça de altura, que é o
     único vão fora das cartas, arrasta a mão inteira. Era a queixa.

     A troca é consciente e barata: o que se perde é começar um arrasto
     por um traço quase horizontal, e é o único traço que não leva a
     lugar nenhum — a mesa fica ACIMA da mão em qualquer formato, e é
     para cima que a carta sai. Só o toque muda; `touch-action` não vale
     para o mouse, então o desktop não sente nada.

     E o navegador só toma o gesto quando há o que rolar: com a mão
     cabendo inteira não existe pan horizontal a fazer, e o arrasto
     continua ganhando em qualquer direção. */
  touch-action: pan-x;
}

/* Sobreposição em leque plano: colapsa conforme a mão cresce.

   A fração é da LARGURA DA CARTA, não um px fixo. Enquanto a mão tinha
   altura constante os dois eram a mesma coisa, e 26px eram 18% de uma
   carta de 143px. Com a alça (UX-13) a carta passou a ir de 30 a 230px, e
   26px fixos viram 87% de uma carta pequena: as sete cartas empilhavam
   numa só. Achado por screenshot, com a suíte verde.

   `--hand-card-w` é MEDIDA no DOM e escrita por `syncHandMetrics()` — é a
   mesma medida que a escada de LOD já fazia (ADR-0034), e não uma segunda
   conta. O padrão existe só para o JS falhar não empilhar a mão. */
.hand__cards > .card + .card {
  margin-left: calc(-1 * var(--hand-overlap, 0.18) * var(--hand-card-w, 143px));
}
.hand__cards:has(> .card:nth-child(9))  { --hand-overlap: 0.28; }
.hand__cards:has(> .card:nth-child(13)) { --hand-overlap: 0.39; }

.hand__empty {
  place-self: center;
  color: var(--fg-dim);
  font-size: 12px;
}

/* =============================================================
   Trilho de mini-mapas (Fase 2) — reservado desde já
   ============================================================= */
.minimap-rail {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-3);
  overflow-x: hidden;
  overflow-y: auto;
  background: var(--bg-surface);
  border-right: 1px solid var(--line);
  z-index: var(--z-rail);
  animation: rail-in 220ms var(--ease) both;
}

/* O SLOT NÃO CEDE — QUEM CEDE É O TRILHO  (DK-17, ADR-0227)

   Sem isto o `overflow-y: auto` de cima é letra morta: item flex encolhe
   por padrão, então com assentos demais quem cedia era o slot, o
   `scrollHeight` nunca passava do `clientHeight` e não havia o que rolar.

   E o aperto não parava numa caixa menor: a altura do mapa é FIXA — sai de
   `--minimap-vw` vezes o aspecto do plano (206 × 900/1600 = 115,9px) —,
   então o slot encolhia e o mapa não. Medido com quatro boards a 1500×859:
   o mapa passava 27px da própria caixa, cobria o rodapé de contagens e
   entrava 15px no board de baixo; a 844×379 eram 60px.

   A faixa já tinha esta linha desde a ADR-0157, pela mesma razão e com o
   mesmo sintoma — lá o assento nascia cortado. Aqui ela sobe para a regra
   base porque vale nos DOIS eixos: o slot tem tamanho próprio, e o trilho é
   quem rola. */
.minimap-rail > .minimap { flex: none; }
#app:not([data-phase="2"]) .minimap-rail { display: none; }

/* =============================================================
   A GAVETA DE BOARDS  (UX-44, ADR-0150 D1)

   Duas telas para o mesmo estado (`#app[data-boards]`), e o critério é o
   da ADR-0061: a tela larga MOSTRA, a estreita PEDE.

   DESKTOP — coluna recolhível. Recolher é levar `--minimap-w` a zero, que
   é literalmente o que a Fase 1 já faz: nenhum mecanismo novo, e por isso
   nada a manter em sincronia. A gaveta some da grade, não fica escondida
   ocupando lugar.
   ============================================================= */
#app[data-phase="2"]:not([data-boards="open"]) { --minimap-w: 0px; }
#app[data-phase="2"]:not([data-boards="open"]) .minimap-rail { display: none; }

/* O MAPA é o único controle da gaveta, e ele fica À ESQUERDA nos DOIS
   aparelhos (ADR-0157). Antes eram dois — chevron no desktop, mapa no
   celular —, e o dono do produto os unificou ao usar: dois desenhos para o
   mesmo gesto é o *igual desenhado diferente* da ADR-0083.

   ESQUERDA porque é o lado da gaveta: o controle fica onde a coisa que ele
   abre vai aparecer. No desktop ela é a coluna à esquerda; no celular ela
   cobre o campo a partir dali.

   ELE NÃO TEM MAIS GATE DE FASE. A prévia acabou (ADR-0157): os boards são
   parte do app, e o mapa está sempre lá. */
.battlefield__map {
  display: grid;
  position: absolute;
  top: 8px;
  /* A ESQUERDA DO CAMPO, e não a da grade: com a gaveta ABERTA no desktop,
     `left: 8px` punha o botão em cima do cabeçalho do primeiro board. Somar
     `--minimap-w` o mantém colado na borda do campo nos dois estados — ele
     marca de onde a gaveta sai, que é o que ele faz. Fechada, o token é
     zero e a conta devolve os mesmos 8px. */
  left: calc(var(--minimap-w) + 8px);
  /* Acima do trilho, e não dentro do campo: com o trilho aberto no celular
     ele precisa continuar clicável, senão o acesso rápido não tem volta
     (ADR-0156 D3). */
  z-index: calc(var(--z-rail) + 2);
  background: var(--bg-surface-2);
  border-color: var(--line);
}
#app[data-boards="open"] .battlefield__map {
  color: var(--fg);
  border-color: var(--accent);
}


@keyframes rail-in {
  from { opacity: 0; transform: translateX(-12px); }
  to   { opacity: 1; transform: none; }
}

/* =============================================================
   Visualizador de pilha (cemitério / exílio / grimório)
   ============================================================= */
.viewer {
  width: min(880px, 92vw);
  max-height: 78vh;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--bg-surface);
  color: var(--fg);
}
.viewer::backdrop { background: rgba(0, 0, 0, 0.6); }
.viewer__head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--line);
  font-weight: 650;
}

/* As setas da sequência da inspeção (DK-8, ADR-0145).

   DESABILITADAS e não escondidas nas pontas: escondê-las mudaria a largura
   do cabeçalho a cada passo, e o alvo do clique andaria debaixo do dedo de
   quem está justamente clicando ali repetidamente. Ocupar o lugar e não
   responder é a única forma de o alvo ficar parado.

   `tabular-nums` na contagem pela mesma razão: `9 de 47` e `10 de 47` não
   podem empurrar os botões ao lado. */
.viewer__step {
  min-width: 2.25rem;
  font-size: 1.1rem;
  line-height: 1;
}
.viewer__step[hidden] { display: none; }
.viewer__step:disabled { opacity: .35; cursor: default; }
.viewer__count {
  font-size: 12px;
  font-weight: 500;
  color: var(--fg-dim);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.viewer__count[hidden] { display: none; }
/* --- Configurações (ADR-0103) ---
   Mesma moldura do visualizador, como a Ajuda: cabeçalho, corpo rolável e
   "Fechar". Uma opção é uma LINHA — texto à esquerda, ação à direita —, e é
   essa linha que faz o painel crescer sem redesenho.

   Mais estreito que a Ajuda (520 contra 620): aqui não há tabela de duas
   colunas, e linha de texto muito longa é pior de ler. */
.settings { width: min(520px, 92vw); }
/* As duas classes, pela razão do `.viewer__body.token-print__body`: sozinha,
   esta regra nunca valeu — `.viewer__body` declara `display: flex` mais
   abaixo e vence pela ordem. Achado em 2026-08-19 (ADR-0113). */
.viewer__body.settings__body { display: block; }
.settings__row {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
  padding: var(--sp-3) 0;
  /* QUEBRA SOZINHA, e por medição (V-34, 2026-08-25): a 360px o diálogo
     tem 328px e a linha lado a lado espremia a explicação a **154px** —
     título e dois parágrafos numa coluna de dedo. O relato foi *"o layout
     lado a lado fica meio quebrado... imagino que fique melhor um abaixo
     do outro"*.

     `wrap` e não uma media query: o gatilho é o espaço que sobra, não a
     largura da tela. Um diálogo mais estreito, uma fonte maior ou um
     rótulo mais longo produzem o mesmo aperto sem a tela mudar de
     tamanho. */
  flex-wrap: wrap;
}
.settings__row + .settings__row { border-top: 1px solid var(--line); }
/* `min-width: 0` porque o mínimo automático de um item flex é o conteúdo:
   sem ele a explicação empurra o botão para fora em vez de quebrar linha.

   A BASE é quem dispara o `wrap` da regra acima: abaixo dela a explicação
   prefere levar o alvo para a linha de baixo a continuar encolhendo.

   ERA 220px — "o menor valor em que o título de duas palavras e a primeira
   linha ainda cabem sem quebrar palavra" —, e **subiu para 260 na ADR-0149,
   por uma razão que só apareceu quando o botão virou interruptor**: o botão
   escrito media ~105px e o interruptor mede 52, então a 328px (a largura
   medida no aparelho do dono do produto, V-34) a linha voltou a caber lado
   a lado, com 226px de explicação. Não é o aperto de 154px que criou a
   quebra, mas também não é a decisão que ele validou — e trocar um layout
   aprovado em uso por efeito colateral de outra mudança é o tipo de coisa
   que ninguém percebe até reclamar de novo.

   260 e não 240 porque é o mesmo número que a spec do V-34 exige da
   explicação: a base passa a dizer "só fico lado a lado se couber o que a
   spec chama de largura de LER". As duas param de poder divergir. */
.settings__text { flex: 1 1 260px; min-width: 0; }
.settings__title {
  margin: 0 0 var(--sp-2);
  color: var(--fg);
  font-size: 13px;
  font-weight: 650;
}
.settings__desc { margin: 0; color: var(--fg-muted); font-size: 12px; }
.settings__note {
  margin: var(--sp-2) 0 0;
  color: var(--fg-dim);
  font-size: 11px;
  line-height: 1.45;
}
/* O botão não encolhe: ele é o alvo, e um rótulo quebrado em duas linhas
   dentro de uma pílula é o primeiro sintoma de aperto no celular. */
.settings__row .btn { flex: none; }

/* --- O INTERRUPTOR (ADR-0149) ---

   Pedido do dono do produto em 2026-08-27: *"um switch de liga desliga fica
   melhor do que o botão com escrita"*. Vale para os DOIS de Configurações e
   não só para o novo — um switch ao lado de um botão escrito, na mesma tela
   e com a mesma semântica, é o "igual desenhado diferente" da ADR-0083.

   Continua sendo `<button>`, e o papel é `switch` com `aria-checked`: um
   botão de comando com rótulo `Desligado` diz o ESTADO no nome acessível,
   e o nome deveria dizer O QUE ELE CONTROLA. Com `role="switch"` o nome vem
   do `aria-label` e o estado do `aria-checked`, que é a separação certa.

   52x30 e não menor: é a medida de switch das duas plataformas, e é a
   menor em que o polegar acerta sem mirar. A pista inteira é o alvo. */
.switch {
  --switch-w: 52px;
  --switch-h: 30px;
  position: relative;
  flex: none;
  width: var(--switch-w);
  height: var(--switch-h);
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--bg-surface-3);
  cursor: pointer;
  transition: background 140ms var(--ease), border-color 140ms var(--ease);
}
/* O botão do interruptor. `::after` e não um elemento: o estado inteiro
   mora num atributo, então o JS não precisa conhecer a forma. */
.switch::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 3px;
  width: calc(var(--switch-h) - 10px);
  height: calc(var(--switch-h) - 10px);
  border-radius: 50%;
  background: var(--fg-dim);
  transform: translateY(-50%);
  transition: transform 140ms var(--ease), background 140ms var(--ease);
}
.switch[aria-checked="true"] {
  background: var(--accent-soft);
  border-color: var(--accent-line);
}
.switch[aria-checked="true"]::after {
  background: var(--accent);
  /* Anda a pista inteira menos o próprio diâmetro e as duas folgas. */
  transform: translateY(-50%) translateX(calc(var(--switch-w) - var(--switch-h)));
}
.switch:focus-visible {
  outline: 2px solid var(--accent-line);
  outline-offset: 2px;
}

/* --- O CONSOLE DO MODO DE DEPURAÇÃO (ADR-0137) ---

   Esta linha EMPILHA, e é a única das Configurações que faz isso: as
   outras são "explicação à esquerda, botão à direita", que é o desenho
   certo para uma ação de um toque. Aqui o conteúdo é um campo de texto que
   precisa da largura inteira — em 360px, lado a lado, sobrariam ~150px
   para colar um trecho de código, que é o mesmo que nada. */
.settings__row--stack {
  flex-direction: column;
  align-items: stretch;
  gap: var(--sp-2);
}
.debug__io {
  width: 100%;
  box-sizing: border-box;
  padding: var(--sp-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-field);
  color: var(--fg);
  /* Monoespaçada porque o conteúdo é CÓDIGO e SAÍDA: alinhamento de coluna
     é o que torna uma lista de eventos legível, e é exatamente isso que o
     V-33 pede para ler. */
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  line-height: 1.45;
  /* `resize: vertical` e não `none`: no desktop a saída longa é o caso
     comum, e arrastar a borda é mais barato que rolar dentro do campo. */
  resize: vertical;
  /* Sem quebra de palavra: uma linha de evento é uma unidade, e quebrá-la
     ao meio desalinha a coluna que a torna legível. Rola na horizontal. */
  white-space: pre;
  overflow: auto;
}
.debug__io--out { color: var(--fg-muted); }
.debug__bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}
/* Aqui o botão PODE crescer, ao contrário da regra acima: são três numa
   faixa própria, e em 360px eles ocupam a largura em vez de deixar um
   vazio à direita. */
.debug__bar .btn { flex: 1 1 auto; }

/* --- Impressão de um token, por CÓDIGO (DK-3, ADR-0113) ---

   Estreito como as Configurações, e pela mesma razão: aqui não há grade
   nem tabela. O que havia de grade — as 91 impressões de `Beast` — é
   justamente o que este diálogo existe para não mostrar. */
.token-print { width: min(440px, 92vw); }
/* As DUAS classes, e não só a de baixo: `.viewer__body` declara
   `display: flex` e mora MAIS ABAIXO neste arquivo — com a mesma
   especificidade, quem vence é a ordem, e um `display` escrito aqui não
   valeria nada. Foi assim que o `Buscar` e o preview apareceram lado a lado
   no primeiro print (ADR-0113). */
.viewer__body.token-print__body {
  display: grid;
  gap: var(--sp-3);
  /* `start` para o conteúdo não esticar quando o preview ainda não existe:
     o diálogo cresce com a resposta em vez de nascer com um buraco. */
  align-content: start;
  justify-items: stretch;
}
.token-print__hint {
  margin: 0;
  color: var(--fg-dim);
  font-size: 11px;
  line-height: 1.5;
}
.token-print__hint code {
  padding: 1px 4px;
  border-radius: 3px;
  background: var(--bg-surface-2);
  font-size: 11px;
}
/* Os dois botões não esticam: eles são alvos, e alvo da largura do diálogo
   num celular é o que faz o dedo acertar o errado. */
.viewer__body.token-print__body > .btn { justify-self: end; }

/* O preview, que é o que impede um dígito errado de virar token errado em
   silêncio. Ele NÃO tem altura reservada: enquanto não há resposta não há
   nada a dizer, e caixa vazia esperando conteúdo lê como defeito. */
.token-print__preview:empty { display: none; }
.token-print__card {
  display: flex;
  gap: var(--sp-3);
  align-items: center;
  margin: 0;
  padding: var(--sp-3);
  background: var(--bg-surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.token-print__card img,
.token-print__blank {
  flex: none;
  width: 84px;
  /* A proporção real da carta, como na paleta e na grade de impressões:
     sem ela a célula colapsa antes de a imagem chegar e o diálogo salta. */
  aspect-ratio: 146 / 204;
  border-radius: 4px;
  background: var(--bg-app);
  object-fit: cover;
}
.token-print__card figcaption {
  display: grid;
  gap: 2px;
  min-width: 0;
  color: var(--fg-muted);
  font-size: 12px;
}
.token-print__card strong { color: var(--fg); font-size: 13px; }
.token-print__code { color: var(--fg-dim); font-size: 11px; }

/* --- Aviso de download (ADR-0115) ---

   Fixo e centrado logo abaixo da topbar. `fixed` e não filho da `.app` de
   propósito: ele precisa aparecer nos DOIS modos, e em modo deck a `.table`
   está `visibility: hidden` (ADR-0101) — um filho dela seria invisível
   justamente no primeiro acesso, que é onde o download mais acontece. */
.loading-tag {
  position: fixed;
  top: calc(var(--topbar-h) + var(--sp-2));
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-overlay);
  /* Recado, não alvo: ele não pode roubar clique da carta que está embaixo.
     A mesa continua jogável enquanto baixa, e é o ponto da decisão. */
  pointer-events: none;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  max-width: min(92vw, 420px);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--bg-surface);
  color: var(--fg);
  font-size: 12px;
  line-height: 1.4;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
}
.loading-tag[hidden] { display: none; }

/* O ponto que gira. Anima com `transform`, que é a única propriedade que o
   compositor move sem re-layout — e some sozinho quando o usuário pede
   menos movimento. */
.loading-tag__spin {
  flex: none;
  width: 10px;
  height: 10px;
  border: 2px solid var(--line-strong);
  border-top-color: var(--accent-line, var(--fg));
  border-radius: 50%;
  animation: loading-spin 0.7s linear infinite;
}
@keyframes loading-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .loading-tag__spin { animation: none; border-top-color: var(--line-strong); }
}

/* A falha NÃO gira: ela não é espera, é resultado. Mesmo componente, outra
   cor, sem ponto animado. */
.loading-tag[data-tone="warn"] { border-color: var(--c-gold); }
.loading-tag[data-tone="warn"] .loading-tag__spin { display: none; }

/* --- Ajuda --- */
.help { width: min(620px, 92vw); }
/* Idem `.settings__body`: sozinha esta regra nunca valeu. Em flex-wrap a
   segunda seção da Ajuda saía com 423px contra os 506 da primeira. */
.viewer__body.help__body { display: block; }
.help__section + .help__section { margin-top: var(--sp-5); }
.help__title {
  margin: 0 0 var(--sp-2);
  color: var(--fg-dim);
  font-size: 11px;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.help__row {
  display: grid;
  grid-template-columns: 190px minmax(0, 1fr);
  gap: var(--sp-3);
  align-items: baseline;
  padding: 5px 0;
  border-top: 1px solid var(--line);
}
.help__keys { color: var(--fg); font-size: 12px; }
.help__desc { color: var(--fg-muted); font-size: 12px; }
.help__row kbd {
  display: inline-grid;
  place-items: center;
  min-width: 20px;
  padding: 2px 6px;
  background: var(--bg-surface-3);
  border: 1px solid var(--line-strong);
  border-bottom-width: 2px;
  border-radius: 4px;
  font: inherit;
  font-size: 11px;
  font-weight: 650;
}

/* O filtro OCUPA a folga do cabeçalho: ele entra no lugar do
   `<span style="flex:1">` que separava título e botão, então não custa
   linha nem altura. `min-width: 0` porque input tem largura intrínseca
   própria e sem isto ele empurra o "Fechar" para fora numa tela de 500px.
   `type="search"` é pelo ✕ nativo — o filtro é estado de VISÃO e some
   sozinho ao reabrir, mas limpá-lo sem apagar caractere por caractere é
   o gesto que se espera de um campo de busca. */
.viewer__filter {
  /* Não cresce: `flex: 1` o fazia comer toda a folga e virar um campo de
     590px no desktop, que lê como formulário em vez de filtro. Ele ENCOLHE
     (é o que salva o "Fechar" a 360px) e é empurrado para a direita pela
     margem automática, no lugar do `<span style="flex:1">` que os outros
     diálogos usam como espaçador. */
  flex: 0 1 220px;
  margin-left: auto;
  min-width: 0;
  padding: 5px var(--sp-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-app);
  color: var(--fg);
  font: 12px/1.4 inherit;
  font-weight: 400;
}
.viewer__filter:focus-visible { outline: 2px solid var(--accent-line); outline-offset: 1px; }
.viewer__filter::placeholder { color: var(--fg-dim); }

.viewer__body {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  padding: var(--sp-4);
  overflow-y: auto;
  max-height: 62vh;
}

/* A GRADE do visualizador de pilha (ADR-0081), e as duas coisas que o
   teste em aparelho pediu.

   1. CENTRADA. Com `flex-start` a sobra da última coluna ficava toda de um
      lado — medido a 360px, 2 cartas por linha, 0px à esquerda e 46 à
      direita. A grade tem largura de carta fixa e o contêiner não, então a
      sobra existe sempre; o que se escolhe é onde ela fica.

   2. O DEDO ROLA A GRADE INTEIRA, e não só a faixa da barra de rolagem.
      `.card` declara `touch-action: none` (card.css), que é o certo em toda
      zona onde ela arrasta — e aqui ela NÃO arrasta: dentro do diálogo o
      `pointerdown` desiste, porque o fantasma vive fora do top layer. Com o
      dedo tomando tudo, o único vão que rolava eram os ~8px da barra à
      direita. É o mesmo conflito que a mão teve (UX-26), e aqui é mais
      barato de resolver: lá havia um arrasto real a preservar, aqui não há
      nenhum, então o eixo do `overflow` volta inteiro para o dedo.

      `pan-y` e não `auto`: o que falta é a rolagem vertical, e só ela. O
      duplo toque e a pinça do navegador continuam fora, como em toda a mesa
      (ADR-0076).

      O menu por toque longo sobrevive: rolar passa da tolerância de 12px e
      desarma, e quando o navegador toma o gesto ele emite `pointercancel`,
      que já cai no `endDrag` — que desarma também. */
.viewer__body--cards { justify-content: center; }
.viewer__body--cards > .card { touch-action: pan-y; }

/* Zona filtrada até não sobrar nada. Sem isto o corpo fica vazio e o
   visualizador parece ter perdido as cartas. */
.viewer__none {
  padding: var(--sp-2) 0;
  color: var(--fg-dim);
  font-size: 12px;
}

/* =============================================================
   Inspeção ampliada da carta (FN-4)

   Duas colunas: a carta inteira à esquerda, o texto legível à direita.
   A imagem já traz o texto impresso, mas a 300px ele fica no limite —
   quem se lê de fato é a coluna da direita. A imagem é a identificação.
   ============================================================= */
.inspect { width: min(760px, 94vw); }

.inspect__body {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--sp-4);
  align-items: start;
  flex-wrap: nowrap;              /* .viewer__body é flex-wrap: wrap */
}

.inspect__art { position: relative; width: 300px; max-width: 38vw; }

/* O VERSO, sobre a arte da inspeção (UX-49, ADR-0224). Aqui há espaço de
   sobra, então ele é maior que o da carta do campo — a régua de 24px é o
   PISO do alvo, não o tamanho certo em toda superfície. */
.inspect__flip {
  position: absolute;
  left: 50%;
  bottom: 10px;
  transform: translateX(-50%);
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.62);
  box-shadow: inset 0 0 0 1px var(--line-strong);
  color: #fff;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.inspect__flip:hover { background: var(--accent); }
.inspect__flip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.inspect__img {
  display: block;
  width: 100%;
  border-radius: 14px;            /* o canto da carta impressa */
  box-shadow: var(--sh-2);
}
.inspect__noimg {
  display: grid;
  place-items: center;
  aspect-ratio: 63 / 88;
  border: 1px dashed var(--line-strong);
  border-radius: 14px;
  background: rgba(0, 0, 0, 0.25);
  color: var(--fg-dim);
  font-size: 12px;
  text-align: center;
}

.inspect__meta { display: grid; gap: var(--sp-2); min-width: 0; }
.inspect__line {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-width: 0;
}
.inspect__name {
  flex: 1;
  min-width: 0;
  font-size: 17px;
  font-weight: 650;
  letter-spacing: -0.01em;
}
/* O nome ORACLE, sob o impresso (PA-7). A forma é a da Scryfall: o que está
   impresso em cima, no tamanho do título, e o nome de regras embaixo, menor
   e apagado.

   A margem negativa é o par do `gap` do `.inspect__meta`: sem ela o grid
   abre 8px entre as duas e a segunda linha lê como OUTRO campo, quando ela
   é o subtítulo do nome. Encostada, o par se lê de uma vez.

   O custo em altura foi MEDIDO antes de escrever isto, e é por isso que ele
   cabe: nas janelas de duas colunas a coluna do texto tem 246px contra 419px
   da arte — 173px de folga — e a linha custa 23px sem fazer o corpo rolar.
   Na janela estreita, onde as colunas empilham, o corpo já rolava 160px e
   passa a rolar 183. Ver ADR-0165. */
.inspect__oracle {
  margin-top: calc(var(--sp-2) * -1 + 2px);
  color: var(--fg-muted);
  font-size: 12.5px;
}

.inspect__type {
  color: var(--fg-muted);
  font-size: 12.5px;
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--line);
}
.inspect__type > span:first-child { flex: 1; min-width: 0; }

/* O ponto do FN-4: aqui o texto de regras é lido, não decorado. */
.inspect__rules { font-size: 13.5px; line-height: 1.55; }
.inspect__rules p { margin: 0 0 var(--sp-2); }
.inspect__rules p:last-child { margin-bottom: 0; }
.inspect__empty { color: var(--fg-dim); font-style: italic; }
/* Separador de face — o catálogo une as faces com "//" (ADR-0014). */
.inspect__face { height: 0; margin: var(--sp-3) 0; border: 0; border-top: 1px solid var(--line); }

.inspect__state {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding-top: var(--sp-2);
  border-top: 1px solid var(--line);
}
.inspect__state span {
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--bg-surface-2);
  box-shadow: inset 0 0 0 1px var(--line);
  color: var(--fg-muted);
  font-size: 11px;
}

/* Abaixo disso a coluna de texto fica estreita demais para ler regra. */
@media (max-width: 640px) {
  .inspect__body { grid-template-columns: minmax(0, 1fr); }
  .inspect__art { width: 220px; max-width: 60vw; justify-self: center; }
}

/* =============================================================
   Importar decklist
   ============================================================= */
.catalog-tag {
  flex: none;
  font-size: 11px;
  color: var(--fg-dim);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* Etiqueta da partida salva. Os três tons são a política de falha da
   ADR-0042 virando pixel: "salva" é discreta e quase invisível, porque
   funcionar não precisa de atenção; "não está salvando" precisa. */
.game-tag[hidden] { display: none; }
.game-tag::before { content: "· "; }
.game-tag[data-tone="warn"] { color: var(--zone-exile); }
.game-tag[data-tone="danger"] { color: var(--danger); font-weight: 650; }

.import {
  width: min(720px, 94vw);
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--bg-surface);
  color: var(--fg);
}
.import::backdrop { background: rgba(0, 0, 0, 0.6); }

.import__form { display: flex; flex-direction: column; }

.import__head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--line);
  /* Mesma razão do `wrap` do `.import__foot`, e o mesmo lugar onde ela
     aparece: abaixo de 500px, que é onde o arnês não chega (ADR-0072).

     A dica cresceu em 2026-08-24 para nomear as três ferramentas, e a
     palavra mais longa dela (`LigaMagic`) empurra a largura MÍNIMA da
     linha — medido a 360px por sonda: o `Cancelar` ia parar em x=359 num
     diálogo que termina em 344. Sem quebrar, ele saía pela borda.

     Medido nas três formas: com a dica antiga cabia (337), com qualquer
     dica que cite as ferramentas não cabe, e sem dica nenhuma cabe (327).
     Encurtar o texto NÃO resolve — o que estoura é a palavra, não a frase. */
  flex-wrap: wrap;
}
.import__hint { font-size: 12px; color: var(--fg-dim); }

.import__text {
  min-height: 240px;
  max-height: 40vh;
  margin: var(--sp-4) var(--sp-4) 0;
  padding: var(--sp-3);
  resize: vertical;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm, 4px);
  background: var(--bg-app);
  color: var(--fg);
  font: 12px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace;
  tab-size: 2;
}
.import__text:focus-visible { outline: 2px solid var(--accent-line); outline-offset: 1px; }

/* Altura fixa para o relatório não empurrar o botão de importar a cada
   tecla digitada — o alvo de clique tem que ficar parado. */
.import__report {
  min-height: 76px;
  max-height: 26vh;
  overflow-y: auto;
  margin: var(--sp-3) var(--sp-4) 0;
  font-size: 12px;
  line-height: 1.6;
}
.report__head { color: var(--fg); }
.report__deck { color: var(--fg-muted); }

.report__item {
  padding-left: var(--sp-3);
  border-left: 2px solid var(--line-strong);
  color: var(--fg-muted);
}
/* Aviso e erro são informativos, não impeditivos — daí o peso visual
   contido: cor na borda, texto em tom normal. Ver arquitetura.md §1. */
.report__item[data-kind="warn"]  { border-color: var(--zone-exile); }
.report__item[data-kind="error"] { border-color: var(--danger); }
.report__item[data-kind="ok"]    { border-color: var(--ok); color: var(--fg-dim); }

.import__foot {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-4);
  /* O rodapé passou a ter DUAS saídas (RV-3), e quem paga o aperto é a
     NOTA: ela é `flex: 1; min-width: 0`, então sem quebrar ela some antes
     de qualquer botão sair do lugar. Medido a 360px — sem `wrap` os dois
     botões cabem na linha e a nota fica com ~15px, ilegível. E é justamente
     ela que explica por que o botão continua clicável com aviso na tela
     (ADR-0016), o que a ADR-0016 já mandava manter fora da área rolável.
     Abaixo de 500px, que é onde o arnês não chega (ADR-0072). */
  flex-wrap: wrap;
}
.import__note {
  flex: 1;
  min-width: 0;
  font-size: 12px;
  color: var(--fg-dim);
  font-style: italic;
}
.import__note[hidden] { display: none; }
/* Sem a nota, o botão continua encostado à direita. */
.import__foot { justify-content: flex-end; }
.import__foot .import__note:not([hidden]) ~ .btn { flex: 0 0 auto; }
.import__foot .btn:disabled { opacity: 0.45; cursor: not-allowed; }

/* A linha de configuração do import — formato + os dois comandantes
   (ADR-0130). Reusa `.token__field` inteiro; só a distribuição é dela.

   FLEX com `wrap` em vez de grade de colunas, e é a mesma razão do
   `.import__foot` logo acima: os três campos cabem lado a lado num
   diálogo de 560px e quebram sozinhos a 360px, sem breakpoint e sem o
   arnês precisar alcançar abaixo de 500px (ADR-0072). O formato é o
   campo que nunca sai, e é o estreito — a mesma escolha que a ADR-0109
   fez pela Quantidade no "Criar token".

   `padding-top` só: o `.import__report` logo abaixo já traz a margem. */
.import__setup {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4) 0;
}
/* Quinta vez que a mesma pegadinha aparece (`.pile`, `.action-bar__btn`,
   `.token__field`, `.token__grid`): `display: flex` vence `hidden`. */
.import__setup[hidden] { display: none; }
.import__setup .token__field { flex: 1 1 150px; }
/* Descendente, e não `.import__field--fmt` sozinho: a regra acima tem
   especificidade (0,2,0) e venceria — o campo estreito nasceu largo, com o
   Comandante 1 espremido na borda e o 2 quebrando sozinho para a linha de
   baixo. Só o print de 1500px mostrou; a suíte mede `hidden`, não largura. */
.import__setup .import__field--fmt { flex: 0 1 140px; }

/* =============================================================
   Criar token — reaproveita o esqueleto do diálogo de importação
   ============================================================= */
.token { width: min(520px, 94vw); }

.token__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 120px;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-4) 0;
}
/* A grade do "Criar token" inverte as colunas porque a QUANTIDADE vem
   primeiro (UX-37, ADR-0109) — ela é o campo que nunca sai, e é a estreita.
   De quebra a Cor herda a célula larga, que é onde o `<select>` de
   "Incolor / artefato" cabia mal. O diálogo de pedido (`#askGrid`) fica com
   a ordem de sempre: lá o campo largo é o primeiro. */
.token__grid--qty-first { grid-template-columns: 120px minmax(0, 1fr); }
/* E UMA COLUNA SÓ quando o pedido não tem número (DK-22 fatia 3, ADR-0231):
   a segunda coluna ficava vazia e o campo de texto parava 120px antes da
   borda. Quem liga é o `prompt()`, que sabe quais campos o pedido tem. */
.token__grid--single { grid-template-columns: minmax(0, 1fr); }

/* ---------------------------------------------------------------
   A paleta de tokens do deck (RV-1, ADR-0106)

   Mesma grade do seletor de impressão (`auto-fill` com mínimo em vez de
   número de colunas): serve 520px de diálogo e 360px de celular sem
   breakpoint. Teto de altura porque um deck de tokens pode produzir uma
   dúzia, e o diálogo não pode empurrar os campos para fora da tela.
   --------------------------------------------------------------- */
/* A CAIXA é a REGIÃO, não a paleta (JG-24). O teto, a rolagem e a moldura
   desceram para cá porque agora há dois conteúdos dentro — a paleta do deck
   e os marcadores genéricos —, e é esta caixa que absorve o crescimento do
   diálogo quando os campos de face entram (ADR-0109 / JG-15). Com o teto na
   paleta, os marcadores ficavam FORA do que encolhe e o diálogo voltava a
   crescer. */
.token__pieces {
  max-height: 42vh;
  overflow-y: auto;
  margin: var(--sp-4) var(--sp-4) 0;
  padding: var(--sp-2);
  background: var(--bg-surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}

.token__palette {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  gap: var(--sp-2);
  align-content: start;
}
.token__palette[hidden] { display: none; }

/* O cabeçalho de seção da paleta (JG-15). Ele é um ITEM da mesma grade,
   atravessando todas as colunas — e não uma grade aninhada por seção. Com
   grades separadas cada seção calcularia o próprio `auto-fill`, e duas
   seções de larguras diferentes desalinhariam as colunas entre si; a peça
   de uma linha ficaria maior que a da outra sem motivo.

   O primeiro não leva respiro em cima: ali já está o `padding` da paleta, e
   somar os dois abriria um buraco no topo. */
.token__sec {
  grid-column: 1 / -1;
  margin: var(--sp-3) 0 0;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--fg-dim);
}
.token__sec:first-child { margin-top: 0; }

/* ---------------------------------------------------------------
   A SEÇÃO DE MARCADORES GENÉRICOS (JG-24)

   Ela é IRMÃ da paleta, não parte dela, e a razão é de conteúdo: a paleta
   sai do que o DECK produz e some com deck que não produz nada; esta não
   depende de deck nenhum, então ela nunca se esconde. Mesa recém-aberta já
   oferece as quatro fichas.

   Reusa o `.token__sec` do cabeçalho — é a mesma hierarquia visual das
   outras três seções, e o dono do produto pediu a ficha "junto aos tokens
   e emblemas".
   --------------------------------------------------------------- */
/* Sem moldura própria: ela mora DENTRO da `.token__pieces`, que já é a
   caixa. Só o respiro que a separa da paleta de cima — e ele só existe
   quando há paleta, senão abriria um buraco no topo, que é a mesma regra
   do `.token__sec:first-child`. */
.token__markers { margin-top: var(--sp-3); }
.token__palette[hidden] + .token__markers { margin-top: 0; }
.token__marker-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
}

/* 32px é a régua de dedo deste projeto (ADR-0189): a ficha é o alvo, e ela
   é redonda como a peça que vai nascer — o botão PARECE o que ele cria, que
   é o que dispensa rótulo em cada um (o nome vive no `title`/`aria-label`). */
.token__marker {
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--marker-fill);
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.55),
              inset 0 -3px 6px rgba(0, 0, 0, 0.35);
  cursor: pointer;
}
.token__marker:hover,
.token__marker:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* A cor entra por CHAVE, como na ficha do campo (ADR-0019). Os valores são
   os mesmos do `card.css` de propósito: o botão da paleta e a peça que ele
   cria têm de ser a MESMA cor, senão a escolha mente. */
.token__marker[data-color="red"]    { --marker-fill: #d2423c; }
.token__marker[data-color="blue"]   { --marker-fill: #3f7fd0; }
.token__marker[data-color="green"]  { --marker-fill: #46a05a; }
.token__marker[data-color="yellow"] { --marker-fill: #d8a521; }

.token__marker-hint {
  margin: var(--sp-2) 0 0;
  font-size: 11px;
  color: var(--fg-dim);
}

.token__opt {
  display: grid;
  gap: 2px;
  padding: var(--sp-1);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--fg-muted);
  font: inherit;
  font-size: 11px;
  text-align: center;
  cursor: pointer;
}
.token__opt:hover,
.token__opt:focus-visible { border-color: var(--line-strong); }
/* O escolhido. Borda E marca de texto, pela razão da ADR-0099: cor sozinha
   não sobrevive a daltonismo nem a tela ruim — e aqui ela nem seria só cor
   ruim, seria cor CONFUNDÍVEL, porque o anel de foco cai no mesmo lugar. */
.token__opt[aria-current="true"] { border-color: var(--accent-line, var(--fg)); }
.token__opt-on { display: none; color: var(--fg); }
.token__opt[aria-current="true"] .token__opt-on { display: block; }
.token__opt[aria-current="true"] .token__opt-pt { display: none; }
.token__opt:disabled { opacity: 0.45; cursor: not-allowed; }

.token__opt img,
.token__opt-blank {
  width: 100%;
  /* A proporção real da carta, como na grade de impressões: sem isto a
     célula colapsa antes de a imagem preguiçosa chegar e a grade salta. */
  aspect-ratio: 146 / 204;
  border-radius: 4px;
  background: var(--bg-app);
  object-fit: cover;
}
.token__opt-name { color: var(--fg); }

/* "Personalizado" é o token que o deck NÃO prevê (ADR-0028), e a moldura
   tracejada é o que diz isso sem uma palavra a mais: é o lugar vazio da
   paleta. Sem glifo — o mapa de ícones não tem um para "novo", e inventar
   um caractere no markup é exatamente o que a ADR-0070 proíbe. */
.token__opt--custom .token__opt-blank {
  border: 1px dashed var(--line-strong);
  background: none;
}
/* Quarta vez que a mesma pegadinha aparece (`.pile`, `.action-bar__btn`,
   `.token__field`): `display: grid` vence o atributo `hidden`. Aqui é o
   pedido SEM campo nenhum — a confirmação da ADR-0050 —, que sem esta
   linha deixaria a margem da grade vazia entre a nota e o título. */
.token__grid[hidden] { display: none; }

.token__field {
  display: grid;
  gap: 4px;
  min-width: 0;
  color: var(--fg-dim);
  font-size: 11px;
}
/* Terceira vez que a mesma pegadinha aparece (`.pile`, `.action-bar__btn`):
   o `display: grid` acima vence o atributo `hidden`, e o diálogo de pedido
   esconde o campo de rótulo quando o comando só quer um número. */
.token__field[hidden] { display: none; }

/* O campo DESLIGADO, e sobrou uma situação só: "somar à pilha", onde a face
   vem da pilha e o gatilho é o checkbox logo acima dos campos (ADR-0028).
   A opacidade morava na GRADE INTEIRA até a ADR-0109 — e a Quantidade mora
   lá dentro, continuava habilitada, e ficava com cara de desligada. */
.token__field[data-idle="1"] { opacity: 0.45; }

.token__field small { color: var(--fg-dim); opacity: 0.7; }
.token__field input,
.token__field select {
  /* SEM ISTO O CAMPO TRANSBORDA A CÉLULA (V-125, achado em uso). Um
     `<input>` tem largura intrínseca própria (~163px medidos), e item de
     grade não encolhe abaixo do min-content — o `min-width: 0` do
     `.token__field` acima protege a CÉLULA, não o campo dentro dela.

     Ficava invisível enquanto o P/T morava na coluna larga; a inversão de
     colunas da ADR-0109 o pôs na de 120px, e desde então ele vazava 43px
     por cima do `<select>` de Cor — que, sendo posterior no DOM, ganhava o
     hit test e tornava o fim do campo inclicável.

     A suíte não podia ver: ela mede `hidden`, não largura. É a mesma
     armadilha que o comentário do `.import__field--fmt` já registrava,
     duas dezenas de linhas acima, e que só o print pegou lá também. */
  min-width: 0;
  padding: 7px var(--sp-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-app);
  color: var(--fg);
  font: 13px/1.4 inherit;
}
.token__field input:focus-visible,
.token__field select:focus-visible { outline: 2px solid var(--accent-line); outline-offset: 1px; }
.token__field input:disabled,
.token__field select:disabled { cursor: not-allowed; }

/* DESABILITADO TEM DE PARECER DESABILITADO (JG2-1 emenda, ADR-0246).

   A regra acima trocava só o CURSOR, e o `color: var(--fg)` da base
   mantinha o campo com cara de operável — o print mostrou o seletor de
   formato travado desenhando texto branco e chevron normais, exatamente
   como um livre. Desabilitar sem parecer desabilitado é o `UX-37`
   INVERTIDO: lá o campo parecia inerte e não estava, aqui ele está e não
   parece. Mesma família, mesmo preço.

   ESCOPADO NO SELETOR DE FORMATO, e não em todo `.token__field`: o diálogo
   de token tem tratamento PRÓPRIO para campo inerte — o `[data-idle="1"]`
   logo acima, com a opacidade de 0.45 que a ADR-0028/0109 escolheu — e
   mudar aquilo de carona neste item seria regredir uma decisão de desenho
   que não está em discussão.

   A opacidade é a mesma do `[data-idle]`, e isso é de propósito: o app já
   tem UM jeito de dizer "este campo não responde agora", e um segundo
   obrigaria quem olha a aprender dois. */
.import__field--fmt select:disabled {
  opacity: 0.45;
  color: var(--fg-dim);
}

.token__merge {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: var(--sp-4) var(--sp-4) 0;
  padding: var(--sp-2) var(--sp-3);
  background: var(--bg-surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--fg-muted);
  font-size: 12px;
  cursor: pointer;
}
.token__merge[hidden] { display: none; }

/* Diálogo de pedido (rótulo e/ou número) — o mesmo esqueleto, mais
   estreito: ele nunca tem mais de dois campos. */
.ask { width: min(420px, 94vw); }

/* =============================================================
   Responsivo
   ============================================================= */
@media (max-width: 1180px) {
  :root { --rail-w: 132px; --minimap-vw: 158; }
  #app[data-phase="2"] { --minimap-w: 196px; }
  .topbar { gap: var(--sp-2); }
  .topbar__brand small { display: none; }
}

/* A barra de fases é a única coisa que pode encolher: vida, veneno e
   ações continuam clicáveis em qualquer largura. */
@media (max-width: 1000px) {
  /* A MARCA FICA, e o nome sai. Aqui morava um `.topbar__brand {
     display: none }`: abaixo de 1000px a barra é apertada e a identidade era
     a primeira coisa a ser cortada. O que mudou é que ela virou a PORTA DE
     VOLTA para o site (ADR-0279) — escondê-la tirava a saída junto, e sair da
     mesa no celular passava a não ter caminho nenhum.

     O custo é medido, não estimado: a marca sozinha ocupa 27px de largura
     contra os 68px do logo inteiro, e é ela que o `<picture>` do `index.html`
     carrega abaixo deste mesmo 1000px — o corte do markup e o desta folha são
     o mesmo número de propósito, porque são a mesma decisão. */
  /* O TOKEN existe para a suíte perguntar ao CSS em vez de repetir o 1000px
     (`00-common.js`, FMT). É ele que deixa uma spec provar que este corte e o
     `<source media>` do `index.html` estão no mesmo número — se alguém mudar
     um só, a asserção acusa. */
  :root { --fmt-brand-mark: 1; }
  .topbar__logo { height: 34px; }
  /* O rótulo vira ÍCONE, e NÃO some (ADR-0060). Sumindo, "Vida" e
     "Veneno" viravam duas caixas `− 20 +` idênticas — e pior: desde a
     ADR-0057 o rótulo do contador de jogador É o gatilho do menu de
     espécie, então escondê-lo tirava do celular a única forma de trocar
     de marcador. Achado em aparelho, não em screenshot. */
  .counter__word { display: none; }
  .counter__icon { display: inline; }
  .counter { padding-left: 6px; }
}
/* O trilho vira FAIXA quando a largura é o recurso escasso, e continua
   COLUNA quando o escasso é a altura. Até 2026-08-14 a condição era só
   `max-width: 860px`, e o que ela media de verdade era "isto é um
   celular" — proxy que funcionou enquanto todo aparelho estreito estava
   em pé. Deitado ele tem largura de tablet (844px) e altura de nada
   (309px), e a faixa cobrava 68px do recurso que já não havia.

   `orientation: portrait` é a metade que faltava, e ela não é um segundo
   breakpoint: é a pergunta que os 860px estavam tentando fazer. A faixa
   troca altura por largura, e essa troca só é boa em pé. Ver UX-25.

   Consequência aritmética, de graça: `chromeOutsideHand()` (ADR-0052)
   pergunta ao CSS se o trilho é `row` em vez de repetir o breakpoint em
   JS. Deitado ele deixa de ser, o custo fixo cai de ~120px para 52, e o
   teto da mão devolve altura ao campo sem uma linha de JS nova. */
@media (max-width: 860px) and (orientation: portrait) {
  /* trilhos viram faixas horizontais; o battlefield continua intocado —
     o plano lógico não muda, só o divisor de --bf-scale. */
  .table {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr) auto;
  }
  /* O trilho vira FAIXA HORIZONTAL, e aí os eixos de rolagem TROCAM.

     Sem esta troca ele herdava `overflow-x: hidden` da regra de cima, que é
     a certa para a coluna do desktop e a errada aqui: o segundo assento
     nascia cortado pela direita e o terceiro era inalcançável. Achado em
     aparelho em 2026-08-27, no V-59 — *"o da direita está ficando cortado"*.

     O `flex: none` que a faixa exigia — sem ele o slot encolhe até deformar
     em vez de deixar a faixa rolar — MORA NA REGRA BASE desde o DK-17
     (ADR-0227): a coluna precisava dele pela mesma razão, e o sintoma lá
     era o mapa desenhando por cima do board de baixo.

     E o `scroll-snap` é o que responde ao pedido literal — *"que seja
     possível ver os painéis completos"*: a faixa para com um assento
     inteiro à vista, nunca com meio. */
  .minimap-rail {
    grid-column: 1 / 2;
    grid-row: 1;
    flex-direction: row;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x proximity;
    overscroll-behavior-x: contain;   /* não puxa a navegação do aparelho */
  }
  /* `flex: none` saiu daqui: subiu para a regra base (DK-17, ADR-0227),
     porque a coluna precisava dele pela mesma razão. Repetir seria a
     segunda cópia da mesma decisão. Fica o que É da faixa. */
  /* E NA FAIXA O SLOT VALE O QUE O PREVIEW VALE (DK-22 fatia 3, ADR-0231).

     Aqui a largura é o eixo PRINCIPAL do flex, então a base do slot é
     `auto` = `max-content`: com nome comprido ele crescia para caber o nome
     inteiro, e o `ellipsis` nunca entrava em ação. Medido a 412px com o
     `content-visibility` desligado: **544px numa tela de 412**, com o nome
     INTEIRO. O `min-width: 0` da cabeça tirou o PISO e não mexe na base.

     `min-content` descreve a peça: o mapa tem largura própria
     (`--minimap-vw`) e o nome pode encolher a zero, então o mínimo do slot
     É o preview — 172px, o mesmo `--minimap-vw + 14px` que o `+` da faixa
     já escreve à mão no `minimap.css`.

     SÓ NA FAIXA, e isso foi medido depois de quebrar: na COLUNA o slot é
     esticado pelo trilho no eixo cruzado, e `min-content` (220px) passa a
     estourar um trilho de 195px a 900×719 — a bateria acusou nas duas
     janelas de coluna estreita.

     E ISTO ESTAVA INVISÍVEL PARA A SUÍTE: sob relógio virtual o
     `content-visibility: auto` nunca pinta o slot, então toda asserção lia
     a caixa RESERVADA (204px) em vez da real. Quem viu foi o dono do
     produto, no aparelho. */
  .minimap-rail > .minimap {
    width: min-content;
    scroll-snap-align: start;
  }
  .battlefield  { grid-column: 1 / 2; grid-row: 1; }

  /* ===========================================================
     O MAPA SOBRE O CAMPO  (UX-44, ADR-0150 D1/D3)

     A colisão de grade acima — trilho e campo na MESMA célula — deixou de
     ser defeito e virou o mecanismo. O dono do produto reenquadrou o V-59:
     *"faz sentido os mini maps comerem a visão do campo de batalha, mas
     como um acesso rápido"*. Então eles empilham de propósito, e o que
     decide quem está por cima é ter sido PEDIDO.

     O CAMPO CONTINUA NA GRADE, e isto não é descuido: `display: none` nele
     faria o `fitCanvas` medir 0×0 e o enquadramento voltar errado ao
     fechar a gaveta — é a armadilha que a ADR-0094/0095 registrou para o
     painel de deck, na mesma grade. O trilho cobre; o campo mantém a caixa.
     =========================================================== */
  .minimap-rail {
    background: var(--bg-surface);
    z-index: calc(var(--z-rail) + 1);
  }
  /* Fechada ela some, e quem faz isso é a regra GERAL lá em cima — repetir
     aqui seria a segunda cópia da mesma decisão, e foi medido que é: tirar
     a duplicata não muda uma asserção sequer. Aberta, ela ocupa a célula
     inteira, porque a coluna de `--minimap-w` é do desktop e aqui não vale. */
  #app[data-boards="open"] .minimap-rail { width: auto; }

  /* No RETRATO a grade tem uma coluna só e `--minimap-w` não é usado por
     ela — mas o token continua com valor, então a conta de cima empurraria
     o botão 196px para dentro. Aqui ele volta a colar na borda da tela. */
  .battlefield__map { left: 8px; }
  /* A PILHA PARA DE ENCOLHER QUANDO O CONTEÚDO PARA DE CABER (UX-48, ADR-0196).

     Aqui a pilha era `flex: 1` — grow e SHRINK a partir de base zero —, e a
     faixa dividia a largura da tela pelo número de zonas, doa a quem doer. A
     ADR-0193 pôs um mínimo CALIBRADO (112px) e a ADR-0195 o deu só ao
     grimório; as duas erraram pelo mesmo motivo, e a medição de 2026-09-04
     mostrou onde: **as três mediram a faixa RECOLHIDA**, e o dono do produto
     joga com ela ABERTA, onde a pilha desenha a carta do topo.

     Aberta, o `max-width: 100%` do slot esmagava a arte: proporção medida de
     **0,433** contra as **0,716** naturais da carta, nas quatro zonas
     estreitas. *"Ficou desproporcional (…) os desenhos de carta de
     comandante, exílio e cemitério ficaram espremidos."*

     `min-width: auto` É A CORREÇÃO DE RAIZ, e ela não escolhe número nenhum:
     o mínimo automático de um item flex é o `min-content` dele, então a pilha
     para de encolher exatamente quando o conteúdo dela para de caber. O
     `--pile-min-w` calibrado SAIU do projeto — ele existia para adivinhar
     este número.

     O `min-width: 0` da regra base é o que precisava sair daqui: ele existe
     para o caso em que encolher é o certo, e na faixa nunca foi.

     ROLAGEM E NÃO QUEBRA EM DUAS LINHAS — decisão do dono do produto em
     2026-09-04, reafirmada em 2026-09-04 ao pedir para *"não encarar a barra
     de rolagem horizontal como algo horrível"*. Quebrar em duas linhas
     custaria 36px de faixa (41 → 77) e 7,5% do campo; rolar não custa campo.

     O `scroll-snap` é o mesmo tratamento que a gaveta de mini-mapas ganhou no
     V-59, pelo mesmo pedido — *"que seja possível ver os painéis completos"*:
     a faixa para com uma zona INTEIRA à vista, nunca com meia. E o
     `overflow-y: hidden` é obrigatório: a regra base declara `overflow-y:
     auto` para a coluna, e numa faixa de 41px isso vira barra vertical. */
  .rail {
    grid-column: 1 / 2;
    grid-row: 2;
    flex-direction: row;
    border-left: 0;
    border-top: 1px solid var(--line);
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x proximity;
    /* O snap alinha com a borda do SCROLLPORT, não com a do conteúdo, então
       sem isto ele come o `padding` da esquerda e o primeiro filho encosta na
       borda da tela. Medido: `scrollLeft` parava em 4 em vez de 0. */
    scroll-padding-inline: var(--sp-1);
    overscroll-behavior-x: contain;   /* não puxa a navegação do aparelho */
  }
  .pile { flex: 1 0 auto; min-width: auto; }

  /* A BARRA DA PILHA É DUAS METADES, e é ela que garante a invariante do
     JG-18 sem número (UX-48, ADR-0196).

     Pedido do dono do produto em 2026-09-04: *"os ícones podem ser
     centralizados — metade dela com o ícone e a outra metade com o olho +
     números"*. Ele estava consertando o VÃO que a ADR-0189 criou: com
     `space-between` e a pilha larga, o ícone colava na esquerda, a cápsula na
     direita e sobravam **71px vazios no meio**.

     E ela vale mais do que o desenho: com duas colunas IGUAIS, o alvo da ação
     principal é, por construção, pelo menos tão largo quanto a cápsula que
     mora na outra metade. É a invariante da ADR-0189 (`comprar >= ver`)
     deixando de precisar de `--pile-min-w` para valer — e valendo em TODA
     zona, não só no grimório.

     EM TODAS AS ZONAS, e é decisão do dono do produto contra a alternativa
     medida: só no grimório ele ficaria 44% mais largo que as outras, mas
     qualquer pilha que crescesse por sobra de espaço voltaria a ter vão. Com
     as metades em todas, o desenho é um só e a sobra é sempre distribuída.

     Medido a 360px, faixa aberta, Commander com sideboard: a carta volta a
     0,716 nas cinco, o vão do grimório cai de 71 para 8px, `comprar` 57
     contra `ver` 53, e cabem 2 das 5 com rolagem. */
  .pile__meta {
    display: grid;
    grid-template-columns: 1fr 1fr;
    justify-items: center;
  }
  /* TODO FILHO É ALVO DE SNAP, e não só a pilha — a sonda achou por quê.

     O primeiro filho do trilho é o BOTÃO DE RECOLHER, e com o snap só nas
     pilhas o `scrollLeft: 0` deixa de ser uma posição válida: o navegador
     encosta na primeira pilha (32px) e o botão sai de vista sozinho, sem
     ninguém rolar. Pior, o efeito compõe — quando a zona de COMANDO aparece,
     ela entra ANTES das outras, a ancoragem de rolagem soma a largura dela e
     a faixa vai parar em 148px: a mesa abre com a primeira zona escondida.

     Medido em 2026-09-04: sem snap, `scrollLeft` fica em 0 e não se mexe; com
     snap só nas pilhas, 0 → 32 → 148. Com o snap em todos os filhos, o zero
     volta a ser posição válida e a faixa nasce no começo. */
  .rail > * { scroll-snap-align: start; }
  /* O slot sai POR PADRÃO, porque em faixa ele é caro: medido a 360×740,
     devolvê-lo leva o trilho de 41 para 128px e tira 87px do campo — de
     54% para 43% da janela.

     Mas ele deixou de sair SEMPRE (UX-36, ADR-0087): quem o esconde agora
     é a regra geral de `[data-rail="collapsed"]`, lá em cima, e em faixa o
     recolhido é só o PADRÃO. Ver o bloco do toggle abaixo.

     Recolhida, a pilha fica sem nada que pareça alvo, e ela virou alvo na
     ADR-0058. Então a afordância vem daqui: a faixa desenha cada pilha
     como CONTROLE, e o que o toque faz está no hint (ADR-0060). */
  .pile {
    background: var(--bg-surface-2);
    border: 1px solid var(--line);
    border-radius: var(--radius);
  }
  .pile:active { background: var(--bg-surface-3); border-color: var(--line-strong); }

  /* Em pé a faixa divide a largura da tela por três, e "CEMITÉRIO" não
     cabe em um terço de 360px — no aparelho as três palavras apareciam
     cortadas (teste de 2026-08-14). Vira ÍCONE, exatamente como os
     contadores da topbar fizeram na ADR-0060, e pelo mesmo motivo: o
     rótulo cortado não identifica nada, e o espaço não existe.

     Desde o UX-50 (ADR-0218) só a PALAVRA sai aqui: o ícone já estava
     visível no desktop, e o ponto de cor deixou de existir. */
  .pile__word { display: none; }

  /* AQUI MORAVA `justify-content: space-between` (JG-18, ADR-0189), que punha
     o `👁` na borda direita da linha. Ele saiu porque as METADES da ADR-0196,
     mais acima, fazem o mesmo trabalho melhor: `space-between` só distribui
     bem quando o conteúdo enche a linha, e com a pilha larga ele virou um vão
     de 71px no meio. As duas colunas iguais distribuem a sobra em vez de
     empoçá-la, e ainda garantem `comprar >= ver` por construção.

     Fica registrado aqui porque a regra que saiu era a resposta a um relato
     de uso, e voltar a `space-between` neste bloco reabriria o `JG-18`. */

  /* O ESTADO EXPANDIDO VOLTOU À FAIXA (UX-36, ADR-0087), e a ADR-0070 que
     o tinha removido estava certa quando o removeu.

     O argumento dela era este, e continua verdadeiro: aberta e recolhida a
     faixa lia EXATAMENTE a mesma coisa, porque o slot saía por altura e a
     palavra já era ícone. Sobravam 29,5px de diferença e nada de conteúdo.
     Um estado que custa altura e não mostra nada a mais não é escolha, é
     armadilha — e o toggle saiu junto, porque botão que não muda nada é
     pior que botão nenhum.

     O que mudou não foi o argumento: foi o CONTEÚDO. Agora abrir mostra a
     carta do topo de cada zona, como no desktop, e os dois estados passam
     a ler coisas diferentes. O botão volta a ter o que fazer.

     O padrão continua sendo o recolhido, e isso é `railCollapsed()` no JS,
     não daqui: em faixa o campo é o recurso escasso e os 87px só se pagam
     a pedido. */
  .rail { gap: var(--sp-1); padding: var(--sp-1); }
  .pile { padding: 3px 4px; }
  /* Aberta, a pilha respira um pouco mais — é a linha que carrega a carta. */
  #app:not([data-rail="collapsed"]) .pile { padding: 5px 4px 4px; }

  /* A seta aponta para onde o clique LEVA, e aqui o eixo é vertical: a
     faixa mora abaixo do campo, então recolhida ela abre para CIMA. Mesma
     geometria da mão, que é o outro painel de borda (ADR-0061). */
  .rail .panel-toggle__caret { transform: rotate(90deg); }
  [data-rail="collapsed"] .rail .panel-toggle__caret { transform: rotate(-90deg); }
  /* `--rail-w` não é lido aqui: a grade da faixa é de uma coluna só, e o
     que recolher encolhe é a ALTURA da fila, que sai do conteúdo. */
}

/* O outro lado da bifurcação — estreito e DEITADO — não ganhou regra de
   largura, e a ausência é deliberada. A primeira versão deste bloco dava
   `--rail-w: 96px` ao deitado "porque 172px seriam 20% de um aparelho de
   844px"; medido, o trilho a 96px espremia "GRIMÓRIO" em 21px de palavra,
   e a largura que ele economizava não virava nada — deitado o campo é
   preso pela ALTURA (fica no piso de zoom de 0,18 nas duas larguras), e
   largura devolvida ali não vira escala. Os 132px que a regra de 1180px
   já dá bastam, e o que o deitado precisa de verdade é a regra de altura
   abaixo.

   A regra abaixo pergunta ALTURA, e só ela — vale em qualquer largura,
   inclusive numa janela larga e baixa de desktop. Misturar as duas
   perguntas numa media query só é o que fazia a versão anterior dar 96px
   de trilho a uma janela de 1400px que não era estreita, só era baixa.

   A resposta é a MESMA da ADR-0060 na faixa, chegando pelo outro lado: o
   slot é luxo de trilho alto. Deitado, a coluna do trilho não é alta —
   ela é a linha da mesa, que a mão já espremeu —, e com o slot medido por
   PRINT sobrava exatamente UMA pilha visível das quatro, com as outras
   três abaixo da dobra e nada dizendo isso. Sem ele cabem as três de
   sempre. Não é regra nova: é a de sempre, agora perguntando altura em
   vez de perguntar largura. */
@media (max-height: 520px) {
  /* O único lugar do projeto onde este formato é declarado. O JS lê o
     token em vez de repetir a condição — mesma disciplina do
     `chromeOutsideHand()`, que pergunta ao CSS se o trilho é faixa.
     (Escrito junto com `--hand-lift` abaixo.) */

  .pile__slot { display: none; }
  /* --- A BARRA DA MÃO DIVIDE A LINHA COM AS CARTAS (UX-32) ---
     28 dos 41px que a mão gasta sem ser carta eram uma linha inteira para
     um rótulo curto, e aqui os 41 são 40% de uma mão de 102px. Deitada, a
     mão é o painel mais apertado da mesa; a linha é o recurso escasso e o
     rótulo não precisa de uma só para ele.

     Vira COLUNA à esquerda. Custa ~80px de largura numa mão de 844 — 9,5%
     de um eixo que sobra, contra 28px de um que falta. E a largura já era
     rolável: as cartas vivem num `overflow-x: auto`.

     Medido: chrome de 41 → 21px, e a carta de 61 → 81px de altura
     (43,7 → 58 de largura). O ganho não é os 28 inteiros porque a alça
     cobra 8 de volta, abaixo. */
  .hand {
    grid-template-rows: minmax(0, 1fr);
    grid-template-columns: auto minmax(0, 1fr);
  }
  .hand__bar { align-self: center; padding-block: 0; }

  /* O LIFT SAI, e não é por causa do hover (ADR-0067 já tinha desfeito
     essa pergunta). É geometria: a ADR-0068 emprestou o respiro do lift da
     BARRA, subindo a caixa das cartas por cima dela. Sem a barra em cima,
     não há de quem emprestar — e emprestar do campo é o que a ADR-0051
     proíbe, porque ali é alvo de clique e de drop. Onde o empréstimo deixa
     de ser possível, o formato mais apertado da mesa não paga por ele. */
  :root { --fmt-short: 1; --hand-lift: 0px; }

  /* E a ALÇA passa a cair sobre as CARTAS, porque a barra não está mais
     acima delas — era o custo que segurou este item quando ele foi
     separado do UX-28. Ela cobra os 8px por dentro, e é o preço honesto:
     8px de uma carta de 89 seriam 9% dela, e é justamente a faixa de onde
     o arrasto começa. Reservado, a carta fica com 81px e a alça continua
     inteira.

     `max` com o lift, e não soma: os dois reservam a MESMA faixa de cima,
     e num formato onde o lift volte a existir eles não devem se empilhar. */
  .hand__cards { padding-top: max(var(--hand-lift), var(--hand-grip-h)); }

  /* Sem o slot, o que decide a largura do trilho é a LINHA — nome mais
     contagem —, e não mais o retângulo de uma carta. Então ele para de
     ser um número e passa a ser `auto`: a faixa da grade pede exatamente
     o que a linha precisa. Foi a terceira tentativa aqui, e as duas
     primeiras eram números escolhidos a olho (96px, depois 132px); as
     duas cortavam "GRIMÓRIO" no meio, e a segunda só cortava menos.
     Número escolhido a olho para caber um texto é sempre o mesmo erro. */
  :root { --rail-w: auto; }
  .pile {
    background: var(--bg-surface-2);
    border: 1px solid var(--line);
    border-radius: var(--radius);
  }
  .pile:active { background: var(--bg-surface-3); border-color: var(--line-strong); }
}

/* O RELÓGIO ATRASADO (JG-1, ADR-0170). É lembrete e não regra: o número fica
   vermelho quando este assento está atrás de algum outro, e é só isso que a
   diferença produz — nenhum reducer age sobre ela, e nada acontece entre as
   mesas por causa dela. O usuário é o juiz (ADR-0016).

   `--danger` e não um vermelho escrito aqui: a paleta tem um lugar só. */
.clock__turn.is-behind,
.minimap__turn.is-behind { color: var(--danger); font-weight: 700; }

/* No mini-mapa o número SÓ EXISTE quando há atraso (o JS não o desenha nos
   outros casos), então aqui ele só precisa caber: o slot é reconhecível e não
   legível (ADR-0150), e a régua é a mesma da vida ao lado. */
.minimap__turn {
  flex: none;
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
