/* MesaVTT — visual minimalista, escuro, acento vermelho (preto e vermelho, referência
   ao logo — pentagrama vermelho, cor amostrada direto do PNG: #d20f11) */
:root {
  --bg: #0d0d10;
  --panel: #16161b;
  --panel-2: #1e1e25;
  --line: #28282f;
  --text: #ececf1;
  --text-dim: #8b8b97;
  --accent: #c81e1e;
  --accent-hover: #e13a3a;
  --accent-ink: #ffffff;
  /* "perigo" (excluir/remover) fica em laranja, não vermelho — senão coincidiria
     visualmente com o acento vermelho novo e confundiria botão primário com destrutivo */
  --danger: #f97316;
  --danger-dim: #fdba74;
  --font: 'Inter', system-ui, sans-serif;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 14px;
}

h1, h2, h3 { font-family: var(--font); font-weight: 600; }

/* ---------- Landing ---------- */
/* (o redesign estilo Alchemy com as artes de personagem foi tentado em 3 versões e
   revertido por inteiro a pedido — "volte ao normal antes de decidirmos colocar a arte,
   com o menu no meio"; as artes continuam em public/img, incluindo as versões
   otimizadas *_hero.webp, pra uma próxima tentativa) */
body.landing {
  min-height: 100vh;
  display: grid;
  place-items: center;
  /* vinheta avermelhada: brilho forte no topo (atrás do selo) + um segundo mais fraco
     embaixo, e escurecimento nas bordas pra concentrar o olhar no centro */
  background:
    radial-gradient(ellipse 60% 45% at 50% 12%, rgba(200, 30, 30, 0.22), transparent 70%),
    radial-gradient(ellipse 80% 40% at 50% 115%, rgba(200, 30, 30, 0.08), transparent 70%),
    radial-gradient(ellipse 120% 100% at 50% 50%, transparent 55%, rgba(0, 0, 0, 0.45)),
    var(--bg);
}

.landing-card { width: min(420px, 92vw); padding: 32px 0; }

/* selo (já traz o nome escrito) como herói acima do menu, no fluxo normal. O h1 existe
   só pra acessibilidade/SEO — o nome visível é o do próprio selo */
.brand-wrap { display: flex; justify-content: center; margin: 0 0 24px; }
.brand-seal { margin: 0; line-height: 0; }
.brand-logo {
  width: min(260px, 62vw);
  height: auto;
  display: block;
  /* halo vermelho suave atrás do selo, mais uma sombra de contato pra dar profundidade */
  filter: drop-shadow(0 0 28px rgba(200, 30, 30, 0.35)) drop-shadow(0 8px 14px rgba(0, 0, 0, 0.6));
  transition: transform 0.25s ease, filter 0.25s ease;
}
.brand-logo:hover {
  transform: scale(1.04) rotate(-2deg);
  filter: drop-shadow(0 0 38px rgba(200, 30, 30, 0.5)) drop-shadow(0 8px 14px rgba(0, 0, 0, 0.6));
}
.brand {
  position: relative;
  z-index: 1;
  font-size: 34px;
  font-weight: 700;
  margin: 0 0 28px;
  text-align: center;
  letter-spacing: -0.5px;
  /* halo escuro atrás das letras — sem isso o texto (branco/vermelho) se perde em cima
     do logo (também branco/vermelho) na tela inicial; camadas de raio crescente simulam
     um contorno suave em vez de uma sombra deslocada de um lado só */
  text-shadow:
    0 0 6px rgba(0, 0, 0, 0.95),
    0 0 14px rgba(0, 0, 0, 0.9),
    0 0 24px rgba(0, 0, 0, 0.75),
    0 2px 3px rgba(0, 0, 0, 0.9);
}
.brand span { color: var(--accent); }
/* margin:0 sobrescrevendo o `margin:0 0 28px` de `.brand` (esse é só pro título da tela
   inicial, sobre o logo — aqui, dentro da `.topbar` da mesa, ele inflava a barra inteira) */
.brand.small { font-size: 18px; text-align: left; text-decoration: none; color: var(--text); margin: 0; }
/* só o bode (goat_nav.png, versão 96px de goat.png) no lugar do texto na navbar da mesa */
.brand-small-seal { display: flex; align-items: center; line-height: 0; }
/* margem negativa: o bode (32px) ocupa só 24px de layout, então a .topbar continua com
   40px exatos (os painéis do mestre/jogadores em top:56px dependem dessa altura).
   A arte é preta com detalhes brancos — o halo vermelho evita que a silhueta suma no
   fundo escuro da barra */
.brand-small-seal img {
  height: 32px; width: auto; margin: -4px 0; display: block;
  filter: drop-shadow(0 0 5px rgba(200, 30, 30, 0.6));
  transition: transform 0.2s ease, filter 0.2s ease;
}
.brand-small-seal:hover img { transform: scale(1.1) rotate(-3deg); filter: drop-shadow(0 0 8px rgba(225, 58, 58, 0.9)); }

.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 20px;
}
.panel h2 { margin: 0 0 14px; font-size: 16px; font-weight: 600; }

/* ---------- Cartões expansíveis da tela inicial (criar/carregar/entrar) ---------- */
.action-cards { display: flex; flex-direction: column; gap: 10px; margin-bottom: 18px; }
.action-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
  transition: border-color 0.15s;
}
.action-card:hover { border-color: var(--accent); }
.action-card.expanded { border-color: var(--accent); }

.action-card-header {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 18px;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  color: var(--text);
  font-family: var(--font);
}
.action-card-icon {
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  background: var(--panel-2);
  color: var(--accent);
  transition: background 0.15s;
}
.action-card-icon svg { width: 20px; height: 20px; }
.action-card.expanded .action-card-icon { background: var(--accent); color: var(--accent-ink); }
.action-card-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.action-card-title { font-size: 15px; font-weight: 600; }
.action-card-desc { font-size: 12px; color: var(--text-dim); }
.action-card-chevron {
  color: var(--text-dim);
  font-size: 20px;
  line-height: 1;
  flex-shrink: 0;
  transition: transform 0.2s, color 0.2s;
}
.action-card.expanded .action-card-chevron { transform: rotate(90deg); color: var(--accent); }

/* técnica grid-template-rows 0fr -> 1fr: anima até a altura de conteúdo sem precisar
   medir em JS (max-height exigiria um valor fixo grande o bastante e chutado) */
.action-card-collapse {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.25s ease;
}
.action-card.expanded .action-card-collapse { grid-template-rows: 1fr; }
/* o item do grid (`.action-card-body`) não pode ter padding — padding nunca é
   comprimido por overflow:hidden nem por min-height:0, então a linha nunca fecharia
   até 0 de verdade (ficaria travada na altura do padding). O padding visual mora no
   wrapper INTERNO (`-inner`), que não é ele mesmo o item do grid */
.action-card-body { overflow: hidden; min-height: 0; }
.action-card-body-inner { padding: 0 18px 18px; }
.action-card-body-inner label:first-child { margin-top: 2px; }
.action-card-empty { color: var(--text-dim); font-size: 13px; margin: 0; }

/* max-height+scroll interno (mesmo padrão de .library-list/.door-sounds-modal .merge-list)
   — reportado com print: muitas pastas/mesas fazem o card inteiro crescer sem limite e
   empurrar o resto da tela inicial pra fora ("o conteudo sai da tela"). A lista agora
   pára de crescer a partir de 46vh e rola por dentro; o resto do card (breadcrumb,
   "+ Nova pasta", mensagem de vazio) continua sempre visível, fora da área que rola */
.room-list { display: flex; flex-direction: column; gap: 8px; max-height: 46vh; overflow-y: auto; padding-right: 4px; }
/* virou COLUNA (nome numa linha, botões de ação na linha de baixo) — pedido explícito
   comparando com print: "o nome da mesa fica muito apertado ali... aumentar
   horizontalmente" foi descartado de propósito (alargar a página quebraria a coluna
   estreita centralizada que login/criar/etc já usam) a favor de dar mais respiro
   VERTICAL ao nome em vez de espremer ele contra 3 botões na mesma linha — mesma técnica
   já usada pro popup "Sons" quando o nome de som ficou espremido lá */
.room-list-item {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  padding: 10px 12px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  font-size: 14px;
  transition: border-color 0.15s;
}
.room-list-item:hover { border-color: var(--accent); }
.room-list-link {
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  color: var(--text);
  text-decoration: none;
}
/* linha de baixo: renomear/código/excluir (mesas próprias) — alinhados à direita, mesmo
   lugar visual de quando ainda dividiam a linha com o nome */
.room-list-actions { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 8px; }
/* pasta de organização na lista "Carregar mesa" (estilo biblioteca de tokens) — mesma
   linha (.room-list-rename-row) do modo "renomear" mesa, só que ícone+nome+excluir em
   vez de thumb+nome+salvar/cancelar */
.room-folder-icon {
  width: 32px;
  height: 32px;
  border-radius: 6px;
  background: var(--panel);
  display: grid;
  place-items: center;
  color: var(--accent);
  flex-shrink: 0;
}
.room-folder-icon svg { width: 60%; height: 60%; }
/* select "mover pra pasta" — mesma pílula discreta do .room-list-copy ao lado, só que é
   um <select> (precisa de padding/tamanho de fonte próprios, a regra genérica de
   formulário do app deixaria ele grande/branco demais nesse contexto compacto) */
.room-folder-select {
  flex-shrink: 0;
  display: inline-block;
  width: auto;
  max-width: 130px;
  margin-top: 0;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--text-dim);
  font-family: var(--font);
  font-size: 11px;
  padding: 3px 6px;
  cursor: pointer;
}
.room-folder-select:hover { border-color: var(--accent); color: var(--text); }
/* modo "renomear": thumb+input+salvar+cancelar viram UMA linha só (não mais um <a> — ver
   comentário no JS), envolvidos nesse wrapper pra continuar sendo o único filho direto
   do item (que agora é coluna) e não espalhar cada botão numa linha própria */
.room-list-rename-row { display: flex; align-items: center; gap: 8px; }
.room-list-date { color: var(--text-dim); font-size: 12px; flex-shrink: 0; }
/* miniatura do mapa (tela inicial): retrato pequeno em paisagem, cortado pra caber —
   quando a mesa não tem mapa o <img> nem existe (onerror remove), o item fica como antes */
.room-list-thumb {
  width: 52px;
  height: 36px;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--line);
  flex-shrink: 0;
}
/* flex:1 pro nome ocupar o meio (a miniatura entrou como primeiro filho do link — sem
   isso o space-between do link espalharia thumb/nome/data de um jeito estranho) */
.room-list-name-wrap { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1; }
.room-list-name-wrap > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.room-status-badge {
  flex-shrink: 0;
  font-size: 11px;
  font-weight: 600;
  padding: 2px 7px;
  border-radius: 999px;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.room-status-badge.closed { background: rgba(200, 30, 30, 0.15); color: var(--accent-hover); }
.room-status-badge.private { background: rgba(239, 68, 68, 0.15); color: var(--danger-dim); }
/* selo POSITIVO (verde): "Mestre online" no histórico do jogador, "N online" nas mesas
   do mestre — mesma pílula dos selos de bloqueio, só a cor diz "pode entrar/tem gente" */
.room-status-badge.online { background: rgba(34, 197, 94, 0.15); color: #4ade80; }

/* botão "copiar link" em cada mesa da lista "Carregar mesa" (tela inicial) — corta o
   passo de precisar ENTRAR na mesa só pra pegar o link de convite na navbar de lá */
.room-list-copy {
  flex-shrink: 0;
  background: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--text-dim);
  font-family: var(--font);
  font-size: 11px;
  padding: 3px 9px;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s;
}
.room-list-copy:hover { border-color: var(--accent); color: var(--text); }
.room-list-copy.copied { border-color: #4ade80; color: #4ade80; }

/* olhinho de mostrar/ocultar senha — o wrap segura o posicionamento absoluto do botão
   por cima do canto direito do input; o margin-top que o input teria (regra global de
   input) sobe pro wrap, senão o botão ficaria descentralizado em relação ao input */
/* botão de texto discreto (ex.: "Esqueci minha senha", "Voltar" nos painéis de auth) —
   sem fundo/borda, sublinhado, cor neutra que acende no acento ao passar o mouse */
.link-btn {
  display: block;
  width: auto;
  background: none;
  border: none;
  padding: 8px 0 0;
  margin: 0;
  font-size: 13px;
  color: var(--text-dim);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.link-btn:hover { color: var(--accent); }

.password-wrap { position: relative; display: block; margin-top: 6px; }
.password-wrap input[type="password"], .password-wrap input[type="text"] { margin-top: 0; padding-right: 40px; }
.password-toggle {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  width: 28px;
  height: 28px;
  padding: 4px;
  background: none;
  border: none;
  color: var(--text-dim);
  cursor: pointer;
}
.password-toggle:hover { color: var(--text); }
.password-toggle svg { width: 100%; height: 100%; display: block; }

.user-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 10px 14px;
  margin-bottom: 20px;
  font-size: 13px;
  color: var(--text-dim);
}
.user-bar strong { color: var(--text); }
.user-bar[hidden] { display: none; }
/* Menu do usuário — clicar no avatar/nome abre um dropdown com Perfil/Armazenamento/
   Sair; "amigos" fica de fora como .icon-btn próprio (badge de pendências sempre
   visível). Substituiu os 4 chips de texto lado a lado (.user-bar-actions, removida) */
.user-menu-wrap { position: relative; min-width: 0; }
.user-menu-trigger {
  display: flex; align-items: center; gap: 8px;
  background: none; border: none;
  padding: 4px 8px; margin: -4px 0 -4px -8px;
  border-radius: 8px;
  font: inherit; color: inherit;
  cursor: pointer;
  min-width: 0;
}
.user-menu-trigger:hover { background: var(--panel-2); }
.user-menu-chevron { font-size: 9px; color: var(--text-dim); transition: transform 0.15s ease; }
.user-menu-trigger.open .user-menu-chevron { transform: rotate(180deg); }
.user-menu-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  min-width: 180px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  z-index: 50;
}
/* display:flex acima é regra de autor e venceria o [hidden] padrão do navegador —
   mesma armadilha já documentada n vezes nesse arquivo */
.user-menu-dropdown[hidden] { display: none; }
.user-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-radius: 6px;
  padding: 8px 10px;
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
}
.user-menu-item:hover { background: var(--panel-2); }
.user-menu-item-danger { color: var(--danger); }
.user-menu-item-danger:hover { background: rgba(249, 115, 22, 0.08); }

/* ---------- Avatares / perfil ---------- */
.avatar-small { width: 24px; height: 24px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.avatar-large {
  width: 150px; height: 150px; border-radius: 50%; object-fit: cover; flex-shrink: 0;
  background: var(--panel-2); border: 1px solid var(--line);
}
.avatar-placeholder {
  display: flex; align-items: center; justify-content: center;
  color: var(--text-dim); font-size: 52px; font-weight: 600;
}
/* `display: flex` acima é regra de autor e vence a regra padrão do navegador pra
   [hidden] (que é só do user-agent) — sem isso o placeholder nunca some de verdade
   e fica em cima da foto de verdade (mesmo z-order dos dois em profile-avatar-wrap) */
.avatar-placeholder[hidden] { display: none; }
/* pedido explícito: "aumente o popup do perfil para que seja mais visivel e maior
   tanto a foto de perfil como o banner" — 420→540px, banner/avatar escalados junto
   (ver .profile-banner/.avatar-large/.profile-avatar-wrap abaixo). Bug real achado ao
   vivo: essa regra fica ANTES de `.modal` (width:360px) no arquivo — com as duas com a
   MESMA especificidade (uma classe só), quem vence é ordem no arquivo, e `.modal` vinha
   DEPOIS, sobrescrevendo o width pretendido silenciosamente (o modal de perfil nunca
   teve o tamanho declarado aqui, nem antes desse bullet — só ninguém tinha medido em
   pixel real pra notar). `.modal.profile-modal` (as DUAS classes) tem especificidade
   maior de propósito, pra não depender de ordem nenhuma no arquivo daqui pra frente */
.modal.profile-modal { width: min(540px, 92vw); position: relative; }
/* X sutil no canto (pedido explícito: "em vez de um botão fechar vamos colocar um
   sutil X no canto superior direito") — substituiu o botão "Fechar" de sempre no
   rodapé; mesmo id (#profile-close), só a aparência/posição mudou, o listener de
   clique não precisou de nenhum ajuste */
.modal-close-x {
  position: absolute;
  top: 14px;
  right: 14px;
  /* sem isso, o X fica atrás do banner do perfil — os dois são elementos
     posicionados sem z-index, então quem vence é ordem no DOM, e o banner vem
     DEPOIS do botão no markup (reportado com print: "o X de fechar está por
     baixo do banner"). Precisa ficar acima de qualquer conteúdo do modal (banner,
     avatar, etc.), não só do banner especificamente */
  z-index: 5;
  width: 26px;
  height: 26px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  border-radius: 6px;
  color: var(--text-dim);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.modal-close-x:hover { color: var(--text); background: var(--panel-2); }
/* banner estilo Discord no topo do perfil (imagem 3:1) — o avatar sobrepõe a borda de
   baixo via margin-top negativo no wrap (por isso o align-items: flex-end na row,
   senão o align center anularia o efeito do margin) */
.profile-banner {
  position: relative; /* âncora do overlay de hover (::after, inset:0) */
  height: 168px;
  border-radius: 10px;
  background-color: var(--panel-2);
  background-size: cover;
  background-position: center;
  border: 1px solid var(--line);
  cursor: pointer;
}
/* clicar no próprio banner/avatar troca a imagem (sem botões separados) — o overlay
   de hover com o texto é a única affordance de que dá pra clicar, então precisa existir */
.profile-banner::after {
  content: 'Trocar imagem';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.45);
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  border-radius: 10px;
  opacity: 0;
  transition: opacity 0.15s;
  pointer-events: none; /* o clique é do banner em si */
}
.profile-banner:hover::after { opacity: 1; }
/* pedido explícito ("pode colocar embaixo da foto parecido com o twitter") — a linha
   agora só carrega o avatar; o nome vira um bloco SEPARADO logo abaixo (.profile-name-below),
   não mais um segundo item ao lado dele nesse flex row */
.profile-photo-row { display: flex; align-items: flex-end; margin-bottom: 6px; }
.profile-avatar-wrap { position: relative; width: 150px; height: 150px; flex-shrink: 0; margin-top: -56px; margin-left: 16px; cursor: pointer; }
.profile-avatar-wrap .avatar-large { position: absolute; top: 0; left: 0; border: 4px solid var(--panel); }
.profile-avatar-wrap::after {
  content: 'Trocar foto';
  position: absolute;
  inset: 4px; /* dentro da borda de 4px do avatar, pro círculo do overlay casar com o da foto */
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  border-radius: 50%;
  opacity: 0;
  transition: opacity 0.15s;
  pointer-events: none;
}
.profile-avatar-wrap:hover::after { opacity: 1; }
/* modo "ver perfil de outro amigo" (só leitura) — mesmas classes/tamanhos do modal de
   edição, só sem a affordance de clicar-pra-trocar (sem overlay, sem cursor de mão) */
.profile-banner.readonly, .profile-avatar-wrap.readonly { cursor: default; }
.profile-banner.readonly::after, .profile-avatar-wrap.readonly::after { content: none; }
/* nome do perfil (o PRÓPRIO ou o de um amigo visto via friends.js) EMBAIXO do avatar,
   estilo Twitter/X — margin-left casa com o margin-left:16px de .profile-avatar-wrap
   acima, pro texto alinhar com a borda esquerda da foto, não com a borda do modal.
   Seletor com DUAS classes de propósito (não só `.profile-name-below`) — `.modal h2`
   (classe+elemento, especificidade 1,1) venceria uma classe só (especificidade 1,0)
   MESMO vindo depois no arquivo; `.profile-modal .profile-name-below` (duas classes,
   2,0) bate `.modal h2` sem depender de ordem — mesma categoria de bug já documentada
   no bullet do resize do modal de perfil */
.profile-modal .profile-name-below { margin: 0 0 2px 16px; font-size: 20px; }
/* horas jogadas (estilo Steam), logo abaixo do nome — os DOIS modais (editar o próprio
   e ver o de um amigo) têm essa linha, então o respiro de 18px que era do nome desceu
   pra cá (2px entre nome e horas, 18px depois — total igual ao de antes). Mesma razão
   do seletor com duas classes do .profile-name-below acima */
.profile-modal .profile-hours { margin: 0 0 18px 16px; font-size: 12px; color: var(--text-dim); }
/* espaço acima do <details> "Trocar senha" (substituiu o antigo <hr class="profile-divider">
   — o próprio .tool-group já traz a linha de cima, só faltava o respiro) */
#profile-password-group { margin-top: 14px; }
.profile-modal h3 { font-size: 14px; margin: 0 0 12px; color: var(--text); }

.crop-modal { width: min(360px, 92vw); text-align: center; }
.crop-viewport {
  width: 300px; height: 300px; max-width: 100%;
  margin: 0 auto 14px; overflow: hidden; border-radius: 8px;
  background: #000; position: relative; cursor: grab; touch-action: none;
}
.crop-viewport:active { cursor: grabbing; }
.crop-viewport img { position: absolute; top: 0; left: 0; user-select: none; pointer-events: none; }
.crop-zoom-row { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; justify-content: center; }
.crop-zoom-row input[type="range"] { flex: 1; max-width: 200px; }

label { display: block; margin-bottom: 12px; color: var(--text-dim); font-size: 13px; }
label.inline { display: flex; align-items: center; gap: 8px; }
/* slider dentro de um label.inline (texto + range lado a lado): sem isso o range fica na
   largura padrão do navegador (~150-170px), que somada ao texto do rótulo estoura painéis
   estreitos como o do mestre (~220px) — flex:1 deixa ele ocupar só o espaço que sobra
   depois do texto, min-width:0 é o que permite encolher abaixo da largura padrão (comportamento
   default de flex item é nunca encolher além do próprio conteúdo/tamanho intrínseco) */
label.inline input[type="range"] { flex: 1; min-width: 0; }

input[type="text"], input[type="number"], input[type="password"], input[type="email"], select {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 10px 12px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text);
  font-family: var(--font);
  font-size: 14px;
}
input:focus-visible, select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.btn {
  display: inline-block;
  width: 100%;
  padding: 11px 16px;
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 8px;
  font-family: var(--font);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  text-align: center;
  transition: border-color 0.15s, background 0.15s;
}
.btn:hover { border-color: var(--accent); }
.btn.primary { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); font-weight: 600; }
.btn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn.danger { color: var(--danger-dim); border-color: #4a331f; }
.btn.danger:hover { border-color: var(--danger); }
.btn.small-btn { padding: 7px 10px; font-size: 13px; }

.divider {
  display: flex; align-items: center; gap: 12px;
  margin: 18px 0; color: var(--text-dim);
}
.divider::before, .divider::after { content: ''; flex: 1; height: 1px; background: var(--line); }

.error { color: #f2a3a3; font-size: 13px; }
.success { color: var(--accent); font-size: 13px; }

/* ---------- Sala ---------- */
body.room { overflow: hidden; height: 100vh; display: flex; flex-direction: column; }

.topbar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: 18px;
  padding: 8px 14px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  /* acima dos painéis laterais (mestre 20/21, jogadores 25/26): os menus de cena/andar
     moram DENTRO da topbar e ficam presos no z-index dela — com 10, abriam POR TRÁS do
     painel do mestre expandido. A topbar tem só 40px e os painéis começam em 56px, então
     subir não cobre nada */
  z-index: 27;
}
.room-info { display: flex; align-items: center; gap: 10px; }
/* navbar em três zonas: mesa à esquerda, cena e andar no centro, estado à direita */
.topbar-left { display: flex; align-items: center; gap: 18px; min-width: 0; }
.topbar-center { display: flex; align-items: center; gap: 8px; justify-content: center; }
.topbar-right { display: flex; align-items: center; justify-content: flex-end; gap: 10px; min-width: 0; }
.topbar-tool { display: inline-flex; align-items: center; gap: 7px; max-width: 220px; color: var(--accent); border-color: var(--accent); }
.topbar-tool[hidden] { display: none; }
.topbar-tool-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); flex-shrink: 0; }
#topbar-tool-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.topbar-ping { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.topbar-ping svg { width: 15px; height: 15px; display: block; }
#room-name { font-size: 14px; font-weight: 600; }
/* mestre: clicar no título renomeia (ver app.js) — cursor de "clicável" só ligado pra
   ele (classe adicionada em setupGmTools, jogador nunca ganha essa affordance) */
#room-name.room-name-editable { cursor: pointer; }
#room-name.room-name-editable:hover { color: var(--accent); }
/* mesma armadilha do [hidden] de sempre nesse projeto: `input[type="text"]` (regra
   genérica de formulário, mais abaixo no arquivo) tem `display:block` com especificidade
   MAIOR que o `[hidden]` padrão do navegador (dois seletores — elemento+atributo — contra
   um só) — sem esse override explícito, o campo de renomear ficava sempre visível
   (vazio, "gigante") mesmo com o atributo `hidden` presente no HTML */
#room-name-input[hidden] { display: none; }

.chip {
  font-size: 12px;
  padding: 3px 10px;
  border-radius: 99px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--text-dim);
  cursor: pointer;
}
.chip:hover { border-color: var(--accent); color: var(--text); }

/* seletor de cena (só mestre): mesmo visual do de andar, sem display de autor pra o
   [hidden] padrão do navegador funcionar */
.scene-switcher { position: relative; margin-right: 8px; }
#scene-switcher-btn { display: inline-flex; align-items: center; gap: 7px; max-width: 260px; }
.scene-switcher-name {
  color: var(--text); font-weight: 600; max-width: 170px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.floor-switcher { position: relative; display: flex; align-items: center; gap: 4px; }
/* botão do andar: ícone + cena (cinza, pequena) + andar (destaque) + contador "2/4" */
#floor-switcher-btn { display: inline-flex; align-items: center; gap: 7px; max-width: 320px; }
.floor-switcher-ico { width: 14px; height: 14px; flex-shrink: 0; color: var(--text-dim); }
.floor-switcher-scene {
  color: var(--text-dim); font-size: 11px; max-width: 110px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.floor-switcher-scene::after { content: '›'; margin-left: 7px; opacity: .6; }
.floor-switcher-name {
  color: var(--text); font-weight: 600; max-width: 150px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.floor-switcher-count { color: var(--text-dim); font-size: 11px; font-variant-numeric: tabular-nums; }
/* olhinho "andar oculto" ao lado do botão do andar (só mestre, só com o andar oculto) */
.floor-hidden-eye { flex-shrink: 0; }
/* setas ↑/↓ do seletor (mesmo efeito de ArrowUp/ArrowDown) */
.floor-step-btn {
  width: 22px; height: 22px; padding: 0; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; border: 1px solid var(--line); background: var(--panel-2);
  color: var(--text-dim); cursor: pointer;
}
.floor-step-btn svg { width: 12px; height: 12px; }
.floor-step-btn:hover { border-color: var(--accent); color: var(--text); }
.floor-step-btn.is-disabled { opacity: .35; pointer-events: none; }
/* bolinhas de quem está em cada andar, no menu */
.floor-switcher-dots { display: inline-flex; align-items: center; gap: 3px; }
.floor-switcher-dots i { width: 9px; height: 9px; border-radius: 50%; box-shadow: 0 0 0 1px rgba(0,0,0,.45); }
.floor-switcher-dots b { font-size: 10px; font-weight: 600; color: var(--text-dim); }
.floor-switcher-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 6px 8px; border-radius: 6px; cursor: pointer; font-size: 13px; white-space: nowrap;
}
.floor-switcher-row:hover { background: var(--panel-2); }
/* olhinho menor dentro das linhas do menu, sem engordar a linha */
.floor-switcher-row .eye-toggle { width: 22px; height: 22px; padding: 3px; margin: -3px 0; }
.floor-switcher-row.current { color: var(--accent); font-weight: 600; }

/* ---------- Jogadores conectados (estilo TTS: coluna no canto superior direito) ---------- */
/* seta que acompanha o painel (mesma técnica do `.gm-tools-toggle`, espelhada pro lado
   direito): parada em `right:238px` (colada na borda ESQUERDA do painel aberto, ao lado
   do primeiro card — mesmo `top` do painel, nunca em cima dele) quando visível, e
   deslizando pra `right:12px` (colada na borda direita da tela, feito uma aba) quando o
   painel é escondido. `top:56px` nos dois (mesmo valor de `.gm-tools`/`.gm-tools-toggle`,
   já que a seta não ocupa mais espaço próprio acima do painel) — antes era `top:96px`,
   sobrando um vão vazio em cima só por causa da seta que ficava ali antes de virar
   `right:238px` ao lado do primeiro card. */
.players-panel-toggle {
  position: fixed;
  top: 56px;
  right: 12px;
  z-index: 26;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--text-dim);
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  display: none; /* só aparece com o painel escondido (.collapsed) */
  align-items: center;
  justify-content: center;
  padding: 0;
}
.players-panel-toggle.collapsed { display: flex; }
.players-panel-toggle:hover { border-color: var(--accent); color: var(--text); }
/* container FIXO externo: lista rolável em cima + rodapé estático embaixo (convidar
   amigos + código da mesa), sempre visível mesmo com a lista rolada — por isso o
   max-height/overflow moraram na LISTA (.players-panel-list), não aqui */
.players-panel {
  position: fixed;
  top: 56px;
  right: 12px;
  z-index: 25;
  display: flex;
  flex-direction: column;
  width: 220px;
  max-height: calc(100vh - 76px);
  transition: transform 0.25s ease;
}
/* empurra o painel inteiro (lista + rodapé) pra fora da tela pela direita (translateX
   em % é relativo à própria largura do painel, então não precisa bater um valor fixo em px) */
.players-panel.collapsed { transform: translateX(calc(100% + 24px)); }
.players-panel-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  overflow-y: auto;
  min-height: 0; /* permite encolher dentro do flex column pai — senão nunca respeitaria o max-height do pai, empurrando o rodapé pra fora */
}
/* rodapé fixo abaixo do último jogador — "+" convida amigos (canto direito), código
   da mesa logo abaixo (pedido explícito) */
.players-panel-footer {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  margin-top: 8px;
  flex-shrink: 0;
}
.players-panel-invite-btn {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--text-dim);
  font-size: 18px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
}
.players-panel-invite-btn:hover { border-color: var(--accent); color: var(--accent); }
.player-card {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border-radius: 8px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.2;
}
.player-card.player-card-mine,
.player-card.player-card-viewable { cursor: pointer; }
.player-card.player-card-mine:hover,
.player-card.player-card-viewable:hover { box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4), 0 0 0 2px rgba(255, 255, 255, 0.35) inset; }
.player-card-status {
  display: flex; align-items: center; gap: 3px; flex-shrink: 0;
  font-size: 11px; font-weight: 500; opacity: 0.85;
}
.player-card-status svg { width: 14px; height: 14px; display: block; }
.player-card-ident { flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; }
.player-card-right { flex-shrink: 0; display: flex; align-items: center; gap: 6px; }
/* ausente: card esmaecido (a cor de mão continua reconhecível, só mais apagada) */
.player-card.player-card-away { opacity: 0.55; }
.player-card-away-icon { display: flex; opacity: 0.9; }
.player-card-away-icon svg { width: 14px; height: 14px; display: block; }
.player-card-crown { flex-shrink: 0; display: flex; }
.player-card-crown svg { width: 15px; height: 15px; display: block; }
.player-card-name {
  flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* placeholder padrão (inicial do nome) por baixo da foto, revelado sozinho se a <img>
   falhar (onerror já remove ela, ver buildPlayerCard) — sem isso, jogador sem foto ficava
   com um espaço vazio na lista, em vez de um avatar genérico como qualquer outro */
.player-card-avatar-wrap {
  position: relative; width: 28px; height: 28px; border-radius: 50%; flex-shrink: 0;
  overflow: hidden; background: rgba(0, 0, 0, 0.2);
}
.player-card-avatar {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
.player-card-avatar-placeholder {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700; opacity: 0.85;
}
.player-card-blindfold { flex-shrink: 0; display: flex; opacity: 0.85; }
.player-card-blindfold svg { width: 15px; height: 15px; display: block; }
/* estilo TTS: só aparece enquanto o jogador ainda não terminou de baixar mapa/tokens —
   escondido (não criado) assim que chega em 100%, ver setCardLoading() em app.js */
.player-card-loading {
  flex-shrink: 0; font-size: 10.5px; font-weight: 700; opacity: 0.9;
  background: rgba(0, 0, 0, 0.28); padding: 1px 6px; border-radius: 999px;
}

/* ---------- Cortina (estilo TTS: "B" fecha a visão do próprio jogador) ---------- */
/* Não usa [hidden]/display (mesma armadilha do display:flex de autor vencendo o
   [hidden] do navegador, já documentada em outros overlays) — e também porque display
   não dá pra fazer transição suave. Fica sempre "display: flex" (não atrapalha o layout,
   position:fixed já tira do fluxo normal) e alterna opacity+visibility em vez disso: a
   visibility com delay só no fade-out é o que mantém a cortina bloqueando clique/scroll
   embaixo enquanto ainda visível, mas libera assim que o fade termina de sumir */
.blindfold-overlay {
  display: flex;
  position: fixed;
  inset: 0;
  z-index: 40;
  background: #000;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: var(--text-dim);
  font-size: 15px;
  line-height: 1.6;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s ease, visibility 0s linear 0.4s;
}
.blindfold-overlay.visible {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.4s ease;
}

/* ---------- Aviso de carregamento (mapa/tokens baixando) ---------- */
.loading-banner {
  position: fixed;
  top: 66px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--text-dim);
  font-size: 13px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
}
/* `display: flex` acima é regra de autor e vence a regra padrão do navegador pra
   [hidden] (que é só do user-agent) — sem isso o banner nunca some de verdade */
.loading-banner[hidden] { display: none; }
.loading-spinner {
  width: 13px;
  height: 13px;
  border: 2px solid var(--line);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: loading-spin 0.7s linear infinite;
  flex-shrink: 0;
}
@keyframes loading-spin {
  to { transform: rotate(360deg); }
}

/* ---------- Zoom de token (segurar Alt sobre um token) ---------- */
/* usa uma classe pra mostrar (não o atributo hidden) — já tivemos problema em
   outro overlay onde `display` fixo no seletor base vencia o [hidden] do navegador */
.token-zoom-preview {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: none;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.55);
  pointer-events: none;
}
.token-zoom-preview.visible { display: flex; }
.token-zoom-preview img {
  max-width: 70vw;
  max-height: 80vh;
  object-fit: contain;
  transition: transform 0.08s linear; /* scroll ajusta o zoom (ver tokenZoomWheel em app.js) */
  filter: drop-shadow(0 8px 32px rgba(0, 0, 0, 0.6));
}

#canvas-wrap { flex: 1; position: relative; background: #000; }
#canvas-wrap canvas { display: block; }

/* ---------- Preview do token armado pra spawnar (biblioteca), grudado no cursor ----------
   usa o atributo [hidden] de verdade (sem nenhuma regra de `display` aqui que possa
   vencer o padrão do navegador — mesma armadilha já documentada várias vezes nesse
   arquivo) — só `left`/`top` são ajustados via JS a cada movimento do mouse */
.library-spawn-preview {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 45;
  width: 42px;
  height: 42px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background-color: var(--bg);
  background-size: cover;
  background-position: center;
  opacity: 0.6;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
  pointer-events: none; /* nunca intercepta o clique que posiciona o token no mapa */
}
.library-spawn-preview-badge {
  position: absolute;
  bottom: -6px;
  right: -6px;
  min-width: 16px;
  height: 16px;
  padding: 0 3px;
  border-radius: 8px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 10px;
  line-height: 16px;
  text-align: center;
  font-weight: 600;
}

/* ---------- Setas de ping fora da tela (Tab) ---------- */
.ping-arrows { position: fixed; inset: 0; z-index: 35; pointer-events: none; }
.ping-arrow {
  position: fixed;
  width: 22px;
  height: 30px;
  filter: drop-shadow(0 0 6px rgba(200, 30, 30, 0.85));
  opacity: 0;
}
/* seta com haste (não só um triângulo solto) — mais fácil de ler a direção numa rotação
   qualquer; SVG (não CSS puro) porque precisa de uma forma "seta com corpo", não um
   triângulo simples. Aponta "pra cima" por padrão; app.js gira via transform */
.ping-arrow svg { width: 100%; height: 100%; display: block; }

/* ---------- Ferramentas do mestre ---------- */
/* o painel inteiro é o único container de scroll (não cada aba) — como só uma aba fica
   aberta por vez (ver setupGmTools()), o espaço já ocupado pelos cabeçalhos fechados das
   outras 11 varia conforme QUAL aba tá aberta; uma aba no fim da lista (ex: Andares) sobra
   com bem menos altura livre real do que uma no topo, então um max-height fixo por aba
   sempre erra a conta pra alguma delas e o conteúdo vaza pra fora da tela sem scrollbar */
.gm-tools {
  position: absolute;
  top: 56px;
  left: 12px;
  width: 220px;
  max-height: calc(100vh - 76px);
  overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 14px;
  z-index: 20;
  transition: transform 0.25s ease, width 0.25s ease, padding 0.25s ease;
}
/* empurra o painel inteiro pra fora da tela pela esquerda (translateX em % é relativo à
   própria largura do painel, não precisa bater um valor fixo em px) */
/* barra de rolagem temática (mesma do histórico de dados), em vez da branca padrão */
.gm-tools { scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
.gm-tools::-webkit-scrollbar { width: 7px; }
.gm-tools::-webkit-scrollbar-track { background: transparent; }
.gm-tools::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; }
.gm-tools::-webkit-scrollbar-thumb:hover { background: var(--accent); }
.gm-tools h3 { margin: 0; color: var(--accent); font-size: 15px; }
.gm-tools-head { display: flex; align-items: center; justify-content: space-between; margin: 0 0 4px; }
.gm-tools-head-actions { display: flex; align-items: center; gap: 2px; }
/* desfazer/refazer sem nada na pilha: apagados e sem hover (não usa o atributo
   `disabled` porque botão desabilitado engole os eventos de mouse da dica) */
.gm-tools-compact-btn.is-disabled { opacity: 0.35; cursor: default; }
.gm-tools-compact-btn.is-disabled:hover { color: var(--text-dim); border-color: transparent; }
.gm-tools-compact-btn {
  width: 24px; height: 24px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 1px solid transparent; border-radius: 6px;
  color: var(--text-dim); cursor: pointer;
}
.gm-tools-compact-btn svg { width: 14px; height: 14px; transition: transform 0.2s; }
.gm-tools-compact-btn:hover { color: var(--text); border-color: var(--line); }
.gm-tools-compact-btn.active { color: var(--accent); }
/* título de seção (Cenário / Visão e luz / Mesa / Conteúdo / Estrutura) */
.gm-section-title {
  margin: 12px 0 2px;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1px;
  /* cinza, não vermelho: o vermelho fica reservado pro que está LIGADO/armado — antes o
     título da seção e o item ativo tinham a mesma cor e o olho não separava os dois */
  color: var(--text-dim);
  opacity: 0.55;
}
.gm-tools-head + .gm-section-title { margin-top: 6px; }
/* o título já separa a seção: o primeiro item logo abaixo dele não leva a linha de cima */
.gm-section-title + * { border-top: none !important; }
.tool-group { border-top: 1px solid var(--line); }
.tool-group:first-of-type { border-top: none; }
.tool-group summary {
  cursor: pointer;
  padding: 9px 0;
  display: flex;
  align-items: center;
  gap: 9px;
  font-family: var(--font);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-dim);
  list-style: none;
}
.tg-ico { width: 16px; height: 16px; flex-shrink: 0; }
.tg-label { flex: 1; min-width: 0; }
/* bolinha de estado no canto do ícone da aba (data-dot = accent/ok/warn) — o ícone fica
   à esquerda do <summary> no painel largo e centralizado no compacto */
.tool-group summary { position: relative; }
.tool-group summary[data-dot]::before {
  content: '';
  position: absolute;
  top: 6px;
  left: 11px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  border: 1.5px solid var(--panel);
  box-sizing: content-box;
  background: var(--accent);
  pointer-events: none;
}
.tool-group summary[data-dot="ok"]::before { background: #4ade80; }
.tool-group summary[data-dot="warn"]::before { background: #f59e0b; }
.gm-tools-narrow .tool-group summary[data-dot]::before { top: 7px; left: calc(50% + 3px); }
/* tecla do atalho ao lado do nome (F2, Num 7...) — discreta, só pra descobrir */
.tg-key {
  font-family: var(--font);
  font-size: 9px;
  letter-spacing: 0;
  text-transform: none;
  padding: 1px 5px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--panel-2);
  color: var(--text-dim);
}
.tool-group summary::-webkit-details-marker { display: none; }
/* chevron desenhado com borda (o '▸' de texto era minúsculo): aponta pra direita
   fechado e gira pra baixo quando a aba está aberta */
.tool-group summary::after {
  content: '';
  width: 6px;
  height: 6px;
  margin-right: 3px;
  border-right: 1.6px solid currentColor;
  border-bottom: 1.6px solid currentColor;
  transform: rotate(-45deg);
  opacity: 0;
  transition: transform 0.15s, opacity 0.15s;
}
.tool-group summary:hover::after,
.tool-group[open] > summary::after { opacity: 0.75; }
.tool-group[open] summary::after { transform: rotate(45deg); }
.tool-group summary:hover { color: var(--accent); }
/* aba aberta: texto claro e ícone no acento (o vermelho puro continua pra ferramenta armada) */
.tool-group[open] > summary { color: var(--text); }
.tool-group[open] > summary .tg-ico { color: var(--accent); }
.tool-group-body { padding: 0 0 10px; }
.tool-group-body .btn { margin-bottom: 8px; }
.file-btn { margin-bottom: 8px; }
/* opção única na barra (Sons, Biblioteca): mesmo visual do <summary> de um .tool-group
   (borda superior, texto uppercase pequeno, hover accent), mas sem accordion nem seta —
   clicar já dispara a ação direto, sem precisar expandir algo que só teria UM botão lá
   dentro (pedido explícito: "sons só tem uma opção, então nao precisa abrir uma opção
   com apenas um botão, clicar ali em sons ja pode abrir direito o painel de sons") */
.tool-group-flat {
  position: relative;
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  border: none;
  border-top: 1px solid var(--line);
  background: none;
  padding: 9px 0;
  margin: 0;
  text-align: left;
  cursor: pointer;
  font-family: var(--font);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-dim);
}
/* seta diagonal no lugar do chevron: avisa que esse item abre um popup, não uma lista */
.tool-group-flat::after { content: '↗'; font-size: 11px; opacity: 0; transition: opacity 0.15s; }
.tool-group-flat:hover::after { opacity: 0.6; }
.tool-group-flat:hover { color: var(--accent); }
/* mão livre não abre popup — sem a seta ↗ */
.tool-group-flat.hand-tool::after { content: none; }

/* ferramenta ARMADA (desenhar parede, ocultar névoa, colocar luz...): o item do painel
   fica aceso — vale também no modo compacto, onde só o ícone aparece */
.tool-group summary.tool-armed,
.tool-group-flat.tool-armed { color: var(--accent); }
.tool-armed .tg-ico {
  padding: 3px; box-sizing: content-box; margin: -3px;
  background: rgba(200, 30, 30, 0.22); border-radius: 6px;
  box-shadow: 0 0 0 1px rgba(200, 30, 30, 0.55);
}

/* faixa com a dica da ferramenta ativa (fixa embaixo, no centro) */
.tool-hint {
  position: fixed;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  z-index: 25;
  max-width: min(760px, 92vw);
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  background: var(--panel);
  border: 1px solid var(--accent);
  border-radius: 10px;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.5);
  font-size: 12px;
  color: var(--text);
  pointer-events: none;
}
.tool-hint[hidden] { display: none; }
.tool-hint strong { color: var(--accent); white-space: nowrap; }
/* modo edição (painel aberto, sem ferramenta armada): mais discreta */
.tool-hint.editing { border-color: var(--line); color: var(--text-dim); }
.tool-hint.editing strong { color: var(--text-dim); }

/* dica instantânea dos ícones do painel do mestre */
.gm-tooltip {
  position: fixed;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 9px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 6px;
  font-size: 12px;
  color: var(--text);
  white-space: nowrap;
  pointer-events: none;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
}
.gm-tooltip[hidden] { display: none; }

/* botões de efeito de cena (seção Efeitos): nome à esquerda, tecla à direita */
.fx-btn { display: flex; align-items: center; justify-content: space-between; text-align: left; }
.fx-btn .tg-key { background: transparent; }
.tool-group-body .fx-btn.active .tg-key { border-color: rgba(255, 255, 255, 0.5); color: #fff; }

/* modo compacto: só os ícones, painel de 56px. Abrir qualquer aba alarga o painel
   de volta (JS tira a classe enquanto houver aba aberta) */
.gm-tools.gm-tools-narrow { width: 56px; padding: 10px 8px; }
.gm-tools-narrow .gm-tools-head { justify-content: flex-start; padding-left: 8px; }
.gm-tools-narrow .gm-tools-head-actions { flex-direction: column; gap: 2px; }
.gm-tools-narrow .gm-tools-compact-btn { width: 22px; height: 20px; }
.gm-tools-narrow .gm-tools-head { margin-bottom: 6px; }
.gm-tools-narrow .gm-tools-head h3,
.gm-tools-narrow .tg-label,
.gm-tools-narrow .tg-key,
.gm-tools-narrow .tool-group-flat::after,
.gm-tools-narrow .tool-group summary::after { display: none; }
.gm-tools-narrow .tool-group summary,
.gm-tools-narrow .tool-group-flat { justify-content: flex-start; padding: 5px 0 5px 10px; }
.gm-tools-narrow .tg-ico { width: 18px; height: 18px; }
/* compacto sem barra de rolagem visível (sobrar um pouco de altura em janela baixa ainda rola com a
   roda do mouse, mas a barra no meio dos ícones só atrapalha) */
.gm-tools.gm-tools-narrow { scrollbar-width: none; }
.gm-tools.gm-tools-narrow::-webkit-scrollbar { display: none; }
/* sem os rótulos, o título da seção vira só uma linha divisória */
.gm-tools-narrow .gm-section-title {
  font-size: 0; height: 1px; margin: 3px 4px; padding: 0;
  background: var(--line); opacity: 1;
}
/* flyout: no modo compacto o conteúdo da aba aberta sai do painel e vira um painel solto
   ao lado dele. position:fixed escapa do overflow do #gm-tools; a posição vem do JS
   (placeFlyout em app.js) pelas variáveis --fly-left/top/max */
.gm-tools-narrow .tool-group[open] > .tool-group-body {
  position: fixed;
  left: var(--fly-left, 76px);
  top: var(--fly-top, 100px);
  max-height: var(--fly-max, 70vh);
  width: 240px;
  box-sizing: border-box;
  overflow-y: auto;
  padding: 12px 14px 6px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.5);
  z-index: 3;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}
.gm-tools-narrow .tool-group[open] > .tool-group-body::before {
  content: attr(data-title);
  display: block;
  margin-bottom: 10px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--accent);
}
/* alfinete (fixar) no canto do flyout — só existe no modo compacto */
.fly-pin { display: none; }
.gm-tools-narrow .tool-group[open] > .tool-group-body > .fly-pin {
  display: flex;
  position: absolute;
  top: 8px;
  right: 8px;
  width: 24px;
  height: 24px;
  margin: 0;
  padding: 0;
  align-items: center;
  justify-content: center;
  background: none;
  border: 1px solid transparent;
  border-radius: 6px;
  color: var(--text-dim);
  cursor: pointer;
}
.fly-pin svg { width: 14px; height: 14px; transform: rotate(35deg); }
.fly-pin:hover { color: var(--accent); }
.fly-pin.active { color: var(--accent); background: rgba(200, 30, 30, 0.18); border-color: rgba(200, 30, 30, 0.5); }
.fly-pin.active svg { transform: rotate(0); }
/* o ícone da aba que está aberta no flyout fica aceso */
.gm-tools-narrow .tool-group[open] > summary { color: var(--accent); }
.gm-tools.gm-tools-narrow .tool-group,
.gm-tools.gm-tools-narrow .tool-group-flat,
.gm-tools.gm-tools-narrow > .tool-group-focusable { border-top: none; }
.hint { font-size: 12px; color: var(--text-dim); margin: 4px 0 0; }

/* modo "foco" das abas focáveis — Camadas/Cenas/Andares, as três filhas diretas de
   #gm-tools fora do wrapper (nasceu só em Andares — "a aba andares quando clicada vai
   expandir empurrando todas as outras opções pra fora" — generalizado a pedido:
   "adicione esse mesma sistema de foco em camadas, e tambem em cenas"). Com qualquer
   uma aberta, TODO o resto do painel (título, outras abas, botões únicos, tudo dentro
   de #gm-tools-collapsible, MAIS as outras duas focáveis) encolhe suavemente, sobrando
   só a aba aberta ocupando o painel inteiro. Técnica grid-template-rows 0fr->1fr
   (mesma da tela inicial/cartões expansíveis) — animável, ao contrário de display:none
   (corte abrupto que causava o "flash" reportado). MESMA regra de sempre: o item do
   grid que encolhe (.gm-tools-collapsible) não pode ter padding próprio — padding nunca
   é comprimido por overflow:hidden/min-height:0 (documentado à exaustão nesse arquivo)
   — por isso o padding de #gm-tools continua no elemento de fora (.gm-tools) */
.gm-tools-collapsible {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows 0.3s ease;
}
.gm-tools.gm-tools-focus .gm-tools-collapsible { grid-template-rows: 0fr; }
.gm-tools-collapsible-inner {
  overflow: hidden;
  min-height: 0;
  transition: opacity 0.2s ease;
  /* folga lateral DENTRO da área cortada: o anel do ícone de ferramenta armada
     (.tool-armed .tg-ico, 3px pra fora do ícone) era cortado pelo overflow:hidden na
     borda esquerda; a margem negativa devolve o alinhamento original */
  padding: 0 4px;
  margin: 0 -4px;
}
.gm-tools.gm-tools-focus .gm-tools-collapsible-inner {
  opacity: 0;
  pointer-events: none;
}
/* as focáveis são `.tool-group` filhas DIRETAS de #gm-tools (as outras moram dentro de
   .gm-tools-collapsible-inner) — `.tool-group:first-of-type` bateria na primeira delas
   à toa e tiraria a linha separadora de cima; a regra por classe devolve a borda no
   estado normal. FECHADA, uma focável é só a linha do cabeçalho (~40px) — o max-height
   fixo dá um valor concreto de partida pra animar até 0 quando OUTRA focável entra em
   foco (encolhe suave junto com o wrapper, mesma duração); a ABERTA (a focada) fica
   livre (max-height none) — o conteúdo dela aparece na hora, comportamento aceito */
.gm-tools > .tool-group-focusable {
  border-top: 1px solid var(--line);
  max-height: 44px;
  overflow: hidden;
  transition: max-height 0.3s ease, opacity 0.2s ease;
}
.gm-tools > .tool-group-focusable[open] {
  max-height: none;
  overflow: visible;
}
.gm-tools.gm-tools-focus > .tool-group-focusable:not([open]) {
  max-height: 0;
  opacity: 0;
  pointer-events: none;
  border-top: none;
}
.gm-tools.gm-tools-focus > .tool-group-focusable[open] { border-top: none; }
/* trava o scroll do painel inteiro só durante a janela da transição de foco (ver
   comentário em app.js) — sem isso, a soma temporária "wrapper ainda encolhendo +
   lista de andares já revelada de uma vez" estoura o max-height por um instante e uma
   scrollbar pisca na tela antes de sumir de novo quando o wrapper termina de encolher */
.gm-tools.gm-tools-transitioning { overflow: hidden; }

/* mesmo foco pras demais abas (Mapa, Grid, Névoa, Paredes...): a aba aberta sobe pro topo e
   todo o resto some (cabeçalho, outras abas, títulos de seção, favoritos, Sons/Biblioteca e
   as abas de Estrutura). Aqui o conteúdo fica DENTRO do wrapper, então não dá pra encolher o
   wrapper inteiro como no foco de Camadas/Cenas/Andares: esconde cada irmão da aba aberta.
   Fechar a aba (clicar no cabeçalho dela de novo, F1 ou Esc) traz tudo de volta */
.gm-tools.gm-tools-focus-inner .gm-tools-collapsible-inner > :not(.tool-group[open]),
.gm-tools.gm-tools-focus-inner > :not(.gm-tools-collapsible) { display: none !important; }
.gm-tools.gm-tools-focus-inner .tool-group[open] { border-top: none; }

/* ---------- Camadas ---------- */
.layers-list { display: flex; flex-direction: column; gap: 12px; margin-top: 8px; }
.layer-group { display: flex; flex-direction: column; gap: 4px; }
.layer-group-header {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-dim);
  padding: 0 2px;
}
.layer-level-select {
  width: auto;
  margin: 0;
  padding: 3px 6px;
  font-size: 11px;
  flex-shrink: 0;
}
.layer-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 8px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 6px;
  cursor: grab;
  font-size: 12px;
}
.layer-row.dragging { opacity: 0.4; }
.layer-row.drag-over { border-color: var(--accent); }
.layer-row-map { cursor: default; color: var(--text-dim); }
.layer-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.layer-remove {
  width: 20px; height: 20px;
  flex-shrink: 0;
  padding: 0;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 5px;
  color: var(--danger-dim);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}
.layer-remove:hover { border-color: var(--danger); color: var(--danger); }
.layer-remove.confirm { color: var(--danger); border-color: var(--danger); }
.layer-remove:disabled { opacity: 0.35; cursor: default; }

/* ---------- Andares ---------- */
.floor-name-input {
  flex: 1;
  min-width: 0;
  margin: 0;
  padding: 3px 6px;
  font-size: 12px;
  background: transparent;
  border: 1px solid transparent;
}
.floor-name-input:hover, .floor-name-input:focus-visible { border-color: var(--line); background: var(--bg); }
.layer-row.current-scene { border-color: var(--accent); }

.floor-row-group { display: flex; flex-direction: column; gap: 4px; }
.floor-stack-toggle {
  width: 100%;
  margin: 0;
  padding: 4px 8px;
  font-size: 10.5px;
  text-align: left;
  background: transparent;
  border: 1px dashed var(--line);
  border-radius: 5px;
  color: var(--text-dim);
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.floor-stack-toggle:hover { border-color: var(--accent); color: var(--accent); }
.floor-stack-toggle.active { border-style: solid; color: var(--accent); border-color: var(--accent); }
.floor-stack-panel {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 6px;
}
.floor-stack-select { width: 100%; margin: 0; padding: 4px 6px; font-size: 11px; }
.floor-stack-offsets { display: flex; gap: 8px; }
.floor-stack-offsets label {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 10px;
  color: var(--text-dim);
}
.floor-stack-offsets input { flex: 1; min-width: 0; margin: 0; padding: 3px 5px; font-size: 11px; }
/* lista de andares empilhados (pedido explícito: "+ que permite adicionar mais
   andares empilhados, ficando como as camadas dos tokens") — um cartão por entrada,
   já que cada uma tem nome/offsetX/offsetY/camada, controles demais pra caber numa
   linha só na largura estreita da barra lateral */
.floor-stack-list { display: flex; flex-direction: column; gap: 6px; }
.floor-stack-entry {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 6px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 5px;
}
.floor-stack-entry-top { display: flex; align-items: center; gap: 6px; }
.floor-stack-entry-name { flex: 1; min-width: 0; font-size: 11px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.floor-stack-entry-top .merge-row-remove { width: 20px; height: 20px; font-size: 13px; }
.floor-stack-layer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  font-size: 10px;
  color: var(--text-dim);
}
.floor-stack-layer select { margin: 0; padding: 3px 5px; font-size: 11px; }

/* ---------- Modal ---------- */
.modal-backdrop {
  position: fixed; inset: 0;
  background: rgba(6, 6, 8, 0.75);
  display: grid; place-items: center;
  z-index: 100;
}
.modal-backdrop[hidden] { display: none; }
/* sempre por CIMA de qualquer outro modal (join/color/etc, todos z-index:100 igual) —
   com z-index empatado, quem vence é a ordem no DOM, e não dá pra garantir que o aviso
   de conexão caída sempre vai vir DEPOIS de todo popup existente (e futuro) no HTML;
   um z-index bem mais alto resolve isso de vez, não importa o que mais esteja aberto */
#disconnect-overlay { z-index: 200; }
/* mestre caiu: fica abaixo do aviso de conexão perdida (200) e acima de qualquer painel */
#gm-away-overlay { z-index: 150; }
#gm-away-overlay[hidden] { display: none; }

/* aviso rápido genérico (substitui os alert() nativos espalhados — bloqueavam a tela
   inteira e destoavam do resto do app, que sempre usa mensagens inline). Fixo embaixo
   no centro, some sozinho depois de alguns segundos (ver showToast em app.js).
   z-index bem alto — precisa aparecer por cima de QUALQUER popup/painel aberto no
   momento, já que os erros que ele mostra vêm de ações feitas com algo aberto */
.toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  max-width: min(420px, 90vw);
  background: var(--panel);
  border: 1px solid var(--danger);
  border-left: 4px solid var(--danger);
  border-radius: 8px;
  padding: 12px 16px;
  color: var(--text);
  font-size: 13px;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5);
  z-index: 300;
}
.toast[hidden] { display: none; }
.modal {
  width: min(360px, 92vw);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 24px;
}
.modal h2 { margin: 0 0 16px; font-size: 18px; }

/* ---------- Amigos (public/js/friends.js, compartilhado entre index.html/room.html) ---------- */
.friends-modal {
  width: min(460px, 94vw);
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  position: relative; /* âncora do X no canto, mesmo padrão de .profile-modal */
}
/* padding-right reserva espaço pro X do canto (top/right:14px, ~26px de largura) não
   colidir com os ícones do cabeçalho, que ficam mais próximos da borda direita */
.friends-modal-header { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 16px; padding-right: 34px; }
.friends-modal-header h2 { margin: 0; }
.friends-header-icons { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
/* botões só-ícone do cabeçalho (estilo Steam: "+" pra adicionar amigo, lupa pra buscar
   nos já adicionados) — pequenos, sem texto, .active acende quando o painel abaixo dele
   está aberto */
.icon-btn {
  width: 30px;
  height: 30px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text-dim);
  cursor: pointer;
  flex-shrink: 0;
}
.icon-btn svg { width: 16px; height: 16px; }
.icon-btn:hover { color: var(--text); border-color: var(--accent); }
.icon-btn.active { color: var(--accent); border-color: var(--accent); background: var(--panel); }
.friends-section { margin-bottom: 18px; }
.friends-section:last-of-type { margin-bottom: 0; }
.friends-section h3 { margin: 0 0 8px; font-size: 13px; font-weight: 600; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.03em; }
.friends-list { display: flex; flex-direction: column; gap: 8px; }
/* SÓ a lista "Seus amigos" (não as outras que compartilham .friends-list — busca,
   convites, pedidos recebidos/enviados, normalmente pequenas) ganha um teto próprio +
   scroll interno, mesmo padrão de .room-list ("Carregar mesa") — com muitos amigos, o
   cabeçalho (título, ícones de +/lupa) e as outras seções ficam de FORA da área que
   rola, sempre visíveis, em vez de rolar junto com a lista (pedido explícito depois de
   eu confirmar que o popup inteiro tinha scroll, mas rolava tudo junto: "deixe o
   cabeçalho fixo e só a lista 'Seus amigos' role por dentro"). `.friends-modal` mantém
   o próprio `max-height`/`overflow-y` de sempre como rede de segurança (não removido) —
   pro caso raro de convites/pedidos pendentes TAMBÉM ficarem grandes ao mesmo tempo.
   `min-width: 0` não seria necessário aqui (não é filho de um flex row), mas mantém a
   mesma "receita" das outras listas com scroll do app por consistência. */
#friends-list { max-height: 40vh; overflow-y: auto; padding-right: 4px; }
/* respiro entre a lista de amigos e o botão de copiar código logo abaixo dela (senão
   ficam colados, sem separação nenhuma — pedido explícito: "de um espaço") */
#friends-copy-code { margin-top: 10px; }
/* mesma armadilha do [hidden] de sempre nesse projeto: `.btn` (regra de autor) define
   `display: inline-block`, que vence o `display:none` padrão do navegador pro atributo
   `hidden` — sem isso, o botão continua VISÍVEL na tela inicial mesmo com
   `copyCodeBtn.hidden = true` setado certinho em friends.js (bug real, reproduzido ao
   vivo: JS reportava hidden:true, mas o computed display ainda era inline-block) */
#friends-copy-code[hidden] { display: none; }
.friend-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 8px;
}
.friend-row-name { flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.friend-row-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.friend-row-actions .btn.small-btn { width: auto; flex-shrink: 0; } /* mesmo bug/fix de .merge-row .btn.small-btn — width:100% da regra base briga com o flex row */
/* placeholder padrão (inicial do nome) por baixo da foto, revelado sozinho se a <img>
   falhar (onerror já remove ela, ver avatarEl) — sem isso, amigo sem foto ficava com um
   espaço vazio na lista (print do usuário: só o pontinho de online, sem avatar nenhum) */
.friend-row-avatar-wrap {
  position: relative; width: 24px; height: 24px; border-radius: 50%; flex-shrink: 0;
  overflow: hidden; background: var(--panel-2); border: 1px solid var(--line);
}
.friend-row-avatar {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
.friend-row-avatar-placeholder {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  color: var(--text-dim); font-size: 11px; font-weight: 700;
}
.friend-online-dot {
  width: 9px; height: 9px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--text-dim);
}
.friend-online-dot.online { background: #4ade80; box-shadow: 0 0 0 2px rgba(74, 222, 128, 0.2); }
/* botão "amigos" (navbar/topbar) precisa de position:relative pro badge de contagem
   (pedidos recebidos + convites pendentes) se posicionar em cima do canto */
#btn-open-friends { position: relative; }
.friend-badge {
  position: absolute;
  top: -6px;
  right: -6px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--accent);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  border-radius: 999px;
  line-height: 1;
}

/* ---------- Armazenamento (index.html) ---------- */
.modal.storage-modal {
  /* pedido explícito, com print mostrando "Backups" sozinho numa 2ª linha dos totais —
     "aumente o espaço horizontal... dando mais espaço pro resto também". 720px garante
     as 5 colunas de totais (minmax(120px,1fr) + gap:8px, 4 gaps) numa linha só mesmo no
     PIOR caso (632px mínimo pro grid + 48px de padding do .modal = 680px) com folga de
     sobra, e dá mais respiro pras linhas de mesa/lixeira também (nome mais longo antes
     de truncar, botão "testar" do áudio com mais espaço ao lado do nome)
  */
  width: min(720px, 95vw);
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  position: relative; /* âncora do X no canto, mesmo padrão de .profile-modal/.friends-modal */
}
.storage-modal h3 { font-size: 14px; margin: 18px 0 10px; color: var(--text); }
.storage-totals {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 8px;
}
.storage-total-item {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 10px 12px;
}
.storage-total-item .label { display: block; font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.02em; }
.storage-total-item .value { display: block; font-size: 16px; font-weight: 600; margin-top: 2px; }
.storage-total-item .count { display: block; font-size: 11px; color: var(--text-dim); margin-top: 1px; }
.storage-section-header { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 18px 0 4px; }
.storage-section-header h3 { margin: 0; }
/* mesmo padrão de #friends-list/.room-list — teto próprio + scroll interno, o resto do
   modal (totais, botões) fica sempre visível em vez de rolar junto */
/* overflow-x:hidden explícito — sem isso, definir só overflow-y:auto faz o navegador
   computar overflow-x como auto TAMBÉM (regra do CSS: só um dos dois eixos pode ficar
   "visible" quando o outro não é), o que mostrava uma barra de rolagem horizontal
   fantasma (print do usuário) mesmo o conteúdo nunca precisando rolar de lado */
.storage-list { display: flex; flex-direction: column; gap: 6px; max-height: 26vh; overflow-y: auto; overflow-x: hidden; padding-right: 4px; }
.storage-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 10px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 8px;
}
/* preview de cada item da lixeira (pedido explícito: "só por nome dos arquivo é dificil
   entender o que é cada coisa") — thumb pra imagem/gif (congelado no 1º frame, mesma
   técnica de buildLibraryThumbEl em app.js — evitar VÁRIOS gif animando ao mesmo tempo
   com o painel aberto), ícone genérico pra áudio (com botão "testar" ao lado, já que não
   dá pra "ver" um som) */
.storage-row-name-wrap { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1; }
.storage-row-thumb {
  width: 40px;
  height: 40px;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--line);
  background: var(--bg);
  flex-shrink: 0;
}
.storage-row-icon {
  width: 40px;
  height: 40px;
  border-radius: 6px;
  border: 1px solid var(--line);
  background: var(--bg);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-dim);
}
.storage-row-icon svg { width: 18px; height: 18px; }
.storage-row-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
/* mesma armadilha do .btn{width:100%} dentro de flex já documentada várias vezes nesse
   projeto (ex.: popup "Sons") — sem o width:auto aqui, o botão "testar" reivindica ~100%
   da linha inteira (confirmado: 629px de 651px disponíveis), empurrando a linha inteira
   pra um scroll horizontal que não deveria existir */
.storage-row-play { width: auto; flex-shrink: 0; }
/* seção "Uploads ativos" (clicar no cartãozinho de totais abre) — lista com seleção pra
   mandar itens (mesmo em uso) pra lixeira, com aviso fixo de que param de funcionar */
.storage-total-clickable { cursor: pointer; }
.storage-total-clickable:hover,
.storage-total-clickable.active { border-color: var(--accent); }
/* declarada DEPOIS da regra base .hint no arquivo — mesma especificidade (1 classe),
   quem vence é ordem, e essa precisa vencer pra cor de aviso aparecer */
.storage-uploads-warning { color: var(--danger-dim); margin: 0 0 8px; }
.storage-row-check { flex-shrink: 0; width: 15px; height: 15px; accent-color: var(--accent); cursor: pointer; }
.storage-row-selectable { cursor: pointer; }
.storage-row-selectable.selected { border-color: var(--accent); }
.storage-row-name-col { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.storage-row-used { font-size: 11px; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* mesmo fix de .storage-row-play — .btn{width:100%} dentro do flex do cabeçalho
   espremeria o h3 (classe .btn.danger.small-btn não tem reset próprio) */
.storage-section-header .btn { width: auto; flex-shrink: 0; }
.storage-row-meta { flex-shrink: 0; font-size: 12px; color: var(--text-dim); text-align: right; }
/* backups: cada data é uma linha que abre a lista das mesas dela */
.storage-backups-list { max-height: 34vh; }
.storage-backup { display: flex; flex-direction: column; gap: 6px; }
.storage-backup-rooms { display: flex; flex-direction: column; gap: 6px; padding-left: 14px; }
.storage-backup-rooms[hidden] { display: none; }
.storage-backup-actions { display: flex; gap: 6px; flex-shrink: 0; }
.storage-backup-room .btn { width: auto; flex-shrink: 0; }
#btn-storage-empty-trash { margin-top: 12px; }
#storage-msg, #storage-error { margin-top: 10px; }

/* ---------- Menu de contexto ---------- */
.context-menu {
  position: fixed;
  width: 230px;
  max-height: calc(100vh - 16px);
  overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 12px;
  z-index: 50;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5);
}
.menu-title { font-size: 15px; font-weight: 600; margin-bottom: 8px; color: var(--accent); }
.context-menu label { margin-bottom: 8px; font-size: 12px; }
.context-menu input[type="text"], .context-menu input[type="number"], .context-menu select { padding: 6px 8px; font-size: 13px; }
.context-menu .btn { margin-bottom: 8px; }
.context-menu .btn:last-child { margin-bottom: 0; }
/* mesma armadilha do `[hidden]` de sempre nesse projeto: `.btn` tem `display:inline-block`
   de autor, que vence o `[hidden]` padrão do navegador — sem esse override, os botões
   condicionais desse menu (Criar variação de arte, Ver/Resetar variações) SEMPRE
   apareciam, mesmo com `.hidden = true` setado corretamente pelo JS (bug real, achado
   testando: um token sem states nenhum mostrava "Ver variações de arte"/"Resetar states"
   igual um token com states) */
.context-menu .btn[hidden] { display: none; }

.merge-context-menu { width: auto; padding: 6px; }
.merge-context-menu .btn { width: auto; white-space: nowrap; }
/* linha de status do menu da zona de teleporte ("Destino: Andar 2" / "Sem destino...") */
.tpcm-dest-info { font-size: 11px; color: var(--text-dim); padding: 2px 4px 6px; white-space: nowrap; }
/* ---------- Menu do jogo (três riscos na navbar) ---------- */
.menu-burger {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  width: 30px;
  height: 26px;
  padding: 5px 6px;
  background: none;
  border: none;
  cursor: pointer;
  flex-shrink: 0;
}
.menu-burger span {
  display: block;
  height: 2px;
  border-radius: 1px;
  background: var(--text-dim);
  transition: background 0.15s ease;
}
.menu-burger:hover span { background: var(--accent); }
.game-menu-modal { width: min(320px, 92vw); }
.game-menu-modal .btn { margin-top: 10px; }
.game-menu-modal h2 { text-align: center; }
/* view interna trocada por [hidden] — sem regra de display de autor nas divs, então o
   [hidden] padrão do navegador funciona sozinho (sem a armadilha clássica documentada) */
/* cada linha (select OU slider) empilhada — rótulo em cima, controle embaixo na largura
   TOTAL — mesma estrutura pro select e pros sliders, pra ficarem alinhados entre si em
   vez de cada um ter uma largura diferente por causa do texto do rótulo (bug relatado:
   "as barras não são iguais e muito perto uma da outra") */
.settings-row { margin-bottom: 18px; }
.settings-row select { margin-top: 6px; }
.settings-slider-control { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.settings-slider-control input[type="range"] { flex: 1; min-width: 0; }
.settings-slider-value { flex-shrink: 0; font-size: 12px; color: var(--text-dim); width: 36px; text-align: right; }

/* ---------- Popup de criar states ---------- */
.merge-modal { width: min(420px, 92vw); }
/* popup "Importar token": três opções grandes, a última escolhida fica destacada */
.modal.import-token-box { width: min(400px, 92vw); }
.import-size-option {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  text-align: left;
  margin-top: 8px;
}
.import-size-option span { font-size: 12px; font-weight: 400; color: var(--text-dim); }
.import-size-option.last-used { border-color: var(--accent); }
.import-token-box .small-btn { margin-top: 12px; }
/* sons de porta: 3 abrir + 3 fechar + 1 trancada é alto demais pra caber sem rolar em
   telas baixas — lista rola dentro do popup, igual .library-list, em vez do popup
   inteiro (ou o painel lateral antigo) estourar pra fora da tela */
#door-sounds-modal .merge-list { max-height: 60vh; overflow-y: auto; padding-right: 4px; scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
#door-sounds-modal .merge-list::-webkit-scrollbar { width: 7px; }
#door-sounds-modal .merge-list::-webkit-scrollbar-track { background: transparent; }
#door-sounds-modal .merge-list::-webkit-scrollbar-thumb { background: var(--line); border-radius: 99px; }
#door-sounds-modal .merge-list::-webkit-scrollbar-thumb:hover { background: var(--accent); }
/* painel flutuante e arrastável pelo título (mesma classe/mecânica da Biblioteca), largo
   o bastante pro rótulo inteiro ("Gramofone: Variação 1 (mestre)") caber sem cortar */
#door-sounds-modal .door-sounds-box { width: min(720px, 94vw); }
#door-sounds-title { cursor: move; user-select: none; }
/* rótulo fixo do slot ("Abrir #1"/"Trancada") — o nome de verdade (editável) é o
   .floor-name-input ao lado, que já reaproveita o estilo dos nomes de cena/andar */
.door-sound-label {
  flex-shrink: 0;
  font-size: 11px;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 190px;
  max-width: 190px;
}
.merge-list { margin: 14px 0; display: flex; flex-direction: column; gap: 8px; }
.merge-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 8px;
}
/* bug real: .btn herda width:100% da regra base — dentro do .merge-row dos sons (nome
   editável flex:1 + botão "Escolher"/"Trocar"), isso faz o botão brigar pelo espaço com
   o input e espremer ele quase a zero (aparecia como um quadradinho cinza vazio do lado
   do nome). Só esse contexto mistura um .btn.small-btn com um input flex:1 no mesmo
   .merge-row — em nenhum outro popup (states/merge de token) isso acontece */
.merge-row .btn.small-btn { width: auto; flex-shrink: 0; }
/* linha de som (popup "Sons"): virou DUAS linhas — a de cima (nome/escolher/×, já
   existia) e uma nova embaixo (volume + testar), pedido explícito de volume por som.
   .sound-row é um .merge-row que empilha as duas em vez de ficar tudo numa linha só */
.sound-row { flex-direction: column; align-items: stretch; gap: 6px; }
.sound-row-top, .sound-row-bottom { display: flex; align-items: center; gap: 10px; }
.sound-row-bottom { padding-left: 200px; } /* alinha com o início do nome, não com o rótulo fixo à esquerda dele */
.sound-volume-input { flex: 1; min-width: 0; }
.merge-thumb {
  width: 64px; height: 64px;
  object-fit: cover;
  border-radius: 8px;
  background: var(--bg);
  flex-shrink: 0;
}
.merge-name { flex: 1; font-size: 13px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.merge-state-input {
  width: 48px;
  margin: 0;
  padding: 6px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 6px;
  color: var(--text);
  font-family: var(--font);
  font-size: 13px;
  text-align: center;
}
.merge-row-remove {
  width: 26px; height: 26px;
  flex-shrink: 0;
  padding: 0;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 6px;
  color: var(--danger-dim);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}
.merge-row-remove:hover { border-color: var(--danger); color: var(--danger); }
/* aviso de duplicata por linha no salvar em lote da biblioteca — .merge-row é flex
   row (thumb+nome+remover); flex-wrap + flex-basis:100% no banner força ele pra uma
   "segunda linha" dentro da MESMA .merge-row, sem precisar de wrapper extra nem virar
   coluna (mesma técnica de .sound-row, mas só quando o conflito existe) */
.library-batch-row-conflict { flex-wrap: wrap; border-color: var(--accent); }
.library-batch-conflict {
  flex: 1 1 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 2px;
  padding-top: 8px;
  border-top: 1px dashed var(--line);
  font-size: 12px;
  color: var(--text-dim);
}
.library-batch-conflict[hidden] { display: none; }
.merge-actions { display: flex; gap: 10px; margin-top: 6px; }
.merge-actions .btn { width: auto; flex: 1; }

/* ---------- Biblioteca de tokens salvos ---------- */
/* painel FLUTUANTE, não modal — pedido explícito: "que o mestre possa usar a mesa ainda
   com a biblioteca aberta... deixar a biblioteca no canto da tela e spawnar os tokens
   quando precisar". Sem backdrop (não cobre a tela, não bloqueia clique no mapa por
   baixo) e sem `place-items:center` (posição é controlada só por `left`/`top`, ajustados
   ao arrastar pelo título). ARMADILHA: nunca declarar `inset` nessa regra — mesmo como
   `auto`, é shorthand de top/right/bottom/left e reseta os `top`/`left` acima dele na
   MESMA regra (bug real encontrado: painel sempre nascia grudado no canto 0,0) */
.library-panel {
  position: fixed;
  top: 90px;
  left: 260px;
  z-index: 30;
}
/* mesma armadilha do `[hidden]` de sempre nesse projeto: sem essa regra, o painel nunca
   some de verdade (regra de autor `position:fixed` não é display, mas sem um `display:none`
   explícito o elemento continua ocupando espaço/recebendo eventos mesmo com `hidden`) */
.library-panel[hidden] { display: none; }
#library-title { cursor: move; user-select: none; flex: 1; margin: 0; }
/* cabeçalho de painel: título (alça de arrastar) + X de fechar no canto direito */
.panel-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 14px; }
.panel-close-x { width: 30px; height: 30px; flex-shrink: 0; padding: 0; display: flex; align-items: center; justify-content: center; background: none; border: 1px solid transparent; border-radius: 8px; color: var(--text-dim); cursor: pointer; }
.panel-close-x svg { width: 16px; height: 16px; }
.panel-close-x:hover { color: var(--text); border-color: var(--line); background: var(--panel-2); }
.library-modal { width: min(640px, 92vw); }
/* modo lote (2+ tokens salvos de uma vez) precisa de mais espaço horizontal — a fila
   vira uma SEGUNDA COLUNA ao lado da navegação de pastas, não é só texto a mais */
.library-modal.library-modal-wide { width: min(920px, 96vw); }
/* linha (não coluna) só quando o modo lote está ativo — .library-batch-col cuida da
   própria visibilidade via [hidden], então esse wrapper pode ser sempre flex-row */
.library-body { display: flex; gap: 16px; align-items: flex-start; min-width: 0; }
.library-browse-col { flex: 1; min-width: 0; }
.library-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
/* mesma armadilha do `.btn { width:100% }` dentro de flex já corrigida no popup "Sons"
   (ver `.merge-row .btn.small-btn`): sem resetar, o botão "+ Nova pasta" vira um
   flex-basis gigante e espreme o caminho (breadcrumb) pra quase nada — era o bug do
   print ("o caminho em cima por exemplo é pouco espaço e não consigo ver") */
.library-toolbar .btn { width: auto; flex-shrink: 0; }
/* o caminho fica com TODO o espaço que sobra do botão e pode QUEBRAR de linha quando o
   aninhamento de pastas for fundo demais — nunca mais corta com "..." */
.library-breadcrumb { flex: 1; min-width: 0; font-size: 13px; color: var(--text-dim); overflow-wrap: anywhere; }
.library-breadcrumb span { cursor: pointer; }
.library-breadcrumb span:hover { color: var(--accent); }
.library-breadcrumb .sep { margin: 0 4px; cursor: default; }
.library-breadcrumb .sep:hover { color: var(--text-dim); }
/* busca por nome em TODAS as pastas de uma vez (digitou, a grade abaixo vira os
   resultados; apagou, volta pra navegação normal de pastas) */
.library-search { margin: 0 0 8px; padding: 8px 10px; font-size: 13px; }
/* grade de ícones grandes, igual "ícones grandes" do Explorador de Arquivos do Windows */
.library-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 6px;
  max-height: 55vh;
  overflow-y: auto;
  padding: 4px;
}

/* ---------- Cenas (aba focável na barra do mestre) ---------- */
/* linha em DUAS partes (miniatura+nome em cima, olho+× embaixo) — de volta na barra
   lateral estreita (era um painel flutuante de 420px; revertido a pedido: "voltando a
   parte de cenas pro conjunto do mestre"), tudo numa linha só espremeria o nome de
   volta pros ~70px que motivaram a saída na época */
.scene-row { flex-direction: column; align-items: stretch; gap: 6px; }
.scene-row-main { display: flex; align-items: center; gap: 8px; min-width: 0; }
.scene-row-controls { display: flex; align-items: center; justify-content: flex-end; gap: 6px; }
/* miniatura do mapa da cena — mesma proporção da miniatura das mesas na tela inicial */
.scene-thumb {
  width: 52px;
  height: 36px;
  object-fit: cover;
  border-radius: 4px;
  border: 1px solid var(--line);
  flex-shrink: 0;
  pointer-events: none; /* clique na miniatura conta como clique na linha (trocar de cena) */
}
/* olhinho de "nome visível/oculto" — ícone no lugar do antigo botão de texto */
.eye-toggle {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  padding: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 5px;
  color: var(--text-dim);
  cursor: pointer;
}
.eye-toggle svg { width: 100%; height: 100%; }
.eye-toggle:hover { border-color: var(--accent); color: var(--accent); }
.eye-toggle.active { color: var(--accent); border-color: var(--accent); }
.library-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 8px;
  border-radius: 8px;
  border: 1px solid transparent;
  cursor: pointer;
}
.library-tile:hover { background: var(--panel-2); border-color: var(--line); }
.library-tile.drag-over { border-color: var(--accent); background: var(--panel-2); }
.library-thumb {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 8px;
  background: var(--bg);
}
.library-folder-icon {
  width: 100%;
  aspect-ratio: 1;
  border-radius: 8px;
  background: var(--panel-2);
  display: grid;
  place-items: center;
  color: var(--accent);
}
.library-folder-icon svg { width: 55%; height: 55%; }
.library-tile-name {
  width: 100%;
  margin: 0;
  padding: 2px 4px;
  background: transparent;
  border: 1px solid transparent;
  color: var(--text);
  font-family: var(--font);
  font-size: 12px;
  text-align: center;
}
.library-tile-name:hover, .library-tile-name:focus-visible { border-color: var(--line); background: var(--bg); }
.library-tile-delete {
  position: absolute;
  top: 4px;
  right: 4px;
  opacity: 0;
  transition: opacity 0.15s;
  background: var(--panel);
}
.library-tile:hover .library-tile-delete, .library-tile-delete.confirm { opacity: 1; }
/* selo de contagem num item de GRUPO ("salvar junto", 2+ tokens salvos com posição
   relativa preservada) — canto inferior-direito, o superior-direito já é o "×" de
   excluir (ver .library-tile-delete acima) */
.library-tile-group-badge {
  position: absolute;
  bottom: 22px;
  right: 4px;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: 9px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 11px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
}
.library-empty-hint { grid-column: 1 / -1; font-size: 12px; color: var(--text-dim); padding: 4px; }
.library-save-row { display: flex; gap: 8px; margin-top: 10px; }
/* mesma armadilha do `[hidden]` de sempre nesse projeto: a regra de autor `display:flex`
   acima vence a regra padrão do navegador pra [hidden] — sem esse display:none explícito,
   o campo "Nome do token"/"Salvar aqui" aparecia SEMPRE, até em modo navegar (o JS já
   setava o atributo certo, só o CSS não obedecia) */
.library-save-row[hidden] { display: none; }
.library-save-row input { flex: 1; margin: 0; }
.library-save-row .btn { width: auto; flex-shrink: 0; }
.library-save-conflict { margin-top: 10px; }
.library-save-conflict p { margin: 0 0 10px; }
/* salvar VÁRIOS tokens de uma vez — coluna PRÓPRIA ao lado da navegação de pastas
   (pedido explícito: "se a pasta ja tiver tokens ela sai da tela, então faça a fila de
   tokens aparecer ao lado em vez de embaixo, expandindo o painel pro lado") — nunca
   estoura o painel pra fora da tela não importa quantos itens a pasta atual já tenha,
   já que tem seu próprio scroll independente do `.library-list`. Mesma armadilha do
   [hidden] de sempre (regra de autor `display:flex` venceria o `[hidden]` padrão) */
.library-batch-col {
  display: flex;
  flex-direction: column;
  width: 260px;
  flex-shrink: 0;
  padding-left: 16px;
  border-left: 1px solid var(--line);
  /* mesmo teto de altura do .library-list ao lado, pra ficarem visualmente equilibrados */
  max-height: 55vh;
}
.library-batch-col[hidden] { display: none; }
.library-batch-col .merge-list { flex: 1; min-height: 0; overflow-y: auto; padding-right: 4px; margin: 0 0 10px; }
.library-batch-col .btn { width: 100%; flex-shrink: 0; }
/* thumb menor nessa coluna estreita — o padrão de 64px (herdado de .merge-thumb, usado
   nos popups mais largos de criar states) espremeria demais o nome ao lado */
.library-batch-col .merge-thumb { width: 40px; height: 40px; }

/* ---------- Mesa de documentos (livros, páginas de diário, cartas...) ---------- */
/* Tela cheia própria, separada do mapa principal — mesmo padrão de outros overlays
   deste projeto: nunca usa [hidden] (regra de autor "display" venceria a regra padrão
   do navegador), só alterna a classe .visible */
.documents-board-modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 45;
  background: var(--bg);
  flex-direction: column;
}
.documents-board-modal.visible { display: flex; }
.documents-board-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}
.documents-board-header h2 { margin: 0; }
.documents-board-header .btn { width: auto; flex-shrink: 0; }
.document-board-canvas-wrap { flex: 1; position: relative; overflow: hidden; }
.document-board-canvas-wrap canvas { display: block; }
.document-reader-image-wrap.dragging { cursor: grabbing; }

/* ---------- Cor de mão (estilo TTS) ---------- */
.color-modal { width: min(380px, 92vw); text-align: center; }
.color-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  margin: 16px 0 20px;
}
.color-swatch {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  border-radius: 10px;
  border: 2px solid transparent;
  cursor: pointer;
  padding: 0;
}
.color-swatch:hover:not(:disabled) { transform: scale(1.08); }
.color-swatch.selected { border-color: #fff; box-shadow: 0 0 0 2px var(--accent); }
/* pedido explícito: a cor NÃO fica mais escura quando já escolhida (confundia jogador,
   parecia só um tom mais escuro disponível) — o quadrado continua no brilho normal, quem
   já pegou aparece como uma foto/inicial por cima (.color-swatch-owner-avatar-wrap) */
.color-swatch:disabled { cursor: not-allowed; }
.color-swatch-owner-avatar-wrap {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 68%;
  aspect-ratio: 1;
  border-radius: 50%;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.35); /* fundo escuro atrás da foto/inicial — garante contraste em cima de qualquer cor da paleta, inclusive as claras (amarelo, branco) */
  border: 2px solid rgba(255, 255, 255, 0.55);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}
.color-swatch-owner-avatar { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.color-swatch-owner-placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}

/* ---------- Névoa de guerra ---------- */
/* grid 2x2 em vez de 4 numa linha só (bug corrigido: 4 botões com `flex:1` numa fileira
   só, cada um com uns 39px de largura disponível dentro dos ~220px do painel, estouravam
   o container — "Pincel" cortado, "Laço" nem aparecia; texto de botão não encolhe abaixo
   do próprio min-content em flexbox por padrão) — 2 colunas dão o dobro do espaço por
   botão, cabendo confortavelmente mesmo o rótulo mais longo ("Retângulo") */
.fog-shape-toggle { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 8px; }
.fog-shape-toggle .btn { margin-bottom: 0; }
.tool-group-body .btn.active { background: var(--accent); border-color: var(--accent); color: #fff; }

/* ---------- Canto inferior direito: dados / documentos / ajuda ---------- */
/* pedido explícito: "as opções da navbar de dados e documentos... vão ficar no canto
   inferior direito junto com a ajuda, 3 circulos um acima do outro" — .shortcuts-toggle
   (só o "?", sozinho) virou .corner-toggle (compartilhado pelos 3), dentro de um
   container que empilha em coluna. z-index 20 é MENOR que o do painel de dados (30) e
   do quadro de documentos (45) de propósito — quando qualquer um dos dois abre, ele
   cobre esse canto naturalmente (mesmo comportamento que "?" já tinha contra o popup
   de atalhos), sem precisar mover os círculos pra outro lugar */
.corner-toggles {
  position: fixed;
  right: 14px;
  bottom: 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 20;
}
.corner-toggle {
  width: 32px;
  height: 32px;
  border-radius: 99px;
  background: var(--panel);
  border: 1px solid var(--line);
  color: var(--text-dim);
  font-family: var(--font);
  font-size: 15px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.corner-toggle:hover { border-color: var(--accent); color: var(--accent); }
.corner-toggle svg { width: 17px; height: 17px; }

/* popup LARGO com os atalhos fluindo em colunas lado a lado (pedido explícito: "em vez
   de um scroll pode aumentar o tamanho horizontal... opções em mais colunas") — cada
   .shortcuts-list usa CSS multi-column com `columns: <largura-mínima>` (não um número
   fixo de colunas): o navegador encaixa quantas colunas de ~330px couberem na largura
   atual do popup (3 num desktop comum, 2/1 conforme a janela aperta), responsivo de
   graça. O max-height + scroll do miolo continuam SÓ como rede de segurança pra janela
   muito baixa — no tamanho normal de tela nada rola */
.shortcuts-modal {
  width: min(1240px, 96vw);
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 48px);
}
.shortcuts-scroll { overflow-y: auto; min-height: 0; padding-right: 6px; }
/* barra de rolagem temática (a padrão branca do navegador destoava do popup escuro) */
.shortcuts-scroll, .shortcuts-modal { scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
.shortcuts-scroll::-webkit-scrollbar, .shortcuts-modal::-webkit-scrollbar { width: 7px; }
.shortcuts-scroll::-webkit-scrollbar-track, .shortcuts-modal::-webkit-scrollbar-track { background: transparent; }
.shortcuts-scroll::-webkit-scrollbar-thumb, .shortcuts-modal::-webkit-scrollbar-thumb { background: var(--line); border-radius: 99px; }
.shortcuts-scroll::-webkit-scrollbar-thumb:hover, .shortcuts-modal::-webkit-scrollbar-thumb:hover { background: var(--accent); }
.shortcuts-section-title {
  margin: 0;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--accent);
}
#shortcuts-gm-section { margin-top: 18px; }
#shortcuts-gm-section[hidden] { display: none; }
.shortcuts-list { list-style: none; margin: 0; padding: 0; columns: 300px; column-gap: 32px; }
.shortcuts-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 8px 0;
  border-top: 1px solid var(--line);
  font-size: 13px;
  color: var(--text-dim);
  /* um item nunca quebra no meio entre uma coluna e a próxima */
  break-inside: avoid;
}
/* sem a exceção de :first-child de antes — com multi-column, cada COLUNA começa com um
   item "do meio" da lista, então tirar a borda só do primeiro deixaria as colunas
   assimétricas; borda em todos = topo uniforme em toda coluna */
.shortcuts-list .keys { color: var(--text); flex-shrink: 0; }
.shortcuts-list kbd {
  display: inline-block;
  padding: 2px 7px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 5px;
  font-family: var(--font);
  font-size: 12px;
}

@media (max-width: 700px) {
  .gm-tools { width: 180px; }
}

/* ---------- Painel de dados (rolagem, tecla R) ---------- */
.dice-panel {
  position: fixed;
  right: 12px;
  bottom: 12px;
  /* alargado (era 280px) — pedido explícito: "aumente o espaço pra todos os dados
     caberem em uma linha" (7 botões, d4 a d100, numa fileira só) */
  width: 420px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 12px;
  z-index: 30; /* mesmo nível do painel flutuante da Biblioteca */
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
}
/* display:flex de autor vence o [hidden] padrão do navegador — mesma armadilha
   documentada n vezes nesse arquivo */
.dice-panel[hidden] { display: none; }
.dice-panel-header { display: flex; align-items: center; justify-content: space-between; }
.dice-panel-title { display: flex; align-items: center; gap: 7px; }
.dice-panel-header h3 { margin: 0; font-size: 14px; }
/* régua tracejada no lugar de uma borda reta simples — pedido explícito de fugir do
   básico ("como podemos deixar isso melhor e mais bonito"), pequeno toque "desenhado
   à mão" combinando com o resto da identidade visual escura/crua do app */
.dice-panel-divider { border-top: 1px dashed var(--line); margin: 2px 0 -2px; }
/* dado vira ÍCONE (contorno poligonal por face) + rótulo embaixo, não mais um pill de
   texto plano — cartão próprio (fundo panel-2, acende a cor de acento no hover) */
.dice-quick { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.dice-quick-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 5px 2px 4px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
}
.dice-quick-btn:hover { border-color: var(--accent); }
.dice-quick-btn svg { display: block; width: 20px; height: 20px; }
.dice-quick-btn span { font-size: 10px; color: var(--text-dim); }
/* align-items:center — sem isso, o campo (~20px), o botão de limpar (30px fixo) e
   "Rolar" (~32px) ficam cada um alinhado ao TOPO da linha por padrão (align-items:stretch
   não estica quem já tem altura fixa, mas também não centraliza), dando a impressão de
   desalinhado (pedido explícito, com print) */
.dice-expr-row { display: flex; align-items: center; gap: 6px; }
/* margin-top:0 explícito (duas classes, vence por especificidade) — sem isso, a regra
   genérica `input[type="text"]` (elemento+atributo, MAIS específica que a classe
   .floor-name-input sozinha) grudava um margin-top:6px que nem o `margin:0` de
   .floor-name-input conseguia sobrescrever, empurrando o campo 6px pra baixo e
   quebrando o alinhamento vertical com os botões ao lado (mesma armadilha de
   especificidade já documentada várias vezes nesse projeto) */
.dice-expr-row .floor-name-input { flex: 1; min-width: 0; margin-top: 0; }
/* mesma armadilha do .btn{width:100%} dentro de flex de sempre */
.dice-expr-row .btn { width: auto; flex-shrink: 0; }
/* "rolagem oculta" (GM-only): label.inline define display:flex, que venceria o [hidden] */
#dice-hidden-row { font-size: 12px; color: var(--text-dim); }
#dice-hidden-row[hidden] { display: none; }
.dice-history { display: flex; flex-direction: column; gap: 6px; max-height: 260px; overflow-y: auto; overflow-x: hidden; }
/* histórico vazio some de vez — senão a div de altura 0 ainda contribui um gap extra
   do flex entre o cabeçalho e os botões */
.dice-history:empty { display: none; }
/* barra de rolagem temática (pedido explícito, com print: "essa barra branca não
   combina") — o navegador nunca tinha uma barra estilizada nesse app inteiro, esse é o
   primeiro lugar; fina, trilho invisível (funde com o fundo do painel), acende
   --accent no hover, mesma linguagem do resto da UI escura */
.dice-history { scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
.dice-history::-webkit-scrollbar { width: 7px; }
.dice-history::-webkit-scrollbar-track { background: transparent; }
.dice-history::-webkit-scrollbar-thumb { background: var(--line); border-radius: 99px; }
.dice-history::-webkit-scrollbar-thumb:hover { background: var(--accent); }
/* cada rolagem virou um "cartão" (fundo próprio + faixa colorida à esquerda na cor de
   mão de quem rolou) em vez de só texto separado por linha — pedido explícito: "achei
   muito simples... como podemos deixar isso melhor e mais bonito" */
.dice-entry {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  padding: 6px 8px;
  border-radius: 6px;
  background: var(--panel-2);
  border-left: 3px solid var(--line); /* sobrescrita inline com a cor de mão, quando válida */
}
/* mesmo padrão de avatar já usado no painel de jogadores (.player-card-avatar-wrap) —
   placeholder com a inicial embaixo, <img> por cima que some sozinha via onerror */
.dice-entry-avatar-wrap {
  position: relative; width: 22px; height: 22px; border-radius: 50%; flex-shrink: 0;
  overflow: hidden; background: rgba(0, 0, 0, 0.2); margin-top: 1px;
}
.dice-entry-avatar { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.dice-entry-avatar-placeholder {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 10px; font-weight: 700; color: var(--text-dim);
}
.dice-entry-body { flex: 1; min-width: 0; }
.dice-entry-line { font-size: 12px; color: var(--text-dim); overflow-wrap: anywhere; }
.dice-entry-name { font-weight: 600; color: var(--text); }
.dice-entry-total { font-weight: 700; font-size: 14px; color: var(--text); }
.dice-entry-detail { font-size: 11px; color: var(--text-dim); margin-top: 2px; overflow-wrap: anywhere; }
.dice-entry-hidden { font-style: italic; }
/* "natural 20"/"natural 1" num termo de d20 — clássico da mesa de RPG, barato de fazer
   e de bom retorno visual; #ffd60a é o MESMO dourado já usado na coroa do mestre
   (CROWN_SVG em app.js), reaproveitado aqui por consistência em vez de inventar um
   tom novo. Falha usa um vermelho dessaturado (derivado do --accent da marca, não uma
   cor importada de fora) */
.dice-entry-total.dice-crit { color: #ffd60a; }
.dice-entry-total.dice-fail { color: #a17272; }
.dice-crit-tag { font-size: 10px; color: #ffd60a; font-weight: 600; margin-left: 4px; }
.dice-fail-tag { font-size: 10px; color: #a17272; margin-left: 4px; }
/* total/tag ficam invisíveis enquanto os dados ainda estão girando, revelados com um
   pequeno "pop" assim que o último dado da rolagem para (ver revealDiceTotal em app.js) */
.dice-total-pending { opacity: 0; }
.dice-total-reveal { animation: dg-total-reveal 0.25s ease; }
@keyframes dg-total-reveal { from { opacity: 0; transform: scale(1.3); } to { opacity: 1; transform: scale(1); } }
/* ---------- Roleta de dados (pedido explícito, estilo Alchemy: "uma animação vai
   aparecer com os numeros girando como se fosse uma roleta até cair no número
   resultado") — cada dado individual da rolagem vira um círculo próprio que gira
   (número trocando rápido) e para no valor de verdade, em cascata (1º pra 2º pra 3º...
   como um caça-níquel) em vez de todos pararem juntos ---------- */
.dice-die-row { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; margin-top: 5px; }
.dice-die-badge-wrap { position: relative; display: inline-flex; }
.dice-die-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--panel);
  border: 1.5px solid var(--accent);
  font-size: 12px;
  font-weight: 700;
  color: var(--text);
}
/* enquanto gira: contorno neutro (a cor de acento só "acende" quando o dado já parou,
   reforçando visualmente qual valor já é definitivo e qual ainda está sorteando) */
.dice-die-badge.spinning { border-color: var(--line); color: var(--text-dim); }
.dice-die-badge.settled { animation: dg-die-settle 0.22s ease; }
@keyframes dg-die-settle { from { transform: scale(1.3); } to { transform: scale(1); } }
/* rótulo "dN" no canto — só aparece quando a rolagem mistura faces diferentes
   (ex.: "1d4+1d6"), pra não confundir qual círculo é de qual dado */
.dice-die-badge-face {
  position: absolute;
  right: -2px;
  bottom: -2px;
  font-size: 8px;
  color: var(--text-dim);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0 2px;
  line-height: 1.3;
}
.dice-entry-modifier { font-size: 12px; font-weight: 600; color: var(--text-dim); margin-left: 2px; }
/* badge de rolagens não vistas no chip da navbar (mesma pílula do botão de amigos) */
#btn-open-dice { position: relative; }

/* barra de rolagem temática da biblioteca (mesma linguagem do histórico de dados e do painel do mestre) */
.library-list, .library-batch-col .merge-list { scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
.library-list::-webkit-scrollbar, .library-batch-col .merge-list::-webkit-scrollbar { width: 7px; }
.library-list::-webkit-scrollbar-track, .library-batch-col .merge-list::-webkit-scrollbar-track { background: transparent; }
.library-list::-webkit-scrollbar-thumb, .library-batch-col .merge-list::-webkit-scrollbar-thumb { background: var(--line); border-radius: 99px; }
.library-list::-webkit-scrollbar-thumb:hover, .library-batch-col .merge-list::-webkit-scrollbar-thumb:hover { background: var(--accent); }

/* ---------- Modo stream (página /stream/<id> usada como fonte de navegador do OBS) ----------
   Só o canvas da mesa aparece: tudo o mais some, nada recebe clique e o cursor some. A
   câmera vem do mestre (ver app.js, 'Modo stream'). Os overlays de aviso (conexão perdida,
   carregando...) também ficam escondidos pra nunca aparecerem na live. */
body.stream-mode { background: #000; cursor: none; }
body.stream-mode > *:not(#canvas-wrap):not(#stream-error):not(#stream-standby):not(script) { display: none !important; }
body.stream-mode #canvas-wrap { pointer-events: none; }
.stream-standby {
  position: fixed;
  inset: 0;
  z-index: 290;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #0d0d10;
}
.stream-standby[hidden] { display: none; }
.stream-standby img { width: min(260px, 40vmin); opacity: 0.9; }
.server-outdated {
  position: fixed;
  top: 48px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 55;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: min(560px, 92vw);
  padding: 8px 12px;
  border: 1px solid #4a331f;
  border-radius: 8px;
  background: var(--panel);
  color: var(--danger-dim);
  font-size: 13px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5);
}
.server-outdated[hidden] { display: none; }
.server-outdated button {
  border: none;
  background: none;
  color: inherit;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
body.stream-mode .server-outdated { display: none !important; }
.stream-error {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  text-align: center;
  font-size: 20px;
  color: var(--text);
  background: rgba(0, 0, 0, 0.85);
}
.stream-error[hidden] { display: none; }
.stream-warn {
  margin: 8px 0;
  padding: 6px 8px;
  border: 1px solid #4a331f;
  border-radius: 6px;
  font-size: 12px;
  color: var(--danger-dim);
}
.stream-warn[hidden] { display: none; }

/* ---------- Mover a grade (analógico + setas, aba Grid) ---------- */
.grid-nudge-title { margin: 10px 0 6px; font-size: 12px; color: var(--text-dim); }
.grid-nudge-pad {
  display: grid;
  grid-template-columns: 22px 48px 22px;
  grid-template-rows: 22px 48px 22px;
  gap: 3px;
  justify-content: center;
  align-items: center;
  margin: 0 auto 6px;
}
.grid-arrow {
  position: relative;
  justify-self: center;
  align-self: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel-2);
  cursor: pointer;
}
.grid-arrow:hover { border-color: var(--accent); }
.grid-arrow:active { background: var(--accent); }
.grid-arrow::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  border: 5px solid transparent;
}
.grid-arrow-up { grid-column: 2; grid-row: 1; }
.grid-arrow-left { grid-column: 1; grid-row: 2; }
.grid-arrow-right { grid-column: 3; grid-row: 2; }
.grid-arrow-down { grid-column: 2; grid-row: 3; }
.grid-arrow-up::before { border-bottom-color: var(--text); border-top-width: 0; transform: translate(-50%, -60%); }
.grid-arrow-down::before { border-top-color: var(--text); border-bottom-width: 0; transform: translate(-50%, -40%); }
.grid-arrow-left::before { border-right-color: var(--text); border-left-width: 0; transform: translate(-60%, -50%); }
.grid-arrow-right::before { border-left-color: var(--text); border-right-width: 0; transform: translate(-40%, -50%); }
.grid-stick {
  grid-column: 2;
  grid-row: 2;
  position: relative;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: radial-gradient(circle at 50% 50%, var(--panel) 0%, var(--panel-2) 100%);
  touch-action: none;
  cursor: grab;
}
.grid-stick-knob {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 20px;
  height: 20px;
  margin: -10px 0 0 -10px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
  pointer-events: none;
}
.grid-nudge-footer { display: flex; align-items: center; justify-content: space-between; gap: 6px; font-size: 12px; color: var(--text-dim); }
.grid-nudge-footer .btn { width: auto; flex-shrink: 0; }
.grid-offset-field { display: flex; align-items: center; gap: 4px; margin: 0; font-size: 12px; color: var(--text-dim); }
.grid-offset-field input[type="number"] { width: 54px; margin: 0; padding: 4px 6px; font-size: 12px; }

/* ---------- seções recolhíveis, favoritos e estrela (painel do mestre largo) ---------- */
.gm-tools:not(.gm-tools-narrow) .gm-section-title[data-collapsible] {
  display: flex; align-items: center; gap: 6px; cursor: pointer; user-select: none;
}
.gm-tools:not(.gm-tools-narrow) .gm-section-title[data-collapsible]:hover { opacity: 0.9; }
.gm-section-title[data-collapsible]::before {
  content: ''; width: 5px; height: 5px; flex-shrink: 0;
  border-right: 1.4px solid currentColor; border-bottom: 1.4px solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px);
  transition: transform 0.15s;
}
.gm-section-title[data-collapsible][aria-expanded="false"]::before { transform: rotate(-45deg); }
/* seção recolhida com algo ligado lá dentro: bolinha no título */
.gm-section-title[data-dot]::after {
  content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); margin-left: 2px;
}
.gm-section-title[data-dot="ok"]::after { background: #4ade80; }
.gm-section-title[data-dot="warn"]::after { background: #f59e0b; }
.gm-tools-narrow .gm-section-title[data-collapsible]::before,
.gm-tools-narrow .gm-section-title[data-dot]::after { display: none; }
.gm-tools:not(.gm-tools-narrow) .gm-sec-hidden { display: none !important; }

.gm-favorites { display: flex; flex-wrap: wrap; gap: 4px; padding: 4px 0 6px; }
.gm-favorites[hidden], .gm-fav-title[hidden] { display: none; }
.gm-tools-narrow .gm-favorites, .gm-tools-narrow .gm-fav-title { display: none; }
.gm-fav-btn {
  position: relative; width: 30px; height: 30px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 8px;
  color: var(--text-dim); cursor: pointer;
}
.gm-fav-btn svg { width: 16px; height: 16px; }
.gm-fav-btn:hover { color: var(--accent); border-color: var(--accent); }
.gm-fav-btn.active { color: var(--accent); border-color: var(--accent); }
.gm-fav-btn[data-dot]::after {
  content: ''; position: absolute; top: 3px; right: 3px; width: 6px; height: 6px;
  border-radius: 50%; background: var(--accent);
}
.gm-fav-btn[data-dot="ok"]::after { background: #4ade80; }
.gm-fav-btn[data-dot="warn"]::after { background: #f59e0b; }

/* estrelinha de favorito: aparece no hover do item (e fica à mostra se já é favorito) */
.tg-star {
  /* item do flex do cabeçalho (entre o rótulo e o chevron/"Voltar"), não absoluto: assim nunca sobrepõe o botão Voltar do foco */
  flex-shrink: 0; margin-right: 4px;
  width: 18px; height: 18px; display: flex; align-items: center; justify-content: center;
  opacity: 0; color: var(--text-dim); cursor: pointer; border-radius: 4px;
}
.tg-star svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linejoin: round; }
.tool-group summary:hover .tg-star,
.tool-group-flat:hover .tg-star,
.tg-star.on { opacity: 1; }
.tg-star:hover { color: var(--accent); }
.tg-star.on { color: #ffd60a; }
.tg-star.on svg { fill: currentColor; }
.gm-tools-narrow .tg-star { display: none; }

/* ---------- painel de jogadores: cabeçalho e visão só de avatares ---------- */
.players-panel { transition: transform 0.25s ease, width 0.25s ease; }
.players-panel-head { display: flex; align-items: center; justify-content: space-between; margin: 0 0 6px; padding: 0 2px 0 4px; }
.players-panel-title { font-size: 10px; font-weight: 600; letter-spacing: 1px; text-transform: uppercase; color: var(--text-dim); }
.players-panel.compact { width: 56px; }
.players-panel.compact .players-panel-title { display: none; }
.players-panel.compact .players-panel-head { justify-content: center; padding: 0; }
.players-panel.compact .players-panel-list { align-items: center; padding: 6px 0 4px; overflow-x: visible; }
.players-panel.compact .player-card {
  position: relative; width: 40px; height: 40px; padding: 0; border-radius: 50%; justify-content: center; flex-shrink: 0;
}
.players-panel.compact .player-card-ident,
.players-panel.compact .player-card-right { display: contents; }
.players-panel.compact .player-card-name,
.players-panel.compact .player-card-blindfold,
.players-panel.compact .player-card-away-icon,
.players-panel.compact .player-card-status span { display: none; }
.players-panel.compact .player-card-crown {
  position: absolute; top: -7px; right: -5px; background: var(--panel); border-radius: 50%; padding: 1px; z-index: 1;
}
.players-panel.compact .player-card-crown svg { width: 13px; height: 13px; }
.players-panel.compact .player-card-status {
  position: absolute; bottom: -4px; right: -4px; background: var(--panel); border-radius: 50%; padding: 1px; z-index: 1;
}
.players-panel.compact .player-card-status svg { width: 12px; height: 12px; }
.players-panel.compact .player-card-loading {
  position: absolute; bottom: -7px; left: 50%; transform: translateX(-50%); font-size: 9px; z-index: 1;
}
.players-panel.compact .players-panel-footer { align-items: center; }

/* resumo do estado dentro de cada aba do painel do mestre */
.tab-state {
  font-size: 11px; line-height: 1.45; color: var(--text-dim);
  padding: 2px 0 8px; margin-bottom: 8px; border-bottom: 1px dashed var(--line);
}

/* aba em foco: o cabeçalho vira uma barra com o botão "Voltar" no lugar do chevron */
.gm-tools.gm-tools-focus-inner .tool-group[open] > summary,
.gm-tools.gm-tools-focus > .tool-group-focusable[open] > summary { color: var(--text); font-weight: 600; }
.gm-tools.gm-tools-focus-inner .tool-group[open] > summary::after,
.gm-tools.gm-tools-focus > .tool-group-focusable[open] > summary::after {
  content: '‹ Voltar';
  width: auto; height: auto; margin: 0;
  padding: 2px 9px;
  border: 1px solid var(--line); border-radius: 99px;
  font-size: 10px; font-weight: 400; letter-spacing: 0; text-transform: none;
  color: var(--text-dim);
  transform: none; opacity: 1;
}
.gm-tools.gm-tools-focus-inner .tool-group[open] > summary:hover::after,
.gm-tools.gm-tools-focus > .tool-group-focusable[open] > summary:hover::after { border-color: var(--accent); color: var(--accent); }

/* favoritos: arrastar pra reordenar */
.gm-fav-btn.dragging { opacity: 0.4; }
.gm-fav-btn.drag-over { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }

/* ---------- variações do mapa: lista na aba Mapa e seletor na navbar ---------- */
.map-variants { margin-top: 10px; padding-top: 8px; border-top: 1px dashed var(--line); }
.map-variants-list { display: flex; flex-direction: column; gap: 4px; margin: 6px 0 8px; }
.map-variants-empty { font-size: 11px; line-height: 1.45; color: var(--text-dim); margin: 4px 0 8px; }
.map-variant-row {
  display: flex; align-items: center; gap: 6px; padding: 3px 6px;
  border: 1px solid var(--line); border-radius: 8px; cursor: pointer;
}
.map-variant-row:hover { border-color: var(--text-dim); }
.map-variant-row.active { border-color: var(--accent); }
.map-variant-dot { width: 8px; height: 8px; border-radius: 50%; border: 1px solid var(--text-dim); flex-shrink: 0; }
.map-variant-row.active .map-variant-dot { background: var(--accent); border-color: var(--accent); }
.map-variant-row .floor-name-input { flex: 1; min-width: 0; margin-top: 0; }
.map-variant-remove {
  flex-shrink: 0; background: none; border: none; padding: 2px 4px; border-radius: 4px;
  color: var(--text-dim); font-size: 13px; line-height: 1; cursor: pointer;
}
.map-variant-remove:hover { color: var(--danger); }
.map-variant-remove.confirm { color: var(--danger); font-size: 11px; }
.variant-switcher { position: relative; }
#variant-switcher-btn { display: inline-flex; align-items: center; gap: 7px; max-width: 220px; }

/* item da biblioteca que o mestre clicou pra substituir (modo salvar) */
.library-tile.library-tile-replace-target { border-color: var(--accent); background: var(--panel-2); }

/* ---------- Desenho livre no mapa ---------- */
.draw-float-body { position: fixed; left: 76px; top: 56px; z-index: 40; width: 240px; box-sizing: border-box; padding: 12px 14px 8px; border-radius: 12px; background: var(--panel); border: 1px solid var(--line); display: flex; flex-direction: column; gap: 6px; box-shadow: 0 8px 28px rgba(0, 0, 0, 0.5); }
/* título igual ao do flyout do painel do mestre */
.draw-float-body::before { content: "Lápis"; display: block; margin-bottom: 4px; font-size: 11px; font-weight: 600; letter-spacing: 1px; text-transform: uppercase; color: var(--accent); }
.draw-float-body[hidden], #draw-controls-gm [hidden], #draw-controls-player [hidden] { display: none !important; }
.draw-float-body .btn { width: 100%; }
.draw-float-body .btn.active { background: var(--accent); border-color: var(--accent); color: #fff; }
.draw-float-body .fog-shape-toggle { margin-bottom: 0; }

/* ---------- Caixa de permissões ---------- */
.permissions-modal { width: min(460px, 94vw); max-height: 88vh; overflow-y: auto; }
.permissions-modal h2 { text-align: center; }
.permissions-modal .btn { margin-top: 10px; }
.perm-entry { display: block; margin-bottom: 16px; }
.perm-entry select { margin-top: 6px; }
.perm-title { font-size: 12px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-dim); margin: 4px 0 8px; }
.perm-table { display: flex; flex-direction: column; border: 1px solid var(--border, #2a2a33); border-radius: 10px; overflow: hidden; }
.perm-row { display: grid; grid-template-columns: 1fr repeat(var(--perm-cols, 1), 84px); align-items: center; gap: 8px; padding: 9px 12px; border-top: 1px solid var(--border, #2a2a33); }
.perm-row:first-child { border-top: 0; }
.perm-head { font-size: 11px; color: var(--text-dim); background: rgba(255, 255, 255, 0.03); }
.perm-all { background: rgba(255, 255, 255, 0.04); font-weight: 600; }
.perm-who { display: flex; align-items: center; gap: 8px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.perm-who i { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.perm-cell { display: flex; justify-content: center; }
.perm-cell input { width: 18px; height: 18px; margin: 0; cursor: pointer; accent-color: var(--accent); }

/* ---------- Barra lateral do jogador ---------- */
/* usa as classes do painel do mestre (.gm-tools, .gm-tools-narrow, .tool-group-flat, .tg-ico...), então
   o visual vem de lá; aqui só o que é diferente */
.player-bar[hidden], .player-bar .tool-group-flat[hidden] { display: none !important; }
.player-bar .tool-group-flat::after { content: none; }
.player-bar .gm-tools-head + .tool-group-flat { border-top: none; }
/* lápis sem permissão: escurecido, sem hover e sem clique (mesma ideia do desfazer/refazer apagado) */
.player-bar .tool-group-flat.is-disabled { opacity: 0.3; cursor: not-allowed; }
.player-bar .tool-group-flat.is-disabled:hover { color: var(--text-dim); }
.player-bar .gm-tools-compact-btn.flip svg { transform: rotate(180deg); }
/* o painel do lápis abre ao lado da barra (compacta: 56px; expandida: 220px) */
body.player-bar-expanded .draw-float-body { left: 240px; }
.draw-shapes { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.draw-shapes .btn { display: flex; align-items: center; justify-content: center; margin-bottom: 0; }
/* seletor de cor do desenho */
.draw-picker { display: flex; flex-direction: column; gap: 8px; margin: 2px 0 6px; }
.draw-sv { position: relative; height: 96px; border-radius: 8px; cursor: crosshair; touch-action: none; border: 1px solid var(--line); }
.draw-sv-handle { position: absolute; width: 14px; height: 14px; margin: -7px 0 0 -7px; border-radius: 50%; border: 2px solid #fff; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6); pointer-events: none; }
.draw-hue { -webkit-appearance: none; appearance: none; width: 100%; height: 10px; margin: 2px 0; border-radius: 6px; background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00); outline: none; }
.draw-hue::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; border-radius: 50%; background: #fff; border: 2px solid rgba(0, 0, 0, 0.55); cursor: pointer; }
.draw-hue::-moz-range-thumb { width: 12px; height: 12px; border-radius: 50%; background: #fff; border: 2px solid rgba(0, 0, 0, 0.55); cursor: pointer; }
.draw-hex-row { display: flex; align-items: center; gap: 8px; }
.draw-hex { flex: 1; min-width: 0; margin: 0; padding: 6px 8px; font-family: ui-monospace, Consolas, monospace; font-size: 13px; text-align: center; letter-spacing: 0.04em; }
.draw-hex.invalid { border-color: var(--danger, #f97316); }
.draw-preview { width: 26px; height: 26px; border-radius: 50%; flex-shrink: 0; border: 2px solid rgba(255, 255, 255, 0.35); box-sizing: border-box; }
.draw-mine-btn { display: flex; align-items: center; justify-content: center; gap: 6px; margin-bottom: 0; }
.draw-mine-btn i { width: 12px; height: 12px; border-radius: 50%; border: 1px solid rgba(255, 255, 255, 0.4); }

/* ---------- Biblioteca: visões, ordenação, tamanho, favoritos, seleção e redimensionar ---------- */
.library-modal { position: relative; }
.library-controls { display: flex; align-items: center; gap: 8px; margin: 0 0 8px; flex-wrap: wrap; }
.library-chips { display: flex; gap: 4px; }
.library-chips button { padding: 4px 9px; font-size: 12px; font-family: var(--font); background: transparent; color: var(--text-dim); border: 1px solid var(--line); border-radius: 999px; cursor: pointer; }
.library-chips button:hover { color: var(--text); border-color: var(--text-dim); }
.library-chips button.active { background: var(--accent); border-color: var(--accent); color: #fff; }
.library-sizes { margin-left: auto; }
.library-sizes button { padding: 4px 8px; min-width: 26px; }
.library-sort { width: auto; margin: 0; padding: 4px 6px; font-size: 12px; }
.library-sort:disabled { opacity: 0.45; }
#library-list[data-size="s"] { grid-template-columns: repeat(auto-fill, minmax(70px, 1fr)); }
#library-list[data-size="l"] { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
#library-list[data-size="s"] .library-tile-name { font-size: 11px; }
.library-tile-star { position: absolute; top: 4px; left: 4px; width: 22px; height: 22px; padding: 0; border: 0; border-radius: 6px; background: var(--panel); color: var(--text-dim); font-size: 14px; line-height: 1; cursor: pointer; opacity: 0; transition: opacity 0.15s; z-index: 1; }
.library-tile:hover .library-tile-star, .library-tile-star.on { opacity: 1; }
.library-tile-star.on { color: #ffd60a; }
.library-tile-star:hover { color: #ffd60a; }
.library-tile-selected { border-color: var(--accent); background: rgba(200, 30, 30, 0.16); }
.library-tile-selected:hover { background: rgba(200, 30, 30, 0.22); }
.library-selection-bar { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 0 0 8px; padding: 6px 8px; border: 1px solid var(--accent); border-radius: 8px; background: rgba(200, 30, 30, 0.1); }
.library-selection-bar[hidden] { display: none; }
.library-selection-count { font-size: 12px; font-weight: 600; margin-right: auto; }
.library-selection-bar .btn { width: auto; margin: 0; flex-shrink: 0; }
.library-selection-bar select { width: auto; margin: 0; padding: 4px 6px; font-size: 12px; max-width: 150px; }
.library-selection-clear { width: 24px; height: 24px; padding: 0; background: none; border: 1px solid transparent; border-radius: 6px; color: var(--text-dim); font-size: 16px; line-height: 1; cursor: pointer; }
.library-selection-clear:hover { color: var(--text); border-color: var(--line); }
/* painel com tamanho escolhido: a grade ocupa o que sobra, em vez do teto de 55vh */
.library-modal.library-sized { display: flex; flex-direction: column; max-width: calc(100vw - 16px); }
.library-modal.library-sized .library-body { flex: 1; min-height: 0; align-items: stretch; }
.library-modal.library-sized .library-browse-col { display: flex; flex-direction: column; min-height: 0; }
.library-modal.library-sized .library-list { flex: 1; min-height: 0; max-height: none; }
/* com altura própria a grade é mais alta que as linhas: sem isso o grid ESTICA as linhas pra preencher e cada item vira uma coluna enorme */
.library-list { align-content: start; grid-auto-rows: max-content; }

/* ordem manual: marca de onde o item cai e o aviso de excluir pasta com conteúdo */
.library-tile.drop-before { box-shadow: inset 4px 0 0 var(--accent); background: var(--panel-2); }
.library-tile.drop-after { box-shadow: inset -4px 0 0 var(--accent); background: var(--panel-2); }
.library-folder-delete { margin: 0 0 8px; padding: 10px 12px; border: 1px solid #f97316; border-radius: 8px; background: rgba(249, 115, 22, 0.08); }
.library-folder-delete[hidden] { display: none; }
.library-folder-delete p { margin: 0 0 8px; }
.lfd-target-row { display: block; font-size: 12px; margin-bottom: 8px; }
.lfd-target-row select { margin-top: 4px; }
.library-folder-delete .merge-actions { flex-wrap: wrap; }
.library-folder-delete .merge-actions .btn { flex: 1 1 auto; }
/* selos nos itens, menu de contexto do item e soltar imagens */
.library-thumb-wrap { position: relative; width: 100%; }
.library-tile-badges { position: absolute; left: 4px; right: 4px; bottom: 4px; display: flex; flex-wrap: wrap; gap: 3px; pointer-events: auto; }
.library-tile-badges span { padding: 1px 5px; font-size: 9px; font-weight: 600; line-height: 14px; letter-spacing: 0.02em; color: #fff; background: rgba(0, 0, 0, 0.72); border-radius: 4px; }
#library-list[data-size="s"] .library-tile-badges { display: none; }
.library-item-menu { width: 210px; }
.library-item-menu .btn { width: 100%; white-space: normal; }
.lim-many { display: flex; align-items: flex-end; gap: 6px; margin-bottom: 8px; }
.lim-many label { flex: 1; margin: 0; }
.lim-many input { width: 100%; margin: 4px 0 0; }
.lim-many .btn { width: auto; flex-shrink: 0; margin: 0; }
.library-drop-hint { position: absolute; inset: 8px; z-index: 5; display: flex; align-items: center; justify-content: center; text-align: center; padding: 20px; border: 2px dashed var(--accent); border-radius: 12px; background: rgba(13, 13, 16, 0.88); color: var(--text); font-size: 14px; font-weight: 600; pointer-events: none; }
.library-drop-hint[hidden] { display: none; }
.library-toolbar #btn-library-add-images { margin-left: auto; }
/* o X de excluir fica acima da miniatura com selos (o wrapper relativo passou por cima dele) */
.library-tile-delete { z-index: 2; }
/* ---------- Biblioteca: acabamento visual ---------- */
/* barra de cima: botões só de ícone */
.library-toolbar { gap: 6px; }
.library-icon-btn { width: 30px; height: 30px; flex-shrink: 0; padding: 0; display: flex; align-items: center; justify-content: center; background: none; border: 1px solid var(--line); border-radius: 8px; color: var(--text-dim); cursor: pointer; }
.library-icon-btn svg { width: 16px; height: 16px; }
.library-icon-btn:hover { color: var(--text); border-color: var(--text-dim); background: var(--panel-2); }
.library-icon-btn[hidden] { display: none; }
.library-toolbar #btn-library-add-images { margin-left: 0; }
/* nome do item como texto (o campo só aparece ao renomear pelo lápis) */
.library-tile-namebox { position: relative; width: 100%; }
.library-tile-label { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; padding: 2px 4px; font-size: 12px; line-height: 1.3; text-align: center; word-break: break-word; color: var(--text); }
.library-tile-label[hidden], .library-tile-name[hidden], .library-tile-rename[hidden] { display: none; }
.library-tile-name:focus { border-color: var(--accent); background: var(--bg); }
.library-tile-rename { position: absolute; right: 0; top: 0; width: 20px; height: 20px; padding: 0; display: flex; align-items: center; justify-content: center; border: 0; border-radius: 5px; background: var(--panel-2); color: var(--text-dim); cursor: pointer; opacity: 0; transition: opacity 0.15s; }
.library-tile-rename svg { width: 12px; height: 12px; }
.library-tile:hover .library-tile-rename { opacity: 1; }
.library-tile-rename:hover { color: var(--text); }
#library-list[data-size="s"] .library-tile-label { font-size: 11px; }
/* alças de redimensionar nos 4 cantos e 4 bordas (a do canto de baixo à direita mostra o desenho) */
.library-rz { position: absolute; z-index: 6; touch-action: none; }
.library-rz-n, .library-rz-s { left: 14px; right: 14px; height: 8px; }
.library-rz-e, .library-rz-w { top: 14px; bottom: 14px; width: 8px; }
.library-rz-n { top: -4px; cursor: ns-resize; }
.library-rz-s { bottom: -4px; cursor: ns-resize; }
.library-rz-e { right: -4px; cursor: ew-resize; }
.library-rz-w { left: -4px; cursor: ew-resize; }
.library-rz-ne, .library-rz-nw, .library-rz-se, .library-rz-sw { width: 18px; height: 18px; }
.library-rz-ne { top: -4px; right: -4px; cursor: nesw-resize; }
.library-rz-sw { bottom: -4px; left: -4px; cursor: nesw-resize; }
.library-rz-nw { top: -4px; left: -4px; cursor: nwse-resize; }
.library-rz-se { bottom: -4px; right: -4px; cursor: nwse-resize; }
.library-rz-se::after { content: ''; position: absolute; right: 6px; bottom: 6px; width: 14px; height: 14px; opacity: 0.85; background: linear-gradient(135deg, transparent 0 46%, var(--text-dim) 46% 54%, transparent 54% 64%, var(--text-dim) 64% 72%, transparent 72% 82%, var(--text-dim) 82% 90%, transparent 90%); pointer-events: none; }
.library-rz-se:hover::after { opacity: 1; }
/* alça de arrastar no título + rolagem nas cores do app */
.panel-drag { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; cursor: move; user-select: none; }
.panel-drag h2 { margin: 0; flex: 0 1 auto; }
.panel-grip { display: flex; flex-shrink: 0; color: var(--text-dim); opacity: 0.55; }
.panel-grip svg { width: 14px; height: 14px; }
.panel-drag:hover .panel-grip { opacity: 1; color: var(--text); }
.library-list, .library-batch-col .merge-list { scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
.library-list::-webkit-scrollbar, .library-batch-col .merge-list::-webkit-scrollbar { width: 7px; }
.library-list::-webkit-scrollbar-track, .library-batch-col .merge-list::-webkit-scrollbar-track { background: transparent; }
.library-list::-webkit-scrollbar-thumb, .library-batch-col .merge-list::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; }
.library-list::-webkit-scrollbar-thumb:hover, .library-batch-col .merge-list::-webkit-scrollbar-thumb:hover { background: var(--accent); }
/* paredes em 3D: os campos aparecem só com a opção ligada (label.inline/label definem display) */
#wall-3d-height-row[hidden], #wall-3d-color-row[hidden] { display: none; }/* objetos 3D (aba do mestre) */
/* catálogo de objetos 3D (flutuante, mestre) */
/* biblioteca de objetos 3D: enviar pacotes e créditos */

.props-credit { font-size: 11px; color: #9a9aa6; word-break: break-word; }
.props-credit a { color: #e13a3a; }
.props-credit a:hover { text-decoration: underline; }
.modal.model-upload-modal { width: min(460px, 92vw); }
.model-upload-modal label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; margin-bottom: 10px; }
.model-upload-modal label.inline { flex-direction: row; align-items: center; gap: 8px; }
.model-upload-modal label[hidden], #model-upload-files-wrap[hidden], #model-upload-confirm-row[hidden] { display: none; }
.model-upload-files { list-style: none; margin: 8px 0 12px; padding: 0; max-height: 130px; overflow-y: auto; font-size: 12px; }
.model-upload-files li { display: flex; align-items: center; gap: 8px; padding: 3px 0; }
.model-upload-files li span:first-child { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.model-upload-files li .state { color: #9a9aa6; flex-shrink: 0; }
.model-upload-files li.erro .state { color: #f97316; }
.model-upload-files li.ok .state { color: #4caf50; }
.model-upload-files button { background: none; border: none; color: #9a9aa6; cursor: pointer; font-size: 16px; line-height: 1; padding: 0 4px; }
.model-upload-actions { display: flex; gap: 8px; }
.model-upload-actions .btn { width: auto; }
#model-upload-status { min-height: 1.4em; }
/* erro de envio: a mensagem é longa, quebra em linhas em vez de ganhar barra de rolagem */
.model-upload-files { overflow-x: hidden; }
.model-upload-files li.erro { flex-wrap: wrap; }
.model-upload-files li.erro .state { flex-basis: 100%; white-space: normal; }

/* ===== objetos 3D: aba enxuta, inspetor flutuante e catálogo (redesenho) ===== */
/* aba (flyout do painel do mestre) */
.pa-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.pa-switch { display: flex; align-items: center; justify-content: space-between; gap: 10px; cursor: pointer; font-size: 14px; margin: 0; padding: 6px 0; }
.pa-switch input { position: absolute; opacity: 0; pointer-events: none; }
.pa-switch i { width: 36px; height: 20px; border-radius: 10px; background: #3a3a45; position: relative; flex-shrink: 0; transition: background 0.15s; }
.pa-switch i::after { content: ''; position: absolute; left: 2px; top: 2px; width: 16px; height: 16px; border-radius: 50%; background: #ececf1; transition: transform 0.15s; }
.pa-switch input:checked + i { background: #c81e1e; }
.pa-switch input:checked + i::after { transform: translateX(16px); }
.pa-switch input:focus-visible + i { outline: 2px solid #e13a3a; outline-offset: 2px; }
.pa-head .pa-switch { flex: 1; }
.pa-gear { background: none; border: none; color: #9a9aa6; cursor: pointer; padding: 6px; border-radius: 8px; display: flex; flex-shrink: 0; }
.pa-gear:hover, .pa-gear.on { color: #ececf1; background: #23232b; }
.pa-note { font-size: 12px; line-height: 1.4; color: #9a9aa6; margin: 2px 0 8px; }
.pa-note[hidden] { display: none; }
.pa-options { background: #1a1a20; border: 1px solid #2a2a33; border-radius: 10px; padding: 8px 10px; margin: 4px 0 10px; }
.pa-options[hidden] { display: none; }
.pa-keys { list-style: none; margin: 6px 0 0; padding: 8px 0 0; border-top: 1px solid #2a2a33; font-size: 12px; color: #9a9aa6; display: flex; flex-direction: column; gap: 4px; line-height: 1.35; }
.pa-keys b { color: #ececf1; font-weight: 600; }
.pa-main { width: 100%; display: flex; align-items: center; justify-content: center; gap: 8px; background: #c81e1e; color: #fff; border: none; border-radius: 10px; padding: 11px 12px; font: inherit; font-size: 14px; font-weight: 600; cursor: pointer; margin: 4px 0 8px; }
.pa-main:hover { background: #e13a3a; }
.pa-list-toggle { width: 100%; display: flex; align-items: center; justify-content: space-between; background: none; border: none; border-top: 1px solid #2a2a33; color: #9a9aa6; font: inherit; font-size: 13px; padding: 10px 2px 6px; cursor: pointer; }
.pa-list-toggle:hover { color: #ececf1; }
.pa-list-toggle svg { transition: transform 0.15s; }
.pa-list-toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
.props-list { display: flex; flex-direction: column; gap: 4px; padding-bottom: 4px; }
.props-list[hidden] { display: none; }
.props-list .props-row { display: flex; align-items: center; gap: 8px; text-align: left; background: #1e1e25; color: inherit; border: 1px solid #2a2a33; border-radius: 8px; padding: 5px 8px; font: inherit; font-size: 13px; cursor: pointer; }
.props-list .props-row:hover { border-color: #e13a3a; }
.props-list .props-row.on { background: #2a1a1d; border-color: #c81e1e; }
.props-list .props-row img, .props-list .props-row .props-row-img { width: 30px; height: 30px; border-radius: 6px; object-fit: cover; flex-shrink: 0; background: #26262d; }
.props-list .props-row span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.props-empty { font-size: 12px; color: #9a9aa6; padding: 4px 2px; }
.props-inline-field { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 13px; margin: 0; padding: 6px 0; }

/* campo numérico com unidade (inspetor e opções) */
.pi-num { display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0; }
.pi-num input { width: 62px; background: #0f0f13; border: 1px solid #2a2a33; border-radius: 8px; color: #ececf1; padding: 6px 8px; font: inherit; font-size: 14px; text-align: right; }
.pi-num input:focus { outline: none; border-color: #c81e1e; }
.pi-num em { font-style: normal; font-size: 12px; color: #9a9aa6; min-width: 22px; }

/* inspetor flutuante, canto direito da tela */
.props-inspector { position: fixed; right: 16px; bottom: 92px; width: 300px; max-height: calc(100vh - 300px); overflow-y: auto; background: #16161b; border: 1px solid #2a2a33; border-radius: 12px; box-shadow: 0 10px 40px #000a; padding: 12px; z-index: 55; color: #ececf1; font-size: 14px; }
.props-inspector[hidden], .props-inspector [hidden] { display: none; }
.pi-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.props-inspector .pi-head { cursor: grab; user-select: none; }
.props-inspector .pi-head:active { cursor: grabbing; }
.props-inspector .pi-head button { cursor: pointer; }
.pi-thumb { width: 44px; height: 44px; border-radius: 8px; overflow: hidden; background: #26262d; flex-shrink: 0; display: block; }
.pi-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pi-name { flex: 1; min-width: 0; font-size: 15px; font-weight: 600; line-height: 1.25; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; word-break: break-word; }
.pi-icon { display: flex; align-items: center; justify-content: center; width: 36px; height: 36px; background: #1e1e25; border: 1px solid #2a2a33; border-radius: 8px; color: #ececf1; cursor: pointer; flex-shrink: 0; padding: 0; }
.pi-icon:hover { border-color: #e13a3a; }
.pi-danger { color: #f97316; }
.pi-danger:hover { border-color: #f97316; background: #2a1d12; }
.pi-colors { margin: 0 0 8px; }
.pi-colors:empty { display: none; }
.pi-colors .props-swatch { width: 30px; height: 30px; }
.pi-row { display: flex; align-items: center; gap: 10px; padding: 6px 0; }
.pi-lab { width: 58px; font-size: 13px; color: #9a9aa6; flex-shrink: 0; }
.pi-fill { flex: 1; }
.pi-hint { margin: 0; font-size: 12px; line-height: 1.4; color: #9a9aa6; }
.pi-range { -webkit-appearance: none; appearance: none; flex: 1; min-width: 0; height: 20px; background: transparent; margin: 0; padding: 0; border: none; cursor: pointer; }
.pi-range::-webkit-slider-runnable-track { height: 4px; background: #2f2f3a; border-radius: 2px; }
.pi-range::-moz-range-track { height: 4px; background: #2f2f3a; border-radius: 2px; }
.pi-range::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; border-radius: 50%; background: #ececf1; border: 2px solid #c81e1e; margin-top: -6px; }
.pi-range::-moz-range-thumb { width: 12px; height: 12px; border-radius: 50%; background: #ececf1; border: 2px solid #c81e1e; }
.pi-range:focus-visible { outline: 2px solid #e13a3a; outline-offset: 3px; border-radius: 4px; }

/* cores (quadradinhos) */
.props-swatches { display: flex; flex-wrap: wrap; gap: 6px; max-height: 120px; overflow-y: auto; }
.props-swatch { width: 38px; height: 38px; padding: 0; border-radius: 8px; border: 2px solid #2a2a33; background: #0f0f13 center/cover no-repeat; cursor: pointer; color: #9a9aa6; font-size: 11px; flex-shrink: 0; }
.props-swatch:hover { border-color: #e13a3a; }
.props-swatch.on { border-color: #c81e1e; box-shadow: 0 0 0 1px #c81e1e; }

/* catálogo flutuante */
.props-catalog { position: fixed; left: 300px; top: 70px; width: 500px; max-width: calc(100vw - 24px); max-height: calc(100vh - 100px); display: flex; flex-direction: column; background: #16161b; border: 1px solid #2a2a33; border-radius: 12px; box-shadow: 0 10px 40px #000a; z-index: 60; overflow: hidden; color: #ececf1; }
.props-catalog[hidden] { display: none; }
.props-catalog.props-drop { outline: 2px dashed #e13a3a; outline-offset: -4px; }
.props-catalog-head { display: flex; align-items: center; gap: 10px; padding: 12px 12px 10px 16px; cursor: grab; user-select: none; }
.props-catalog-title { font-size: 15px; font-weight: 600; flex-shrink: 0; }
.props-catalog-add { flex-shrink: 0; background: #1e1e25; color: inherit; border: 1px solid #2a2a33; border-radius: 8px; padding: 6px 11px; font: inherit; font-size: 13px; cursor: pointer; }
.props-catalog-add:hover { border-color: #e13a3a; }
.props-catalog-head input[type=search] { flex: 1; min-width: 0; background: #0f0f13; border: 1px solid #2a2a33; border-radius: 8px; color: inherit; padding: 7px 10px; font: inherit; font-size: 13px; cursor: text; }
.props-catalog-x { background: none; border: none; color: #9a9aa6; font-size: 26px; line-height: 1; cursor: pointer; padding: 0 4px; }
.props-catalog-x:hover { color: #ececf1; }
.pc-filters { display: flex; gap: 6px; padding: 0 16px 10px; border-bottom: 1px solid #23232b; }
.pc-chip { background: none; border: 1px solid #2a2a33; border-radius: 999px; color: #9a9aa6; padding: 4px 13px; font: inherit; font-size: 13px; cursor: pointer; }
.pc-chip:hover { color: #ececf1; border-color: #3a3a45; }
.pc-chip.on { background: #2a1a1d; border-color: #c81e1e; color: #ececf1; }
.props-catalog-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); gap: 10px; padding: 14px 16px; overflow-y: auto; align-content: start; min-height: 140px; }
.pc-card { display: flex; flex-direction: column; gap: 6px; background: none; border: 2px solid transparent; border-radius: 12px; padding: 4px; cursor: pointer; text-align: center; color: inherit; font: inherit; }
.pc-card:hover { background: #1e1e25; }
.pc-card.on { border-color: #c81e1e; background: #231a1d; }
.pc-imgwrap { aspect-ratio: 1; border-radius: 9px; overflow: hidden; background: #26262d; display: block; }
.pc-imgwrap img, .pc-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pc-img-wait { opacity: 0.3; }
.pc-name { font-size: 13px; line-height: 1.25; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding: 0 2px; }
.props-catalog-empty { grid-column: 1 / -1; font-size: 13px; color: #9a9aa6; padding: 24px 8px; text-align: center; line-height: 1.5; }
.props-catalog-detail { display: grid; grid-template-columns: 132px minmax(0, 1fr); gap: 16px; padding: 16px; border-top: 1px solid #23232b; background: #1a1a20; }
.props-catalog-detail[hidden] { display: none; }
.pc-detail-img { width: 132px; height: 132px; border-radius: 10px; object-fit: cover; background: #26262d; display: block; }
.pc-detail-body { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.pc-detail-name { font-size: 17px; font-weight: 600; line-height: 1.25; word-break: break-word; }
.pc-detail-body .props-swatch { width: 40px; height: 40px; }
.pc-detail-actions { display: flex; align-items: center; gap: 8px; position: relative; flex-wrap: wrap; }
.pc-place { display: inline-flex; align-items: center; gap: 8px; background: #c81e1e; color: #fff; border: none; border-radius: 10px; padding: 10px 16px; font: inherit; font-size: 14px; font-weight: 600; cursor: pointer; }
.pc-place:hover { background: #e13a3a; }
.pc-more { display: flex; align-items: center; justify-content: center; width: 38px; height: 38px; background: #1e1e25; border: 1px solid #2a2a33; border-radius: 10px; color: #ececf1; cursor: pointer; padding: 0; }
.pc-more:hover, .pc-more.on { border-color: #e13a3a; }
.pc-menu { position: absolute; left: 0; bottom: calc(100% + 6px); min-width: 190px; background: #1e1e25; border: 1px solid #2a2a33; border-radius: 10px; box-shadow: 0 8px 28px #000a; padding: 4px; z-index: 5; }
.pc-menu button { display: block; width: 100%; text-align: left; background: none; border: none; border-radius: 7px; color: #ececf1; padding: 9px 11px; font: inherit; font-size: 13px; cursor: pointer; }
.pc-menu button:hover { background: #2a2a33; }
.pc-menu button.pc-danger { color: #f97316; }
.pc-menu button.confirm { background: #f97316; color: #111; }
.pc-about-toggle { background: none; border: none; color: #9a9aa6; font: inherit; font-size: 13px; cursor: pointer; padding: 0; text-align: left; display: inline-flex; align-items: center; gap: 4px; }
.pc-about-toggle:hover { color: #ececf1; }
.pc-about-toggle::before { content: ''; width: 6px; height: 6px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(-45deg); margin-right: 4px; transition: transform 0.12s; }
.pc-about-toggle.open::before { transform: rotate(45deg); }
.pc-about { font-size: 12px; line-height: 1.5; color: #9a9aa6; word-break: break-word; max-height: 110px; overflow-y: auto; }
.pc-about p { margin: 0 0 6px; }
.props-credit a { color: #e13a3a; }
.props-credit a:hover { text-decoration: underline; }

/* ---- tags da pessoa no catálogo de objetos ---- */
.pc-tags { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 10px 16px 8px; max-height: 116px; overflow-y: auto; border-bottom: 1px solid #23232b; scrollbar-width: thin; scrollbar-color: #2a2a33 transparent; }
.pc-tags[hidden], .pc-bulk[hidden], .pc-tagmgr[hidden], .props-catalog-grid[hidden] { display: none; }
.pc-tag { display: inline-flex; align-items: center; gap: 6px; background: #1e1e25; border: 1px solid #2a2a33; border-radius: 8px; color: #c9c9d3; padding: 3px 9px; font: inherit; font-size: 12px; cursor: pointer; }
.pc-tag:hover { border-color: #3a3a45; color: #ececf1; }
.pc-tag.on { background: #2a1a1d; border-color: #c81e1e; color: #ececf1; }
.pc-tag em { font-style: normal; font-size: 11px; color: #7d7d89; }
.pc-tag.on em { color: #e8a1a1; }
.pc-tag-link { background: none; border: none; color: #9a9aa6; font: inherit; font-size: 12px; padding: 3px 4px; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.pc-tag-link:hover { color: #ececf1; }
.pc-bulk { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 10px 16px; background: #231a1d; border-bottom: 1px solid #3a2226; }
.pc-bulk-count { font-size: 13px; font-weight: 600; color: #ececf1; margin-right: 2px; }
.pc-bulk .pc-taginput { flex: 1 1 140px; min-width: 120px; }
.pc-bulk-btn { background: #2a2a33; border: 1px solid #3a3a45; border-radius: 8px; color: #ececf1; padding: 5px 11px; font: inherit; font-size: 12px; cursor: pointer; }
.pc-bulk-btn:hover { border-color: #e13a3a; }
.pc-taginput { background: #0d0d10; border: 1px solid #2a2a33; border-radius: 8px; color: #ececf1; padding: 5px 9px; font: inherit; font-size: 12px; min-width: 0; }
.pc-taginput:focus { outline: none; border-color: #c81e1e; }
.pc-card.multi { border-color: #e13a3a; background: #2a1a1d; position: relative; }
.pc-card.multi::after { content: ''; position: absolute; top: 8px; left: 8px; width: 18px; height: 18px; border-radius: 50%; background: #c81e1e url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='5 12 10 17 19 7'/%3E%3C/svg%3E") center / 12px no-repeat; box-shadow: 0 1px 4px #000a; }
.pc-tagsbox { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.pc-tagchip { display: inline-flex; align-items: center; gap: 4px; background: #26262d; border: 1px solid #34343e; border-radius: 8px; padding: 2px 4px 2px 9px; font-size: 12px; color: #ececf1; }
.pc-tagchip button { background: none; border: none; color: #9a9aa6; font-size: 15px; line-height: 1; padding: 0 4px; cursor: pointer; border-radius: 4px; }
.pc-tagchip button:hover { color: #f97316; }
.pc-tagsbox .pc-taginput { width: 150px; }
.pc-tagmgr { display: flex; flex-direction: column; gap: 8px; padding: 14px 16px; overflow-y: auto; min-height: 140px; }
.pc-tagrow { display: flex; align-items: center; gap: 10px; }
.pc-tagrow input { flex: 1 1 auto; min-width: 0; background: #0d0d10; border: 1px solid #2a2a33; border-radius: 8px; color: #ececf1; padding: 7px 10px; font: inherit; font-size: 14px; }
.pc-tagrow input:focus { outline: none; border-color: #c81e1e; }
.pc-tagcount { flex-shrink: 0; width: 80px; font-size: 12px; color: #9a9aa6; }
.pc-tagdel { flex-shrink: 0; background: none; border: 1px solid #2a2a33; border-radius: 8px; color: #9a9aa6; padding: 6px 11px; font: inherit; font-size: 12px; cursor: pointer; }
.pc-tagdel:hover { color: #f97316; border-color: #f97316; }
.pc-tagdel.confirm { background: #f97316; border-color: #f97316; color: #16161b; font-weight: 600; }

/* ---- visão livre 3D (só o mestre) ---- */
.pa-sec { width: 100%; display: flex; align-items: center; justify-content: center; gap: 8px; background: #1e1e25; color: #ececf1; border: 1px solid #2a2a33; border-radius: 10px; padding: 9px 12px; font: inherit; font-size: 14px; cursor: pointer; margin: 0 0 8px; }
.pa-sec:hover { border-color: #e13a3a; }
.pa-style { margin: 0 0 8px; border: 1px solid #2a2a33; border-radius: 10px; padding: 6px 10px 8px; font-size: 13px; color: #ececf1; }
.pa-style > summary { cursor: pointer; padding: 2px 0; }
.pa-style[open] > summary { margin-bottom: 6px; }
.pa-style label.inline { margin-top: 6px; }
.pa-style .tex-scale-row > span { display: flex; align-items: center; gap: 4px; }
.pa-style .tex-scale-row > span { white-space: nowrap; }
.pa-style select { background: #16161b; color: #ececf1; border: 1px solid #2a2a33; border-radius: 8px; padding: 6px 8px; font: inherit; font-size: 13px; width: 100%; cursor: pointer; }
.pa-style select:hover { border-color: #e13a3a; }
.pa-style input.style-num { width: 50px; height: 26px; flex: none; box-sizing: border-box; background: #16161b; color: #ececf1; border: 1px solid #2a2a33; border-radius: 6px; padding: 2px 6px; margin: 0; font: inherit; font-size: 13px; font-weight: 600; text-align: right; -moz-appearance: textfield; }
.pa-style input.style-num::-webkit-outer-spin-button, .pa-style input.style-num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.pa-style input.style-num:hover, .pa-style input.style-num:focus { outline: none; border-color: #e13a3a; }
.mx-modal { width: min(980px, 96vw); max-height: 94vh; display: flex; flex-direction: column; padding: 18px 20px; }
.mx-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.mx-head h2 { margin: 0; }
.mx-x { background: none; border: 0; color: #a4a4b0; font-size: 26px; line-height: 1; cursor: pointer; padding: 0 6px; }
.mx-x:hover { color: #ececf1; }
.mx-body { display: flex; gap: 16px; min-height: 0; flex: 1; }
.mx-preview-col { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.mx-tabs { display: flex; gap: 6px; }
.mx-tabs[hidden] { display: none; }
.mx-tabs button { background: #1e1e25; color: #ececf1; border: 1px solid #2a2a33; border-radius: 8px; padding: 5px 12px; font: inherit; font-size: 13px; cursor: pointer; }
.mx-tabs button.on { border-color: #e13a3a; background: #2a1c1f; }
.mx-stage { position: relative; flex: 1; min-height: 320px; height: min(62vh, 640px); border: 1px solid #2a2a33; border-radius: 10px; overflow: hidden; display: grid; place-items: center; background: repeating-conic-gradient(#2a2a33 0% 25%, #202027 0% 50%) 50% / 22px 22px; }
.mx-stage img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }
.mx-busy { position: absolute; inset: 0; display: grid; place-items: center; background: rgba(13, 13, 16, 0.55); color: #ececf1; font-size: 14px; }
.mx-busy[hidden] { display: none; }
.mx-info { font-size: 12px; color: #a4a4b0; min-height: 16px; }
.mx-controls { width: 250px; flex: none; display: flex; flex-direction: column; gap: 12px; overflow-y: auto; font-size: 13px; color: #a4a4b0; }
.mx-controls > label { display: flex; flex-direction: column; gap: 5px; }
.mx-controls select { background: #1e1e25; color: #ececf1; border: 1px solid #2a2a33; border-radius: 8px; padding: 7px 8px; font: inherit; font-size: 13px; width: 100%; cursor: pointer; }
.mx-controls select:hover { border-color: #e13a3a; }
.mx-layers { border: 1px solid #2a2a33; border-radius: 10px; padding: 8px 10px 10px; margin: 0; display: flex; flex-direction: column; gap: 6px; }
.mx-layers legend { padding: 0 6px; font-size: 12px; }
.mx-layers label.inline { color: #ececf1; }
.mx-foot { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }
.mx-foot .btn { width: auto; min-width: 110px; }
@media (max-width: 760px) { .mx-body { flex-direction: column; } .mx-controls { width: auto; } }
.free3d { position: fixed; inset: 0; z-index: 15; background: #14141a; }
.free3d[hidden] { display: none; }
.free3d-host { position: absolute; inset: 0; }
.free3d-host canvas { display: block; width: 100%; height: 100%; touch-action: none; cursor: grab; }
.free3d-host canvas:active { cursor: grabbing; }
.free3d-bar { position: absolute; top: 56px; left: 50%; transform: translateX(-50%); display: flex; align-items: center; gap: 10px; background: #16161bee; border: 1px solid #2a2a33; border-radius: 12px; padding: 8px 12px; font-size: 13px; color: #ececf1; box-shadow: 0 8px 28px #0008; white-space: nowrap; }
.free3d-hint { color: #9a9aa6; font-size: 12px; }
.free3d-bar button { background: #26262d; border: 1px solid #34343e; border-radius: 8px; color: #ececf1; padding: 5px 11px; font: inherit; font-size: 12px; cursor: pointer; }
.free3d-bar button:hover { border-color: #e13a3a; }
.free3d-bar .free3d-close { background: #c81e1e; border-color: #c81e1e; font-weight: 600; }
.free3d-bar .free3d-close:hover { background: #e13a3a; }
.free3d-note { position: absolute; bottom: 14px; left: 50%; transform: translateX(-50%); font-size: 12px; color: #9a9aa6; background: #16161bcc; border-radius: 8px; padding: 5px 12px; pointer-events: none; }
@media (max-width: 900px) { .free3d-hint { display: none; } }
.free3d-bar #free3d-walls:not(.on) { color: #9a9aa6; text-decoration: line-through; }
.free3d-bar #free3d-walls.on { background: #2a1a1d; border-color: #c81e1e; }

.pc-wallrow { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #c9c9d3; cursor: pointer; width: fit-content; }
.pc-wallrow input { accent-color: #c81e1e; }

/* ---- texturas de parede ---- */
.wt-section { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; padding-top: 10px; border-top: 1px solid var(--line); }
.wt-section[hidden] { display: none; }
.wt-head { display: flex; align-items: center; justify-content: space-between; font-size: 12px; color: #9a9aa6; }
.wt-add, .wt-link { background: none; border: none; color: #9a9aa6; font: inherit; font-size: 12px; padding: 2px 0; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; width: fit-content; }
.wt-add:hover, .wt-link:hover { color: #ececf1; }
.wt-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.wt-item { position: relative; }
.wt-grid > .pa-note { grid-column: 1 / -1; margin: 0; }
.wt-chip { width: 100%; aspect-ratio: 1; padding: 0; background: #1e1e25; border: 2px solid #2a2a33; border-radius: 8px; overflow: hidden; cursor: pointer; display: flex; align-items: center; justify-content: center; color: #9a9aa6; font: inherit; font-size: 10px; }
.wt-chip:hover { border-color: #3a3a45; }
.wt-chip.on { border-color: #c81e1e; }
.wt-chip img { width: 100%; height: 100%; object-fit: cover; display: block; }
.wt-del { position: absolute; top: 2px; right: 2px; width: 16px; height: 16px; border-radius: 50%; border: none; background: #000a; color: #fff; font-size: 12px; line-height: 14px; padding: 0; cursor: pointer; opacity: 0; }
.wt-item:hover .wt-del, .wt-del:focus-visible { opacity: 1; }
.wt-del:hover { background: #f97316; }

/* pisos por pacote: quadradinhos mais soltos, contagem de cores no canto e a faixa de cores do pacote aberto */
#floor-tex-grid, #wall-tex-grid { gap: 10px; }
.wt-chip.open { border-color: #5a5a68; }
.wt-count { position: absolute; right: 3px; bottom: 3px; min-width: 16px; padding: 0 4px; border-radius: 8px; background: #000b; color: #ececf1; font-size: 10px; line-height: 16px; text-align: center; pointer-events: none; }
.wt-chip { position: relative; }
.wt-variants { background: #16161b; border: 1px solid #2a2a33; border-radius: 10px; padding: 8px; display: flex; flex-direction: column; gap: 8px; }
.wt-variants[hidden] { display: none; }
.wt-vlabel { font-size: 11px; color: #9a9aa6; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wt-variants .wt-grid { gap: 8px; }

.pi-row[hidden] { display: none; }
.pi-toggle { background: #1e1e25; border: 1px solid #2a2a33; border-radius: 8px; color: #c9c9d3; padding: 4px 12px; font: inherit; font-size: 12px; cursor: pointer; }
.pi-toggle:hover { border-color: #3a3a45; color: #ececf1; }
.pi-toggle.on { background: #2a1a1d; border-color: #c81e1e; color: #ececf1; }

/* ---- aba Mapa 3D: sub-abas (objetos, paredes, pisos) ---- */
.m3-tabs { display: flex; gap: 4px; padding: 3px; background: #121217; border: 1px solid #2a2a33; border-radius: 10px; margin-top: 6px; }
.m3-tab { flex: 1; background: none; border: none; border-radius: 7px; color: #9a9aa6; padding: 6px 4px; font: inherit; font-size: 12px; cursor: pointer; }
.m3-tab:hover { color: #ececf1; }
.m3-tab.on { background: #26262d; color: #ececf1; box-shadow: inset 0 -2px 0 #c81e1e; }
.m3-panel { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.m3-panel[hidden] { display: none; }

.ft-tools { display: flex; gap: 6px; }
.ft-tools button { flex: 1; background: #1e1e25; border: 1px solid #2a2a33; border-radius: 8px; color: #c9c9d3; padding: 6px 4px; font: inherit; font-size: 12px; cursor: pointer; }
.ft-tools button:hover { border-color: #3a3a45; color: #ececf1; }
.ft-tools button.on { background: #2a1a1d; border-color: #c81e1e; color: #ececf1; }

.tex-scale-row { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: #c9c9d3; margin-top: 6px; }
.tex-scale-row[hidden] { display: none; }
.tex-scale-row b { color: #ececf1; font-weight: 600; margin-left: 4px; }

/* ---- catálogo de objetos: maior, redimensionável, sem cortar as tags e com a barra de rolagem do tema ---- */
.props-catalog { width: 660px; height: min(800px, calc(100vh - 100px)); max-height: calc(100vh - 24px); min-width: 420px; min-height: 380px; resize: both; }
/* numa coluna flex tudo encolhe pra caber: o que não pode ser espremido (cabeçalho, filtros, tags, barra de seleção, detalhe) não encolhe, e só a grade rola */
.props-catalog-head, .pc-filters, .pc-tags, .pc-bulk, .props-catalog-detail { flex-shrink: 0; }
.props-catalog-grid { flex: 1 1 0; min-height: 120px; }
.pc-tagmgr { flex: 1 1 0; min-height: 120px; }
.pc-tags { max-height: 120px; }
.props-catalog-grid, .pc-tags, .pc-tagmgr, .props-inspector, .props-list, .props-catalog-detail { scrollbar-width: thin; scrollbar-color: #34343e transparent; }
.props-catalog-grid::-webkit-scrollbar, .pc-tags::-webkit-scrollbar, .pc-tagmgr::-webkit-scrollbar, .props-inspector::-webkit-scrollbar, .props-list::-webkit-scrollbar { width: 7px; }
.props-catalog-grid::-webkit-scrollbar-track, .pc-tags::-webkit-scrollbar-track, .pc-tagmgr::-webkit-scrollbar-track, .props-inspector::-webkit-scrollbar-track, .props-list::-webkit-scrollbar-track { background: transparent; }
.props-catalog-grid::-webkit-scrollbar-thumb, .pc-tags::-webkit-scrollbar-thumb, .pc-tagmgr::-webkit-scrollbar-thumb, .props-inspector::-webkit-scrollbar-thumb, .props-list::-webkit-scrollbar-thumb { background: #34343e; border-radius: 4px; }
.props-catalog-grid::-webkit-scrollbar-thumb:hover, .pc-tags::-webkit-scrollbar-thumb:hover, .pc-tagmgr::-webkit-scrollbar-thumb:hover { background: #c81e1e; }
/* o detalhe do objeto (cores, tags, ações) não pode tomar a grade inteira: passa de ~42% do catálogo e rola */
.props-catalog-detail { max-height: 42%; overflow-y: auto; }
/* tags e cores do detalhe rolam cada uma no seu canto (muita tag ou 19 cores não empurram o "Colocar no mapa" pra fora) */
.props-catalog-detail .pc-tagsbox { max-height: 64px; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #34343e transparent; }
.props-catalog-detail .props-swatches { max-height: 66px; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #34343e transparent; }
.props-catalog-detail { max-height: 48%; }
