/* =============================================================
   Deck Sandbox — Componente Carta
   Camada 3: um único componente serve mão, battlefield, pilhas e
   (Fase 2) mini-mapas. Quem decide o posicionamento é o modificador,
   nunca o componente.
   ============================================================= */

.card {
  position: relative;
  width: var(--card-w);
  height: var(--card-h);
  flex: none;
  touch-action: none;          /* drag por Pointer Events, sem scroll */
  cursor: grab;
  user-select: none;
  -webkit-user-drag: none;
}
.card img {
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
}

/* -------------------------------------------------------------
   Modificador de posicionamento absoluto no plano lógico.
   --x / --y vêm CRUS do JSON (números, sem unidade).
   ------------------------------------------------------------- */
.card--placed {
  position: absolute;
  top: 0;
  left: 0;
  /* `translate` 2D, e NÃO `translate3d` — a diferença é de nitidez, não de
     desempenho (RV-4, ADR-0104).

     `translate3d` força a carta a virar CAMADA COMPOSTA própria, e ela vive
     dentro do `.bf-canvas`, que é escalado por `transform` (ADR-0033). O
     WebKit rasteriza essa camada na escala DELA — 1× — e deixa o
     compositor esticar a textura até a escala do plano; a 200% isso é
     metade da resolução, e é a carta borrada que o dono do produto via no
     Safari. O Chromium re-rasteriza na escala efetiva, e por isso o arnês
     inteiro (que é Chromium) dizia "nítido" — medido nas quatro janelas, em
     DPR 1 e 2, com e sem GPU.

     `translate` 2D posiciona igual e **continua criando contexto de
     empilhamento**, então o `--z` segue valendo. O que ele não faz é pedir
     camada própria, e é justamente isso que devolve a carta ao raster do
     plano. Verificado em WebKit de verdade com `tools/webkit.sh`. */
  transform: translate(calc(var(--x, 0) * 1px), calc(var(--y, 0) * 1px));
  z-index: var(--z, 1);
}

/* -------------------------------------------------------------
   .card__face concentra TODA transformação visual (tap, hover).
   Separar posição (no .card) de rotação (no .card__face) evita que
   um `transform` sobrescreva o outro e mantém a matemática do drag
   trivial: só mexo em --x/--y.
   ------------------------------------------------------------- */
.card__face {
  position: relative;          /* âncora do .card__full */
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  width: 100%;
  height: 100%;
  border-radius: var(--card-radius);
  background: #0e1319;
  box-shadow: var(--sh-2);
  overflow: hidden;
  transform: rotate(var(--rot, 0deg));
  transform-origin: 50% 50%;
  transition: transform 160ms var(--ease), box-shadow 160ms var(--ease);
}

.card[data-tapped="true"] { --rot: 90deg; }

/* -------------------------------------------------------------
   AFORDÂNCIA DO TAP (UX-4)
   No campo, o hover inclina a carta na direção do tap. É o gesto
   sendo mostrado em vez de anunciado — e ele aponta para onde o
   duplo clique VAI levar, não para um estado genérico de "sou
   clicável", que o `cursor: grab` já dizia e que ensinava a coisa
   errada.

   SÓ no `#bfCanvas`, e isso é a metade que importa: o duplo clique só
   vira carta no campo (o handler filtra `zone === "battlefield"`).
   A dica aparece exatamente onde o gesto existe — na mão ela seria
   uma promessa falsa, e lá o hover já é do lift (ADR-0068).

   `body:not(.is-dragging)` porque durante o arrasto o ponteiro
   atravessa cartas que não são o alvo, e vê-las girando enquanto se
   carrega outra é ruído puro.

   A transição já existe no `.card__face` (160ms), então isto não
   acrescenta animação nenhuma — só um valor de chegada.
   ------------------------------------------------------------- */
@media (hover: hover) {
  body:not(.is-dragging) #bfCanvas .card:hover .card__face {
    --rot: var(--tap-hint);
  }
  body:not(.is-dragging) #bfCanvas .card[data-tapped="true"]:hover .card__face {
    --rot: var(--tap-hint-tapped);
  }
}

/* Verso NOSSO, no tom do impresso. Nada de logo nem do desenho da
   Wizards: o que faz a carta virada parecer carta é a cor quente contra
   o campo frio, e isso a cor sozinha entrega.

   Tudo em `background` e `box-shadow`, sem nó extra e sem imagem: o
   mesmo verso precisa funcionar de 15px (mini-mapa) a 118px (mão), e
   uma carta virada não tem conteúdo para desenhar em cima. */
.card[data-face-down="true"] .card__face {
  background:
    /* miolo mais claro, onde a carta impressa tem o oval */
    radial-gradient(58% 42% at 50% 50%, var(--card-back-glow) 0%, transparent 72%),
    /* trama diagonal: textura de graça, e some sozinha no LOD baixo */
    repeating-linear-gradient(45deg,
      var(--card-back-1) 0 6px, var(--card-back-2) 6px 12px);
  box-shadow:
    var(--sh-2),
    inset 0 0 0 1px var(--card-back-edge),     /* fio escuro no limite */
    inset 0 0 0 5px var(--card-back-frame);    /* moldura marrom, 4px */
}
/* `.card__full` na lista, e não é detalhe: sem ela, uma carta virada na
   mão mostraria a carta impressa inteira por cima do verso. */
.card[data-face-down="true"] .card__head,
.card[data-face-down="true"] .card__art,
.card[data-face-down="true"] .card__full,
.card[data-face-down="true"] .card__foot { visibility: hidden; }

/* --- cabeçalho: nome + custo ---
   min-width:0 é obrigatório: sem ele o mínimo automático do item de
   grid é o max-content da linha e o nome da carta vaza para fora
   do card em vez de truncar. */
.card__head {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
  padding: 4px 5px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.02));
}
.card__name {
  flex: 1;
  min-width: 0;
  font-size: 9.5px;
  font-weight: 650;
  line-height: 1.15;
  letter-spacing: -0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.card__cost {
  display: flex;
  gap: 2px;
  flex: none;
}
.card__pip {
  display: grid;
  place-items: center;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--pip, #2a333f);
  color: #0c1017;
  font-size: 8px;
  font-weight: 800;
}

/* --- arte (placeholder por identidade de cor) --- */
.card__art {
  margin: 0 4px;
  border-radius: 3px;
  background:
    linear-gradient(160deg, color-mix(in oklab, var(--ci) 82%, #000) 0%,
                            color-mix(in oklab, var(--ci) 34%, #000) 100%);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.5);
}
.card__art img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }

/* --- rodapé: tipo + P/T --- */
.card__foot {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;                /* idem .card__head */
  padding: 4px 5px;
}
.card__type {
  flex: 1;
  min-width: 0;
  color: var(--fg-muted);
  font-size: 8.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.card__pt {
  flex: none;
  padding: 1px 4px;
  border-radius: 3px;
  background: rgba(0, 0, 0, 0.55);
  font-size: 9.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* --- identidade de cor --- */
.card[data-ci="W"] { --ci: var(--c-white); }
.card[data-ci="U"] { --ci: var(--c-blue); }
.card[data-ci="B"] { --ci: var(--c-black); }
.card[data-ci="R"] { --ci: var(--c-red); }
.card[data-ci="G"] { --ci: var(--c-green); }
.card[data-ci="M"] { --ci: var(--c-gold); }
.card[data-ci="A"] { --ci: var(--c-artifact); }
.card[data-ci="L"] { --ci: var(--c-land); }
.card { --ci: var(--c-artifact); }

/* -------------------------------------------------------------
   Marcadores (counters) — mapa espécie → quantidade na entidade
   (§3.1 de docs/arquitetura.md, ADR-0019).
   Ficam FORA do .card__face para não girarem junto com o tap.
   ------------------------------------------------------------- */
/* A FAIXA DE BAIXO, QUE O BOTÃO DE VIRAR E OS MARCADORES DIVIDEM (JG-22).

   Antes eram dois absolutos no MESMO ponto, e o `z-index: 2` do botão —
   escrito para ele ganhar do ARRASTO (ADR-0224 D2) — fazia ele cobrir 100%
   do selo do marcador. Quem achou foi o dono do produto, jogando.

   Agora a faixa é UMA linha flex centrada e são eles que a dividem, com o
   `gap` no lugar de duas posições fixas. Isso é o que a torna dinâmica: o
   selo cresce por espécie de marcador e a faixa acompanha sozinha, sem
   ninguém recalcular deslocamento.

   O `z-index` sobe para a FAIXA, que é quem agora precisa vencer a face —
   o botão dentro dela não disputa com o irmão, então não precisa do dele. */
.card__strip {
  position: absolute;
  left: 50%;
  bottom: -9px;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--card-strip-gap);
  transform: translateX(-50%);
  /* O contêiner não é alvo; quem é botão reativa o seu (o `.card__flip`,
     abaixo). Sem isto a faixa inteira viraria uma barra invisível por cima
     da borda da carta, que é alvo de clique e de drop. */
  pointer-events: none;
}

.card__counters {
  display: flex;
  gap: 3px;
}
.card__counter {
  display: grid;
  place-items: center;
  min-width: 20px;
  height: 17px;
  padding: 0 4px;
  border-radius: 999px;
  font-size: 9px;
  font-weight: 750;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}
/* Estilo por TOM, não por espécie: espécie nova (energia, experiência,
   óleo, um set que ainda nem saiu) cai no tom neutro e renderiza igual,
   sem precisar de regra nova aqui. Ver ADR-0019. */
.card__counter[data-tone="buff"]    { background: #2f7d52; color: #eafff2; }
.card__counter[data-tone="debuff"]  { background: #8c3b3b; color: #ffecec; }
.card__counter[data-tone="loyalty"] { background: #2b2b33; color: #f0e6d2; box-shadow: 0 0 0 1px #6b6b7a; }
.card__counter[data-tone="neutral"] { background: #33455c; color: #dbe8f7; }

/* -------------------------------------------------------------
   LOD "full" — a carta impressa, de 106px de largura para cima.

   Cobre o quadro inteiro em vez de substituí-lo: assim o fallback é o
   próprio quadro, sem nenhum caso especial no JS, e impressão não
   resolvida simplesmente não gera este nó.

   A TRAVESSIA É UM CROSSFADE (ADR-0035). É por isso que a camada, uma vez
   nascida, não é mais removida: para dissolver um desenho no outro os
   dois precisam coexistir, e a imagem já está em cache — manter o nó não
   custa rede. Quem sai de cena é a opacidade.
   ------------------------------------------------------------- */
.card__full {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
  opacity: 1;
  transition: opacity var(--lod-fade) var(--ease);
}

/* Abaixo do limiar a camada continua no DOM, invisível. O quadro que
   está embaixo reaparece dissolvendo, e não por corte. */
[data-lod="frame"] .card__full,
[data-lod="art"] .card__full,
[data-lod="low"] .card__full { opacity: 0; }

/* -------------------------------------------------------------
   As marcas de travessia (ADR-0035, ADR-0036).

   Uma camada recém-inserida não tem estado anterior para transicionar —
   o nó acabou de nascer —, e é `@starting-style` que dá esse estado. Mas
   TODA inserção é um nó novo, inclusive a de um `render()` qualquer: sem
   marca, a camada aparece pronta, que é o certo para uma recriação
   (senão cada jogada faria a mesa inteira piscar).

   Duas coisas independentes, duas marcas:

     DIREÇÃO   `--surge` sobe (nasce em 0)  ·  `--land` desce (nasce em 1)
     CAUSA     `--drop` troca o tempo para `--lod-fade-drop`; sem ela,
               vale `--lod-fade`, que é o caso do zoom.

   Sem suporte a `@starting-style` o app degrada para o que já fazia:
   a travessia é seca em vez de dissolver.
   ------------------------------------------------------------- */
.card__full--drop { transition-duration: var(--lod-fade-drop); }

@starting-style {
  /* Sobe: no destino a regra que vale é `.card__full { opacity: 1 }`
     (0,1,0). Empate de especificidade, e o `@starting-style` vem depois
     na ordem do arquivo — então ele ganha e a camada nasce em 0. */
  .card__full--surge { opacity: 0; }

  /* Desce: aqui NÃO há empate. O destino casa
     `[data-lod="frame"] .card__full` (0,2,0), então o seletor precisa
     ser mais específico de propósito. Com só uma classe (0,1,0) a regra
     que zera a opacidade vencia e a camada nascia JÁ invisível — não
     dissolvia nada, e nada na suíte acusava, porque o arnês desliga
     transição. Achado medindo `opacity` no navegador real. */
  [data-lod] .card__full.card__full--land { opacity: 1; }
}

/* -------------------------------------------------------------
   Quantidade — pilha de token (ADR-0025).

   Uma pilha é UMA instância; o badge diz quantas unidades ela vale.
   Fica FORA do .card__face pelo mesmo motivo dos marcadores: não pode
   girar junto com o tap.
   ------------------------------------------------------------- */
/* O VERSO, na carta do campo (UX-49, ADR-0224).

   Fica na borda de baixo, centrado, como na referência que o dono do
   produto mandou — e FORA do `.card__face`, senão giraria com a carta
   virada (ADR-0004), que é a mesma razão do badge de quantidade.

   Alvo de 24px, que é a régua de dedo deste projeto (ADR-0070). Ele nasce
   visível e não no hover: no dedo não há hover, e alvo invisível-e-clicável
   é exatamente o defeito que a UX-29 desfez na pilha. */
/* Ele mora na `.card__strip` desde o JG-22 e não se posiciona mais sozinho:
   quem o coloca é o flex do pai. O `pointer-events` volta porque a faixa o
   desliga para todos, e ele é o único ali que é alvo de verdade. */
.card__flip {
  pointer-events: auto;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  flex: none;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: var(--bg-surface-3);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6), inset 0 0 0 1px var(--line-strong);
  color: var(--fg);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}
.card__flip:hover { background: var(--accent); color: #fff; }
.card__flip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Nos degraus pequenos da escada de LOD a carta não tem onde pôr um alvo
   de 24px sem cobrir a arte inteira — e ali ela também não se lê, que é o
   que o botão serve para resolver. Some, como o resto do detalhe. */
[data-lod="mini"] .card__flip,
[data-lod="tiny"] .card__flip { display: none; }

.card__qty {
  position: absolute;
  top: -6px;
  right: -6px;
  display: grid;
  place-items: center;
  min-width: 22px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--bg-surface-3);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6), inset 0 0 0 1px var(--line-strong);
  color: var(--fg);
  font-size: 10px;
  font-weight: 750;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}

/* -------------------------------------------------------------
   Estados de interação
   ------------------------------------------------------------- */
.card:hover  { z-index: calc(var(--z, 1) + 100); }
.card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--card-radius); }

.hand__cards .card { transition: transform 140ms var(--ease); }
/* A distância sai do token que também RESERVA o espaço dela no
   `.hand__cards` (UX-28). Um número só: um `-20px` aqui contra um
   `padding: 24px` lá são duas verdades que divergem no primeiro ajuste,
   e a que diverge para menos recorta a carta levantada.

   O respiro que este deslocamento exige é EMPRESTADO da barra, e não
   tirado da carta (ADR-0068) — ver o par margem/`padding` no
   `.hand__cards`. A carta levantada passa por cima da barra, e é
   deliberado: ninguém faz hover e arrasta a alça ao mesmo tempo. */
.hand__cards .card:hover { transform: translateY(calc(-1 * var(--hand-lift))); }
.hand__cards .card:hover .card__face { box-shadow: 0 12px 24px rgba(0, 0, 0, 0.55); }

.card.is-selected .card__face { box-shadow: var(--sh-2), 0 0 0 2px var(--accent); }

/* -------------------------------------------------------------
   CAMADA DE ARRASTO

   A carta arrastada sai do fluxo e passa a viver aqui, em
   coordenadas de tela. Assim ela não é recortada pelo
   `overflow: hidden` do battlefield nem pela mão, e não precisa ser
   convertida para o espaço lógico enquanto está no ar — a conversão
   acontece uma única vez, no drop.

   Três transforms aninhados, um trabalho cada:
     .drag-ghost   posição (segue o ponteiro, sem transition)
     > .card       escala   (muda conforme a zona sob o cursor)
     > .card__face rotação  (tap)
   ------------------------------------------------------------- */
.drag-layer {
  position: fixed;
  inset: 0;
  z-index: var(--z-drag);
  pointer-events: none;        /* deixa o elementFromPoint enxergar as zonas */
}

.drag-ghost {
  position: absolute;
  top: 0;
  left: 0;
  transform: translate3d(calc(var(--px, 0) * 1px), calc(var(--py, 0) * 1px), 0);
  will-change: transform;
}

/* O GRUPO carrega a escala. Assim os offsets relativos entre as cartas
   escalam junto, e criatura + equipamento mantêm o arranjo ao entrar no
   campo. Uma carta só é um grupo de uma. */
.drag-ghost__group {
  position: relative;
  width: var(--card-w);
  height: var(--card-h);
  transform: scale(var(--ghost-scale, 1));
  /* Ancorado no ponto onde o dedo pegou, em px: a carta pega fica em
     (0,0) do grupo, então o ponto de pega em coordenadas do grupo é
     exatamente (gx, gy). Porcentagem não serviria — o grupo não tem o
     tamanho de uma carta. */
  transform-origin: calc(var(--gx, 0) * 1px) calc(var(--gy, 0) * 1px);
  /* A ÚNICA transição do caminho do arrasto: a posição segue o ponteiro
     1:1, sem suavização (senão a carta ficaria atrás do dedo), e quem
     amacia é a escala. Tempo em token porque é calibragem de olho, e
     porque o salto depende do zoom — ver `--drag-resize`. */
  transition: transform var(--drag-resize) var(--ease);
}

.drag-ghost__group > .card {
  position: absolute;
  top: 0;
  left: 0;
  transform: translate3d(calc(var(--dx, 0) * 1px), calc(var(--dy, 0) * 1px), 0);
}
.drag-ghost .card__face { box-shadow: var(--sh-drag); }

/* Seleção — o anel precisa vencer a carta vizinha sobreposta, senão
   metade dele fica escondida embaixo do permanente ao lado. */
.card.is-selected { z-index: calc(var(--z, 1) + 200); }
.card.is-selected .card__face {
  box-shadow: var(--sh-2), 0 0 0 2px var(--accent), 0 0 12px rgba(79, 140, 255, 0.35);
}

/* A carta original fica no lugar, apagada: mostra de onde saiu e
   evita que a mão reflua no meio do arrasto. */
.card.is-source { opacity: 0.22; }
.card.is-source .card__face { box-shadow: none; }
.hand__cards .card.is-source:hover { transform: none; }

body.is-dragging { cursor: grabbing; }
body.is-dragging .card { cursor: grabbing; }

/* -------------------------------------------------------------
   LOD (level of detail) — ADR-0034.

   O seletor é `[data-lod="low"] .card`, e não `.card[data-lod]`: o
   nível mora no CONTÊINER, porque é dele que vem a escala e portanto é
   dele que a afirmação é verdadeira. Todas as cartas de um contêiner têm
   a mesma largura; marcar carta a carta repetiria N vezes o que é um.

   Abaixo de 30px de largura, texto vira ruído: some com ele e deixa só o
   bloco de cor + estado de tap. Quem chega aí é o mini-mapa (~15px) — e
   o campo numa janela baixa demais, que é o critério funcionando.
   ------------------------------------------------------------- */
/* -------------------------------------------------------------
   O DEGRAU DO MEIO: `art` — a arte sozinha, sem texto (ADR-0131).

   Ele COMPARTILHA a geometria do `low` (face em bloco, arte ocupando a
   carta inteira, head e rodapé fora) e difere numa coisa só: a imagem
   fica. Por isso os seletores abaixo listam os dois níveis, e a única
   regra que continua sendo exclusiva do `low` é a que esconde o `<img>`.

   Escrito como lista e não como um `:is()` para não mudar a
   especificidade das regras que já existiam — várias delas dependem de
   vencer `.card__art` por (0,2,0).

   `.card[data-art]` na regra do texto, e só nela: quem não tem arte
   resolvida PRECISA do quadro, senão perde o nome e vira a cor sólida que
   este degrau veio consertar. O nível é do contêiner (ADR-0034) e
   continua sendo; a marca da carta só diz se a troca é uma melhora. */
[data-lod="low"] .card__head,
[data-lod="low"] .card__foot,
[data-lod="low"] .card__qty,
[data-lod="low"] .card__counters,
[data-lod="art"] .card[data-art] .card__head,
[data-lod="art"] .card[data-art] .card__foot { display: none; }
/* Vira bloco: sem head/foot, a arte cairia na primeira linha `auto`
   do grid e colapsaria para altura 0. */
[data-lod="low"] .card__face,
[data-lod="art"] .card[data-art] .card__face { display: block; }
[data-lod="low"] .card__art,
[data-lod="art"] .card[data-art] .card__art {
  width: 100%;
  height: 100%;
  margin: 0;
  border-radius: var(--card-radius);
  /* miniatura = bloco chapado; gradiente some em 15px de altura */
  background: var(--ci);
}
/* A arte some junto com o texto, e não é detalhe estético: `background`
   sozinho não vence um `<img>` filho, então com catálogo quente o nível
   `low` vinha desenhando 160 art crops de 15px — barro visual, e
   justamente as 160 decodificações de imagem que este nível existe para
   não pagar (ux.md §3.6). Só aparecia com catálogo resolvido, e a suíte
   roda frio; passou despercebido enquanto `low` era só do mini-mapa da
   Fase 2. Com a ADR-0034 o campo também alcança este nível. */
[data-lod="low"] .card__art img { display: none; }
/* E no `art` ela FICA — é a única diferença entre os dois degraus, e é o
   ponto inteiro dele. A contagem de decodificações que o `low` evita
   continua evitada: `art` começa em 30px, e quem desenha 160 cartas de
   15px (o mini-mapa) segue no `low`. */

/* -------------------------------------------------------------
   E a arte some no `full` TAMBÉM, mas por outra razão (ADR-0118).

   Ali a impressa cobre a carta inteira, então o art crop só aparece
   enquanto ela não chegou — e ver uma imagem e logo outra lê como defeito
   ("abre com arte grande e depois muda para a imagem da carta"). Sem ele,
   o que se vê enquanto carrega é a MOLDURA: nome, custo, tipo e P/T, que
   é um estado desenhado e legível, não um buraco.

   Condicionado ao `data-printed`, que a camada põe ao nascer e TIRA se a
   imagem falhar. Sem essa condição, impressa quebrada deixaria a carta sem
   imagem nenhuma — e é justamente o caso em que o art crop é o resgate. */
[data-lod="full"] .card[data-printed] .card__art img { display: none; }
[data-lod="low"] .card__face,
[data-lod="art"] .card[data-art] .card__face { box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6); }

/* -------------------------------------------------------------
   Preview no hover (UX-10) — a carta grande, sem clique.

   Coordenadas de TELA, como a barra de ação (ADR-0023): ancorado ao
   tabuleiro ele herdaria `--bf-scale` e encolheria junto com o zoom,
   que é o oposto do que se quer de um painel de leitura.

   Não desenha nada próprio: é um `.card` comum numa caixa larga, e a
   escada de LOD conclui `full` sozinha (ADR-0034). Impressão não
   resolvida cai no nosso quadro ampliado, pelo caminho de sempre.
   ------------------------------------------------------------- */
.peek {
  position: fixed;
  top: 0;
  left: 0;
  z-index: var(--z-peek);
  width: var(--peek-w);
  /* Nunca é alvo: o hit-test tem que continuar enxergando a carta que
     está EMBAIXO, senão o painel se auto-cancelaria ao aparecer. */
  pointer-events: none;
  transform: translate3d(calc(var(--peek-x, 0) * 1px), calc(var(--peek-y, 0) * 1px), 0);
  filter: drop-shadow(0 16px 32px rgba(0, 0, 0, 0.6));
}

/* `display: none` explícito: o `hidden` sozinho perderia para qualquer
   `display` declarado — a mesma pegadinha que a `.pile[hidden]` já teve. */
.peek[hidden] { display: none; }

.peek .card {
  width: var(--peek-w);
  height: auto;
  aspect-ratio: 63 / 88;
}

/* DUAS CARTAS lado a lado — a zona de comando inteira (ADR-0134).

   `flex` só quando há duas: com uma, o painel continua sendo exatamente o
   de antes, e nenhuma medida dele muda.

   A largura POR CARTA cai para `--peek-w-2`, e o motivo é o mesmo que já
   deu três termos ao `--peek-w` (ADR-0063): `placePeek` sabe clampear a
   POSIÇÃO, não o tamanho. Duas cartas de 320px são 652px com o vão — mais
   que a viewport inteira de um celular. O painel tem de caber por
   construção, não por sorte. */
.peek[data-n="2"] {
  display: flex;
  gap: var(--sp-2);
  width: auto;
}
.peek[data-n="2"] .card { width: var(--peek-w-2); }

/* =============================================================
   A FICHA GENÉRICA — o marcador arrastável (JG-24)
   =============================================================
   Pedido do dono do produto: *"uma ficha redonda parecida com poker, ou um
   dado, algo genérico que possa ser arrastado no board só para marcar
   algo"*. Ela marca uma designação que o Magic não representa com objeto —
   o portador do anel, o monarca — e por isso NÃO é uma carta: não tem
   nome na peça, nem custo, nem tipo, nem arte.

   Ela continua sendo `.card` no DOM, e é isso que a faz funcionar de
   graça: arrasto, seleção, `z-index` e o índice de zonas leem `.card` +
   `data-id`, e nenhum deles pergunta se há carta por trás.

   O TAMANHO SAI DA CARTA, e não é número escolhido: `--card-w` é a unidade
   do plano lógico (ADR-0001), então a ficha escala junto com a mesa como
   todo o resto. `--marker-scale` (0,34) põe a ficha em ~40tu — grande o
   bastante para o dedo no zoom de leitura, pequena o bastante para caber ao
   lado de uma carta sem cobri-la, que é o trabalho dela.

   O TOKEN É LIDO PELO JS TAMBÉM (`cardRect`), e é por isso que ele é fração
   e não `px`: a caixa LÓGICA da ficha precisa do mesmo número, senão a régua
   de ações e o laço miram um retângulo de carta onde há uma bolinha. */
/* `.card.card--marker` e não `.card--marker`: a especificidade precisa
   empatar com `.card.is-selected` (0,2,0) e `.card:hover` (0,2,0), que
   também escrevem `z-index`. Empatando e vindo depois nesta folha, esta
   ganha — e a ficha selecionada ou sob o ponteiro continua na faixa dela,
   que é o que o pedido quer dizer com "sempre". */
.card.card--marker { z-index: calc(var(--z, 1) + var(--z-marker-lift)); }

.card--marker {
  width: calc(var(--card-w) * var(--marker-scale));
  height: calc(var(--card-w) * var(--marker-scale));
  border-radius: 50%;
  background: var(--marker-fill);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.55),
              inset 0 0 0 2px rgba(255, 255, 255, 0.55),
              inset 0 -3px 6px rgba(0, 0, 0, 0.35);
  cursor: grab;
}

/* A cor entra por CHAVE, nunca por hex no JS (ADR-0019): o `MARKER_COLORS`
   emite `red`, e quem sabe o que é vermelho neste tema é esta folha. */
.card--marker[data-color="red"]    { --marker-fill: #d2423c; }
.card--marker[data-color="blue"]   { --marker-fill: #3f7fd0; }
.card--marker[data-color="green"]  { --marker-fill: #46a05a; }
.card--marker[data-color="yellow"] { --marker-fill: #d8a521; }

/* A INCLINAÇÃO DO HOVER E A BORDA DE SELEÇÃO MORAM NO `.card__face`
   (UX-4, e as regras `.card.is-selected .card__face`), e a ficha não tem
   face — então as duas não a alcançam, sem precisar de exceção nenhuma.

   A seleção precisa de regra própria por isso, e ela vai na própria ficha.
   O `z-index` da seleção continua sendo o da `.card`, que é compartilhado.

   A ficha NÃO ganha a inclinação de propósito: aquilo PREVÊ o tap (a carta
   "quer" girar), e ficha não vira. */
.card--marker.is-selected {
  box-shadow: 0 0 0 3px var(--accent), 0 2px 6px rgba(0, 0, 0, 0.55);
}

/* Nos degraus pequenos ela ENCOLHE mas não some, ao contrário do detalhe da
   carta: a ficha não é detalhe DE nada — ela é a única coisa que diz que
   aquela criatura é o portador do anel, e sumir apagaria a informação. */
[data-lod="low"] .card--marker,
[data-lod="mini"] .card--marker,
[data-lod="tiny"] .card--marker { box-shadow: 0 1px 2px rgba(0, 0, 0, 0.55),
                                              inset 0 0 0 1px rgba(255, 255, 255, 0.5); }
