/* --- lobby-ilha --- (docs/specs/island.md)
   A ILHA do jogador: uma camada DOM sobre o palco do lobby, irmã da do mapa de nódulos (`css/work.css › .node-map`) e
   no MESMO z 1 — abaixo das janelas (z 10+) e acima do canvas.

   A ilha NÃO tem câmera (D2 do `00`): ela cabe inteira na Faixa B, o terreno nasce em (0, 0) e o zoom é 1. Por isso
   aqui não há `cursor: grab`, nem `touch-action`, nem nada que sugira arrasto.

   Os números abaixo são o PADRÃO da folha; quem manda é o dado (`shared/data/config/island.json › viewport`, que o
   bundle publica como `island`). `widgets/islandMap.js` reescreve altura e tamanho do terreno a partir dele na
   montagem — invariante 3: nenhum px de layout é decidido em código ou em folha de estilo. */

.island {
  position: absolute;
  left: 0;
  top: calc(50 * var(--px));
  width: calc(640 * var(--px));
  height: calc(282 * var(--px));
  overflow: hidden;
  z-index: 1;
  pointer-events: auto;
}

/* O terreno: a imagem do fundo `island_home` em px de arte, amostrada nearest como todo o resto do jogo. */
.island-ground {
  position: absolute;
  left: 0;
  top: 0;
  width: calc(640 * var(--px));
  height: calc(360 * var(--px));
  background-repeat: no-repeat;
  background-size: 100% 100%;
  image-rendering: pixelated;
}
/* Sem a arte no manifesto a tela continua funcionando (como `.node-map-bg.is-missing` já faz): o terreno vira um
   gradiente e os lotes que TÊM prop continuam clicáveis. */
.island.is-missing .island-ground {
  background-image: none;
  background: linear-gradient(#1b2740 0%, #2c3c26 62%, #3a3324 100%);
}

.island-lots {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* Um lote: o prédio em si. `left`/`top` inteiros escritos por `placeAt` (D27 do lobby-v2 — nunca `transform`). */
.island-lot {
  position: absolute;
  pointer-events: auto;
  padding: 0;
  border: 0;
  background-color: transparent;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  image-rendering: pixelated;
}
/* Lote reservado (`target: null`): existe na tela, não responde e não entra no Tab. */
.island-lot.is-reserved { pointer-events: none; }

/* A barra contextual (D11, pacote 08): 28 px FIXOS colados ao prédio — nome · [degraus] nível · ação principal ·
   `▲ Melhorar` com o custo dentro · fechar. A moldura é a de 4 px (`.tooltip-frame`, a do tooltip e do menu): a de 8 px do
   `.panel` deixaria 12 px de miolo para botões de 16. Posição e largura: `widgets/islandMap.js › barPlacement`.
   Na Casa ela cresce uma linha (a de expandir a ilha, abaixo). */
.island-bar {
  position: absolute;
  height: calc(28 * var(--px));
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 calc(2 * var(--px));
  z-index: 2;
  white-space: nowrap;
  pointer-events: auto;
}
/* A linha do LUGAR (a de todo lote): 20 px de miolo dentro da moldura de 4 — a mesma barra de 28 de antes. */
.island-bar-row { display: flex; align-items: center; gap: calc(4 * var(--px)); min-height: calc(20 * var(--px)); }
/* --- ajustes (2026-09-30) --- A Casa ganha a 2.ª linha, a da ILHA (`widgets/islandMap.js › renderExpand`): `▲ Expandir ilha` ·
   `Nv 1 → 2` · cada exigência com tem/precisa e ✔/✖. Um fio apagado separa o que é da Casa (linha 1) do que é da ilha. A altura
   deixa de ser fixa; quem mede e pousa é `barPlacement` (28 + `EXPAND_H` sem layout). */
.island-bar.has-expand { height: auto; }
.island-expand {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(2 * var(--px)) calc(4 * var(--px));
  margin-top: calc(1 * var(--px));
  padding: calc(2 * var(--px)) 0 calc(1 * var(--px));
  border-top: var(--px) solid var(--c-text-dim);
}
.island-expand-to { color: var(--c-gold); }
/* entre exigências o vão é maior que o de dentro de cada uma (G1: separador entre famílias); e a lista se afasta do
   `Nv 1 → 2`, que é do botão, não uma exigência */
/* --- consertar F5 (revisão de 2026-10-05; docs/specs/island.md §3.1) --- a linha da ilha QUEBRA: prédios e ouro ficam na linha do
   `▲` (`.island-expand-cost`); os MATERIAIS descem para a linha de baixo, inteira (`flex-basis: 100%`), e também quebram. A barra é
   presa à Faixa B menos o canto do chat (`islandMap.js › barMaxW`, `max-width` no estilo): numa linha só, o Nv 3 → 4 media 953 px
   numa faixa de 640 e o `×` saía da tela. */
.island-expand-cost { display: inline-flex; align-items: center; gap: calc(6 * var(--px)); margin-left: calc(6 * var(--px)); }
.island-expand-reqs { display: flex; flex-wrap: wrap; flex-basis: 100%; align-items: center; gap: calc(2 * var(--px)) calc(6 * var(--px)); }
/* com duas linhas a 1.ª fica mais curta que a 2.ª: o `×` vai para a borda, onde se procura o fechar */
.island-bar.has-expand .island-bar-close { margin-left: auto; }
.island-req { display: inline-flex; align-items: center; gap: calc(2 * var(--px)); }
/* o MIOLO do material (slot + nome + tem/precisa), não focável, leva a frase no `title` do mouse (docs/specs/island.md §3.1):
   a mesma fila do chip, para a linha não mudar de desenho */
.island-req-body { display: inline-flex; align-items: center; gap: calc(2 * var(--px)); }
.island-req .slot { flex-shrink: 0; }
.island-req-mark { display: inline-block; width: calc(8 * var(--px)); text-align: center; }
/* a cor de falta é a da Artesã e da Bancada de Reparo (`.craft-mat`, `.rp-mat`); o ✔/✖ diz o mesmo sem cor (§8.7) */
.island-req.is-ok .island-req-mark { color: var(--c-ok); }
.island-req.is-missing .island-req-val, .island-req.is-missing .island-req-mark { color: var(--c-danger); }
/* o material diz o NOME escrito, em cima do tem/precisa (molde `.craft-mat-text`; ux-casos §13.3) — as duas linhas cabem
   nos 36 px do slot. O ícone de quem falta NÃO se apaga: é justamente o que o jogador precisa reconhecer (a cor e o ✖ já marcam) */
.island-req-text { display: inline-flex; flex-direction: column; justify-content: center; }
.island-req-line { display: inline-flex; align-items: center; gap: calc(2 * var(--px)); }
/* --- ajustes (2026-09-30) --- o MATERIAL da linha é um botão (docs/specs/island.md §3.1): clique = "Onde conseguir", botão
   direito = o menu de item. O realce é o do material da Oficina e do Refino (`craft.css › .craft-mat[role='button']`): o slot
   acende como no hover da Mochila e o nome fica dourado; o foco de teclado é o contorno dourado, 1 px para dentro. O slot
   nasce com `touch-action: none` (o arrasto da Mochila): aqui o toque longo abre o menu. */
.island-req[role='button'] { cursor: pointer; }
.island-req[role='button']:hover .slot,
.island-req[role='button']:focus-visible .slot { border-image-source: url(../assets/ui/slots/slot_hover.png); }
.island-req[role='button']:hover .island-req-name { color: var(--c-gold); }
.island-req[role='button']:focus-visible { outline: var(--px) solid var(--c-gold); outline-offset: calc(-1 * var(--px)); }
.island-req .slot { touch-action: auto; }
.island-bar-head { display: inline-flex; align-items: center; gap: calc(3 * var(--px)); }
.island-bar-name { color: var(--c-gold); }
/* G1 (docs/UX-GRAMATICA.md): o nível é [degraus] + número; a palavra "Nível" mora no `title` e no `aria-label`. */
.island-bar-level { display: inline-flex; align-items: center; gap: calc(1 * var(--px)); }
.island-bar-ico {
  width: calc(16 * var(--px));
  height: calc(16 * var(--px));
  background: var(--ui-ico_lot_level, none) no-repeat 0 0 / 100% 100%;
  image-rendering: pixelated;
}
.island-bar-ico.is-max { background-image: var(--ui-ico_lot_max, none); }
/* G2: estado é SELO (moldura + palavra curta + cor), nunca frase — o nível máximo, que acabou: cor apagada. */
.island-bar-seal {
  display: inline-flex;
  align-items: center;
  height: calc(14 * var(--px));
  padding: 0 calc(3 * var(--px));
  border: var(--px) solid var(--c-text-dim);
  color: var(--c-text-dim);
}
.island-bar .btn { white-space: nowrap; }
.island-bar-cost { margin-left: calc(2 * var(--px)); }
.island-bar-close { padding: 0 calc(3 * var(--px)); }

/* O SELO "pode subir" (D12/D13): `ico_lot_up` no canto de cima do prédio, dentro do botão do lote. A cadência é a de
   `art/src/ui/upgrade_arrow.py` / `.slot-upgrade` em `nine-slice.css` (D22): o quadro aceso (`ico_lot_up_lit`) é o
   `::after`, acende ~15 % de um ciclo de 2,4 s em `step-end`, SEM `animation-delay` por lote — todos em fase.
   Movimento reduzido: só o quadro base.
   --- ajustes (2026-09-30) --- 7 px da borda direita, não 4: com a placa de ofício no pátio da frente, a tábua da vaga 2
   (x 435…461, y 180…196) entrava 1 px na coluna do selo da vaga 4 (x 424…436, y 188…200) — o único par da planta. Com 7
   sobram 2 px (o vão mínimo entre placas) em qualquer arranjo de prédios; cerca: `client/tests/ui/islandPlates.test.js`. */
.island-badge {
  position: absolute;
  right: calc(7 * var(--px));
  top: 0;
  width: calc(12 * var(--px));
  height: calc(12 * var(--px));
  background: var(--ui-ico_lot_up, none) no-repeat 0 0 / 100% 100%;
  image-rendering: pixelated;
  pointer-events: none;
}
.island-badge::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--ui-ico_lot_up_lit, none) no-repeat 0 0 / 100% 100%;
  image-rendering: pixelated;
  opacity: 0;
  animation: island-badge-lit 2.4s step-end infinite;
}
@keyframes island-badge-lit {
  0%   { opacity: 0; }
  85%  { opacity: 1; }
  100% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .island-badge::after { animation: none; }
}

/* --- ajustes (2026-09-30, pedido do dono) --- O BALÃO DE PRONTO (docs/specs/island.md §3.1 › O balão de pronto): o prédio
   com refinado pronto leva `ico_lot_ready` (o caixote num balão de ouro, rabinho para baixo) no alto da caixa, e o CLIQUE nele
   coleta. É um `<button>` IRMÃO do botão do lote em `.island-lots` (molde do cadeado; `widgets/islandMap.js › makeReady`):
   tamanho (da peça no manifesto) e `left`/`top` (`islandView.js › readyRect`) vêm escritos no nó; o `z-index` é o daqui. A
   camada é `pointer-events: none` — o balão pede o ponteiro dele, e nada novo cobre o terreno (só a própria caixa de 20×22).
   Cadência do selo `ico_lot_up`: o quadro aceso (`ico_lot_ready_lit`) é o `::after`, ~15 % de um ciclo de 2,4 s em
   `step-end`, sem atraso por lote — todos em fase com o selo. SEM quique: um quique para CIMA entrava na estaca da tábua da
   fileira de cima (a folga medida é 4 px). Movimento reduzido: só o quadro base. */
.island-lots > .island-ready {
  position: absolute;
  padding: 0;
  border: 0;
  background: var(--ui-ico_lot_ready, none) no-repeat 0 0 / 100% 100%;
  image-rendering: pixelated;
  pointer-events: auto;
  cursor: pointer;
}
.island-lots > .island-ready::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--ui-ico_lot_ready_lit, none) no-repeat 0 0 / 100% 100%;
  image-rendering: pixelated;
  opacity: 0;
  animation: island-badge-lit 2.4s step-end infinite;
}
/* com a coleta em voo ele trava COM o motivo (`ui.common.waitServer`, `setButtonReason`: continua recebendo o ponteiro, que
   traz o balão do motivo) e para de piscar — o pisca é o chamado, e ele já foi atendido */
.island-lots > .island-ready.is-disabled { cursor: progress; }
.island-lots > .island-ready.is-disabled::after { animation: none; }
/* a BARRA que o cruzaria e a tábua À VISTA que passaria por cima dele o escondem inteiro (`syncReadyCover`): meio balão
   espiando por baixo não. `visibility`, não `display`: ele guarda o lugar e volta inteiro */
.island-lots > .island-ready.is-under-bar,
.island-lots > .island-ready.is-under-sign { visibility: hidden; }
/* um chamado dourado por prédio: com o balão no lote, o selo "pode subir" se cala até a coleta (a coleta é de graça e imediata;
   o selo volta sozinho quando o balão sai) */
.island-lot.has-ready > .island-badge { visibility: hidden; }
@media (prefers-reduced-motion: reduce) {
  .island-lots > .island-ready::after { animation: none; }
}
/* --- revisão (2026-10-01, a planta de 16 vagas) --- O TOQUE é do balão (docs/specs/island.md §3 › A régua do toque): a tábua
   da vaga 16 pousa a 3 px do balão da vaga 4 (a da 2, do da 12) e as estacas das vagas de cima a 4 px dos balões do meio — no
   celular deitado (`--px` 1) é meio milímetro entre "coletar aqui" e "abrir o prédio vizinho". O balão fica ACIMA das tábuas
   (1000 a fechada, 1001 a aberta): à vista ele nunca cruza tábua nenhuma (`is-under-sign` o esconde), então na tela nada muda;
   e com o DEDO ganha uma folga invisível de 8 px de arte para CIMA e para os LADOS, que cobre o vão — o alvo cresce e nada
   cresce na tela, a regra do botão de texto da Faixa A (ui-gameplay.md §8.7). Para BAIXO, nada: embaixo do balão mora o
   próprio prédio, e o toque no meio dele continua abrindo a janela (com a folga também embaixo, o meio da caixa caía nela e o
   toque coletava em vez de abrir — visto no navegador). Escondido, o `::before` vai junto (`visibility` herda). Cerca:
   `client/tests/ui/islandReady.test.js` (a régua do toque, em cada vaga). */
.island-lots > .island-ready { z-index: 1002; }
@media (pointer: coarse) {
  .island-lots > .island-ready::before { content: ''; position: absolute; inset: calc(-8 * var(--px)) calc(-8 * var(--px)) 0; }
}

/* --- ilha-v2 --- (D25) `clip` em vez de `hidden`: `focus()` num botão da ilha rolava o container `hidden` 78 px (P31 do lobby-ilha). */
.island { overflow: clip; }
.island-ambient, .island-fxs { position: absolute; inset: 0; pointer-events: none; }
/* --- ilha-v2 --- (desvio P07-2 do PERGUNTAS.md) os lotes ganharam `z-index` = y (regra 4 do 07 §6, 108…253): sem um contexto
   próprio eles cobriam a barra (`z-index: 2`) e roubavam o clique dos botões dela. Com `z-index: 0` a fila iso continua valendo
   ENTRE os lotes, a barra volta a ficar por cima deles e a camada de obra (irmã) fica "por cima dela", como o 07 §5.2 item 7 diz. */
.island-lots { z-index: 0; }
/* --- ilha-v2 --- (desvio P07-2) e as PLACAS, que moram em `.island-lots` sem `z-index`, ficavam atrás de TODO prédio com base
   (a placa da fileira de baixo sumia atrás do prédio da fileira do meio — captura ilha-v2-07-mock-c-vaga.png). Acima de
   qualquer base (a maior é a altura da Faixa B), o nome nunca é cortado — a cerca de `client/tests/ui/islandPlates.test.js`. */
.island-lots > .island-plate-box { z-index: 1000; }
/* --- ajustes (2026-09-30) --- ABERTA pelo gesto (o nome apareceu), a tábua cresce para os lados e passa POR CIMA da vizinha
   fechada — `is-open` é posto por `widgets/islandMap.js › applyLit` junto com o `is-lit` da placa. */
.island-lots > .island-plate-box.is-open { z-index: 1001; }
/* --- ajustes (2026-09-30) --- a camada de obra ganha contexto PRÓPRIO (z 1): continua por cima dos lotes e das placas (z 0),
   mas abaixo da barra (z 2). Sem isto cada `.island-fx` (z = base + 1) competia com a barra no contexto da ilha e, com a barra
   da Casa em duas linhas, a limpeza das vagas 11 e 12 ao expandir 2 → 3 passava POR CIMA dos materiais
   (docs/specs/island.md §3.1, "Obra e expansão"). */
.island-fxs { z-index: 1; }
.island-amb, .island-fx { position: absolute; pointer-events: none; image-rendering: pixelated; background-repeat: no-repeat; }
/* --- ilha-v2 --- (D13) a TIRA: `background-size` = frames × 100 %; `steps(n)` anda um quadro por passo; `--fw` = largura de UM quadro. */
.island-lot.is-anim, .island-amb.is-anim, .island-fx.is-anim {
  animation: island-strip var(--dur, 1000ms) steps(var(--n, 1)) infinite;
}
.island-fx.is-once { animation-iteration-count: 1; animation-fill-mode: forwards; }
@keyframes island-strip { from { background-position-x: 0; } to { background-position-x: calc(var(--n, 1) * -1 * var(--fw, 0px)); } }
/* --- ajustes (2026-09-30, pedido do dono) --- VAGA TRANCADA: mato e pedra APAGADOS (G2: estado é forma + cor apagada — nunca
   vermelho, que é "está acontecendo"), com o disco do cadeado no monte e sem placa. Ela RECEBE o ponteiro: é o hover e o
   clique que trazem o motivo ("Desbloqueia com a ilha Nv N — expanda na sua casa.", `widgets/hint.js`, pelo `aria-disabled` +
   `setHint` do nó). `pointer-events: none` aqui calaria o motivo (guarda `client/tests/ui/disabledReason.test.js`). */
.island-lot.is-locked { pointer-events: auto; cursor: not-allowed; filter: grayscale(0.6) brightness(0.7); }
/* o CADEADO: o disco do `.node-mark-lock` (css/work.css, o lugar trancado do mapa de nódulos) a 2× — fator inteiro, 16×16 —,
   irmão do nó da vaga (a pilha: z = base + 1, por cima dela). `ico_lock` é o cadeado de aço canônico (UX-GRAMATICA §12.3).
   Leva o MESMO apagado da vaga (revisão, 2026-09-30): com o creme cheio, os onze discos eram o que mais saltava na conta nova
   e a seta de ouro da vaga livre — a que chama o clique — perdia para eles. */
.island-lock {
  position: absolute;
  width: calc(16 * var(--px));
  height: calc(16 * var(--px));
  background: var(--ui-ico_lock, none) center / 100% 100% no-repeat, #e8e2d0;
  border-radius: 50%;
  box-shadow: 0 0 0 var(--px) #17141f;
  image-rendering: pixelated;
  pointer-events: none;
  filter: grayscale(0.6) brightness(0.7);
}
/* lote clicável: o cursor diz que o clique faz algo (a vaga livre abre a janela Construir; o resto, a barra) */
button.island-lot { cursor: pointer; }

/* --- ajustes (2026-09-30, pedido do dono; "só o ícone" é decisão da sessão, padrão até ordem em contrário — island.md §10) ---
   A PLACA DE OFÍCIO (docs/specs/island.md §3.1 › Placas): uma tabuinha de
   madeira numa estaca, só com o ÍCONE 16×16 do lugar, plantada no pátio da FRENTE do próprio lote (`islandView.js ›
   signAnchor`). O nome mora no gesto (hover/foco abrem a tábua), na barra e no `aria-label` do lote. As regras abaixo são só
   de `.island-plate` e `.island-plate-box`: a `.npc-plate` de `screens.css` serve também a praça e continua como está — é
   ela que dá os 16 px de altura, esconde o nome (`.npc-plate-name`) e o revela no `is-lit`.
   A MOLDURA é a tábua `island_sign` (9-slice de fatia 4, como o tooltip); sem a peça no manifesto fica a moldura de sempre
   (G1 de docs/UX-GRAMATICA.md: sem a peça, o que havia — nunca um quadrado mudo). */
.island-plate {
  border: calc(4 * var(--px)) solid transparent;
  border-image: var(--ui-island_sign, var(--ui-tooltip, url(../assets/ui/slots/slot_empty.png))) 4 fill / calc(4 * var(--px)) stretch;
  image-rendering: pixelated;
}
/* `position: static` desfaz o `relative` que a `.npc-plate` ganhou por causa do selo da praça (screens.css): a ESTACA
   (abaixo) se conta da CAIXA, não do miolo da placa — contada da placa, o `top: 100%` pararia 4 px acima da aresta de
   baixo, na borda de dentro da moldura. Dois seletores de classe: vence a regra da praça em qualquer ordem de folha. */
.island-plate-box > .island-plate { position: static; }
/* FECHADA e só com ícone: 1 px de respiro de cada lado (o molde da placa de POI da praça, `.city-poi-plate`) — 26 px com
   um ícone, 45 com os dois da Casa. Aberta, ou com o nome sempre escrito (sem o ícone no manifesto; `Construir aqui`), vale
   o respiro de 3 px à direita do nome que a `.npc-plate` já dá. */
.island-plate.is-icon:not(.is-lit) { padding: 0 calc(1 * var(--px)); }
/* sobre a madeira a letra é ESCURA — e continua escura acesa: o ouro do `.npc-plate.is-lit` some na tábua clara. Quem diz
   "aceso" é o aro de ouro de 1 px (o `box-shadow` do `is-lit`, que fica). `.has-sign` vem do manifesto (`widgets/islandMap.js`):
   sem a arte da tábua, a moldura escura de sempre com a letra clara de sempre. */
.island.has-sign .island-plate,
.island.has-sign .island-plate.is-lit { color: var(--c-text-dark); }
/* a ESTACA é o `::after` da PRÓPRIA placa — um nó só: o clique na estaca é o clique no lote, e nada novo cobre o terreno
   com ponteiro (o "clique fora" que fecha a barra continua chegando a `.island-ground`). Ela se conta da CAIXA
   (`.island-plate-box`, que tem o tamanho da placa): `top: 100%` é a aresta de baixo da tábua e `left: 50%` o centro dela,
   mais `--post-dx` — o desvio que mantém a estaca sob o centro do LOTE quando a borda da Faixa B segura a tábua. Tamanho e
   encaixe vêm do MANIFESTO (`--sign-post-w/h/dy`, escritos por `widgets/islandMap.js`; docs/ux-casos.md §13.3): a peça
   mais alta que os 4 px à vista sobe por TRÁS da tábua (`z-index: -1`, dentro do contexto da caixa). A regra só vale com a
   peça `island_sign_post` no manifesto (`.has-sign-post`) — sem ela não há estaca, nem um quadrado mudo no lugar (G1). */
.island.has-sign-post .island-plate::after {
  content: '';
  position: absolute;
  z-index: -1;
  left: calc(50% + (var(--post-dx, 0) - var(--sign-post-w, 4) / 2) * var(--px));
  top: calc(100% + var(--sign-post-dy, 0) * var(--px));
  width: calc(var(--sign-post-w, 4) * var(--px));
  height: calc(var(--sign-post-h, 4) * var(--px));
  background: var(--ui-island_sign_post, none) no-repeat 0 0 / 100% 100%;
  image-rendering: pixelated;
}
/* com a barra contextual aberta some toda placa que ela cruzaria — a do lote selecionado inclusive: a barra já diz o nome
   (G6). `visibility`, não `display`: a placa continua medida e guarda o lugar (`widgets/islandMap.js › syncUnderBar`). */
.island-plate-box.is-under-bar { visibility: hidden; }
/* --- ajustes (2026-09-30) --- a placa da VAGA LIVRE ("Construir aqui") só aparece no hover/foco (`is-lit`, o `lit()` de
   `widgets/islandMap.js`). `visibility`, não `display`: ela continua medida no `plateLayout` e guarda o lugar. */
.island-plate-box.is-peek > .island-plate:not(.is-lit) { visibility: hidden; }
/* --- ajustes (2026-09-30, revisão) --- a placa SEMPRE À VISTA (prédio, casa, portal) recebe o ponteiro e é do PRÓPRIO lote
   (`widgets/islandMap.js › buildLot`): ela mora no pátio da frente, fora do retângulo do botão, e aberta passa sobre os
   vizinhos — com o `pointer-events: none` da `.npc-plate-box` (screens.css), o clique nela cairia no terreno ou no lugar de
   trás. A da vaga livre (`is-peek`) continua sem: invisível até o gesto, não rouba o ponteiro de ninguém. */
.island-lots > .island-plate-box:not(.is-peek) > .island-plate { pointer-events: auto; cursor: pointer; }
/* a SETA que quica sobre a cerca da vaga livre é o `.npc-marker` (screens.css: `steps(2)` a 0,8 s, parada em movimento
   reduzido) a 2× — 18×18, fator inteiro (revisão: a 1× ela perdia para os discos do cadeado); com janela aberta ela se cala —
   chamaria para um clique que a janela cobre */
.island-lot > .island-arrow { width: calc(18 * var(--px)); height: calc(18 * var(--px)); }
.island.has-window .island-arrow { display: none; }
/* --- ajustes (2026-09-30) --- e some enquanto uma tábua À VISTA a cruzaria (`widgets/islandMap.js › syncArrows`): aberta pelo
   gesto, a tábua da fileira do meio cobria a metade de cima da seta da vaga livre de baixo e a ponta de ouro ficava espiando
   por baixo da madeira. `visibility`, não `display`: a seta continua no compasso do quique e volta inteira. */
.island-lot > .island-arrow.is-under-sign { visibility: hidden; }
@media (prefers-reduced-motion: reduce) {
  .island-lot.is-anim, .island-amb.is-anim, .island-fx.is-anim { animation: none; }
}

/* --- ilha-v2 --- a janela Construir: até 6 cartões lado a lado (104 px até 5); o prédio a 1× (64 × 64) no topo do cartão.
   --- ajustes (2026-09-30, pedido do dono) --- os cartões de uma aba têm a altura do MAIS ALTO deles (`stretch`): o custo e o
   botão (ou o selo) moram no pé de todos, na mesma altura. A fileira tem a altura do conteúdo, não a da janela: a janela é
   alta para o pior caso (3 materiais e a fonte 25 % mais larga), e esticar os cartões até ela abria um buraco de ~70 px entre
   o que o prédio é e o que ele custa na aba Nv 1 (só ouro) — o vão que sobra fica embaixo, sob os cartões. */
.ib-body { display: flex; flex-direction: column; gap: calc(4 * var(--px)); }
.ib-tabs { flex: none; }
/* --- conserto (2026-09-30, revisão) --- quem ROLA é a fileira de cartões, não o corpo (docs/specs/island.md §3.1 › O
   tamanho): com 4 a 6 materiais (o schema aceita 6) o corpo inteiro rolava sem o degradê — a barra deste Chromium é overlay
   —, as abas iam junto e o `Construir` ficava cortado pela moldura (o B74). A caixa ocupa o resto do corpo (`flex: 1`), abaixo
   das abas; a fileira continua com a altura do conteúdo, presa no topo dela: `wrap` + `align-content: flex-start` fazem a
   LINHA ter a altura do cartão mais alto (numa caixa de uma linha só, `stretch` esticaria os cartões até o pé da caixa — o
   buraco de ~70 px da aba só de ouro). O degradê é o de `scroll-fade.css`, só com `is-scrollable` (MEDIDO por
   `islandBuild.js › syncOverflow`): sem transbordo os 12 px de baixo seriam o `Construir` desbotado à toa. Rolando, a folga
   do pé é a altura inteira do degradê, e no fim da rolagem o botão fica todo à vista. */
.ib-cards {
  flex: 1 1 auto;
  min-height: 0;
  overflow-x: hidden;
  overflow-y: auto;
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  align-items: stretch;
  gap: calc(4 * var(--px));
  --scroll-fade-pad: calc(12 * var(--px));
}
.ib-cards:not(.is-scrollable) { --scroll-fade: 0px; --scroll-fade-pad: 0px; }
/* --- 2026-10-01 (a Destilaria separada do Laboratório: a aba Nv 1 passou a ter 6 prédios) --- a largura do cartão sai da
   LINHA (`flex: 1 1 0`) entre 72 (o prédio de 64 + o respiro) e 104 px: com até 5 cartões nada muda (104 cada, encostados à
   esquerda), e com 6 eles dividem a fileira (~88 px) numa linha só — com 104 fixos o 6.º quebrava para uma 2.ª linha escondida
   pelo pé da caixa que rola, e era justamente o prédio novo. Abaixo de 72 a fileira volta a quebrar (nunca um prédio cortado). */
.ib-card { position: relative; flex: 1 1 0; min-width: calc(72 * var(--px)); max-width: calc(104 * var(--px)); display: flex; flex-direction: column; align-items: center; gap: calc(2 * var(--px)); padding: calc(2 * var(--px)); }
.ib-sprite { flex: none; width: calc(64 * var(--px)); height: calc(64 * var(--px)); background-repeat: no-repeat; image-rendering: pixelated; }
.ib-sprite.is-anim { animation: island-strip var(--dur, 1000ms) steps(var(--n, 1)) infinite; }
.ib-sprite.is-missing { background: var(--c-field-bg); }   /* sem arte ainda: caixa vazia, não buraco */
/* o nome QUEBRA em vez de vazar (com uma fonte 25 % mais larga, "Centro de Exploração" mede 120 px no cartão de 100 de miolo e
   cruzava o vizinho); com a Courier todo nome do catálogo cabe numa linha no cartão de 104 (o maior mede 96), e nos SEIS
   cartões de 88 da aba Nv 1 (84 de miolo) o "Centro de Exploração" quebra em duas — de propósito: cortado, não */
.ib-name { max-width: 100%; text-align: center; }
.ib-opens { max-width: 100%; text-align: center; min-height: calc(16 * var(--px)); }
.ib-seal { display: inline-flex; align-items: center; height: calc(14 * var(--px)); padding: 0 calc(3 * var(--px)); border: var(--px) solid var(--c-text-dim); color: var(--c-text-dim); }
.ib-seal.is-done { border-color: var(--c-gold); color: var(--c-gold); }
/* --- ajustes (2026-09-30) --- o cartão acima do nível da ilha fala a MESMA língua do mapa (G5): `[cadeado] Ilha Nv N`, com a
   cor de perigo — o `ico_lock` a 1× (8×8) antes do texto. */
.ib-seal.is-locked { border-color: var(--c-danger); color: var(--c-danger); gap: calc(2 * var(--px)); }
.ib-seal-lock { width: calc(8 * var(--px)); height: calc(8 * var(--px)); background: var(--ui-ico_lock, none) center / 100% 100% no-repeat; image-rendering: pixelated; flex: none; }
/* --- ajustes (2026-09-30, revisão) --- o selo OU o botão (um só por cartão, o último filho) cola no pé do cartão: a descrição
   de uma linha (Portal de Masmorras) subia o selo ~4 px em relação aos de duas linhas, e a moldura vermelha deixava à vista. */
.ib-card > .ib-seal, .ib-card > .btn { margin-top: auto; }
/* --- ajustes (2026-09-30, pedido do dono: "os botões uns em cima dos outros … exiba os materiais que precisa além do ouro,
   ícone quantidade") --- O CUSTO POR EXTENSO acima do botão (docs/specs/island.md §3.1 › Janela Construir). O botão diz só
   `Construir` (o preço com "2 materiais" dentro dele media 165 px num cartão de 104 e encavalava no vizinho). Com o custo, é
   ELE quem leva a margem automática e o botão vem colado embaixo (duas margens automáticas dividiriam o vão e nenhum dos dois
   ficaria no pé).
   Uma exigência por linha, na gramática da linha "Expandir ilha" da Casa (as classes `.island-req*` dela, acima: ✔ na cor de
   ok, ✖ e o valor na cor de falta — a cor e o sinal juntos, §8.7). É uma TABELA de três colunas que todas as linhas dividem
   (`subgrid`): o ícone (16), o valor encostado à direita (números se leem em coluna — ux-casos §13.5) e o ✔/✖ (8). O ouro é
   `[moeda] 9.000`; o material é o ícone na casinha de mini-slot de 16 com o selo de tier ↖ (a do banco e do Pronto do Refino,
   `windows.css › .bankmat-ico`) e `tem/precisa`. O ícone de quem falta NÃO se apaga: é o que o jogador precisa reconhecer. */
.ib-card > .ib-cost { margin-top: auto; }
.ib-card > .ib-cost + .btn { margin-top: 0; }
/* `padding-top`: no cartão mais cheio da aba o custo encosta no que o prédio abre, e o vão entre as duas famílias (o que o
   prédio é × o que ele custa) fica o dobro do vão entre as linhas do custo (G1: separador entre famílias) */
.ib-cost {
  padding-top: calc(2 * var(--px));
  display: grid;
  grid-template-columns: calc(16 * var(--px)) auto calc(8 * var(--px));
  column-gap: calc(2 * var(--px));
  row-gap: calc(2 * var(--px));
  align-items: center;
  max-width: 100%;
}
.ib-cost > .island-req { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; align-items: center; white-space: nowrap; }
.ib-cost > .island-req > .ico-gold { justify-self: center; }
.ib-cost .island-req-val { justify-self: end; }
/* o material é CONSULTA (clique = "Onde conseguir", botão direito = o menu de item): o realce do hover e do foco é o aro de
   ouro na casinha, o equivalente do `slot_hover` do slot de 36 da Casa; o contorno de foco é o de `.island-req[role='button']` */
.ib-cost > .island-req[role='button']:hover .ib-mat-ico,
.ib-cost > .island-req[role='button']:focus-visible .ib-mat-ico { box-shadow: inset 0 0 0 var(--px) var(--c-gold); }
@media (prefers-reduced-motion: reduce) { .ib-sprite.is-anim { animation: none; } }

/* --- 2026-10-01 (pedido do dono: "abra diretamente a interface do plot … nessa interface tem que ter uma opção de dar upgrade,
   mostrando os materiais que precisa … o nível do plot") --- A INTERFACE DO PRÉDIO (docs/specs/island.md §3.1;
   `widgets/plotUpgrade.js`): o SELO de nível na barra de título da janela que o prédio abre (Oficina, Refino) e o PAINEL de
   melhoria que ele abre por cima do alto do corpo.
   O selo: os degraus `ico_lot_level` (16, o `[degraus] 2` da barra da ilha) + o número + `▲`; quando dá para melhorar AGORA, o
   martelo `ico_lot_up` no lugar do `▲` (o selo do lote na ilha, com o quadro aceso no mesmo compasso); no topo, os degraus de
   ouro. 16 de altura, a do `×` da barra de título — sem a moldura 9-slice do `.btn`, cujas bordas de 4 + 4 não deixam um ícone
   de 16: o aro é de 1 px, apagado, e acende em ouro no hover, no foco e com o painel aberto. */
.plot-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: calc(1 * var(--px));
  box-sizing: border-box;
  height: calc(16 * var(--px));
  margin: 0;
  padding: 0 calc(3 * var(--px)) 0 calc(1 * var(--px));
  border: 0;
  background: var(--c-field-bg);
  box-shadow: inset 0 0 0 var(--px) var(--c-text-dim);
  font: inherit;
  color: var(--c-text);
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
}
.plot-chip:hover, .plot-chip.is-open { box-shadow: inset 0 0 0 var(--px) var(--c-gold); color: var(--c-gold); }
.plot-chip:focus-visible { outline: var(--px) solid var(--c-gold); outline-offset: var(--px); }
.plot-chip-ico {
  flex: none;
  width: calc(16 * var(--px));
  height: calc(16 * var(--px));
  background: var(--ui-ico_lot_level, none) no-repeat 0 0 / 100% 100%;
  image-rendering: pixelated;
}
.plot-chip-ico.is-max { background-image: var(--ui-ico_lot_max, none); }
.plot-chip-ico.is-missing { display: none; }
.plot-chip-up { color: var(--c-text-dim); }
.plot-chip-badge {
  position: relative;
  flex: none;
  width: calc(12 * var(--px));
  height: calc(12 * var(--px));
  background: var(--ui-ico_lot_up, none) no-repeat 0 0 / 100% 100%;
  image-rendering: pixelated;
}
.plot-chip-badge::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--ui-ico_lot_up_lit, none) no-repeat 0 0 / 100% 100%;
  image-rendering: pixelated;
  opacity: 0;
  animation: island-badge-lit 2.4s step-end infinite;
}
/* O PAINEL: encostado à direita no alto do corpo da janela (`.craft-body` e `.refine-win` são `position: relative`), por cima
   das colunas — é consulta e decisão de um passo, e fechar devolve a janela inteira. A moldura de 4 px é a da barra da ilha
   (`.tooltip-frame`); o custo é a TABELA do cartão da janela Construir (`.ib-cost`: ícone · valor à direita · ✔/✖). */
.plot-up {
  position: absolute;
  top: 0;
  right: 0;
  z-index: 30;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: calc(3 * var(--px));
  min-width: calc(128 * var(--px));
  padding: calc(1 * var(--px)) calc(3 * var(--px)) calc(2 * var(--px));
  white-space: nowrap;
}
.plot-up-head { display: flex; align-items: center; gap: calc(4 * var(--px)); align-self: stretch; }
.plot-up-to { color: var(--c-gold); }
.plot-up-close { margin-left: auto; padding: 0 calc(3 * var(--px)); }
.plot-up-cost { padding-top: 0; }
@media (prefers-reduced-motion: reduce) { .plot-chip-badge::after { animation: none; } }
